feat: polish phase 7 forms and schedules

This commit is contained in:
Artem Kokos
2026-05-01 09:47:08 +07:00
parent 91a494adf5
commit 2fa89f6be0
9 changed files with 1583 additions and 599 deletions

View File

@@ -0,0 +1,101 @@
import '../../models/ignis_group.dart';
final RegExp _groupIdAllowedPattern = RegExp(r'^[a-z0-9][a-z0-9_-]*$');
final RegExp _groupIdSanitizePattern = RegExp(r'[^a-z0-9_-]+');
final RegExp _dashCollapsePattern = RegExp(r'[-_]{2,}');
const Map<String, String> _transliterationMap = {
'а': 'a',
'б': 'b',
'в': 'v',
'г': 'g',
'д': 'd',
'е': 'e',
'ё': 'e',
'ж': 'zh',
'з': 'z',
'и': 'i',
'й': 'y',
'к': 'k',
'л': 'l',
'м': 'm',
'н': 'n',
'о': 'o',
'п': 'p',
'р': 'r',
'с': 's',
'т': 't',
'у': 'u',
'ф': 'f',
'х': 'h',
'ц': 'ts',
'ч': 'ch',
'ш': 'sh',
'щ': 'sch',
'ъ': '',
'ы': 'y',
'ь': '',
'э': 'e',
'ю': 'yu',
'я': 'ya',
};
String slugifyGroupId(String input) {
final lower = input.trim().toLowerCase();
final buffer = StringBuffer();
for (final rune in lower.runes) {
final char = String.fromCharCode(rune);
buffer.write(_transliterationMap[char] ?? char);
}
var value = buffer
.toString()
.replaceAll(RegExp(r'\s+'), '-')
.replaceAll(_groupIdSanitizePattern, '-')
.replaceAll(_dashCollapsePattern, '-')
.replaceAll(RegExp(r'^[-_]+|[-_]+$'), '');
if (value.length > 32) {
value = value.substring(0, 32).replaceAll(RegExp(r'[-_]+$'), '');
}
return value;
}
String? validateGroupId(String value, Iterable<IgnisGroup> existingGroups) {
final normalized = value.trim().toLowerCase();
if (normalized.isEmpty) {
return 'Укажите ID группы';
}
if (!_groupIdAllowedPattern.hasMatch(normalized)) {
return 'Только латиница, цифры, "-" и "_"';
}
final alreadyExists = existingGroups.any((group) => group.id == normalized);
if (alreadyExists) {
return 'Группа с таким ID уже существует';
}
return null;
}
String? validateGroupName(String value) {
if (value.trim().isEmpty) {
return 'Укажите название группы';
}
return null;
}
List<IgnisGroup> findGroupMembershipConflicts(
Set<String> selectedDeviceIds,
Iterable<IgnisGroup> existingGroups,
) {
if (selectedDeviceIds.isEmpty) {
return const [];
}
return existingGroups
.where(
(group) =>
group.macs.any((deviceId) => selectedDeviceIds.contains(deviceId)),
)
.toList();
}