148 lines
3.8 KiB
Dart
148 lines
3.8 KiB
Dart
class ScheduleWeekdayOption {
|
||
final int backendValue;
|
||
final String shortLabel;
|
||
final String fullLabel;
|
||
|
||
const ScheduleWeekdayOption({
|
||
required this.backendValue,
|
||
required this.shortLabel,
|
||
required this.fullLabel,
|
||
});
|
||
}
|
||
|
||
const scheduleWeekdayOptions = <ScheduleWeekdayOption>[
|
||
ScheduleWeekdayOption(
|
||
backendValue: 1,
|
||
shortLabel: 'Пн',
|
||
fullLabel: 'понедельник',
|
||
),
|
||
ScheduleWeekdayOption(
|
||
backendValue: 2,
|
||
shortLabel: 'Вт',
|
||
fullLabel: 'вторник',
|
||
),
|
||
ScheduleWeekdayOption(backendValue: 3, shortLabel: 'Ср', fullLabel: 'среда'),
|
||
ScheduleWeekdayOption(
|
||
backendValue: 4,
|
||
shortLabel: 'Чт',
|
||
fullLabel: 'четверг',
|
||
),
|
||
ScheduleWeekdayOption(
|
||
backendValue: 5,
|
||
shortLabel: 'Пт',
|
||
fullLabel: 'пятница',
|
||
),
|
||
ScheduleWeekdayOption(
|
||
backendValue: 6,
|
||
shortLabel: 'Сб',
|
||
fullLabel: 'суббота',
|
||
),
|
||
ScheduleWeekdayOption(
|
||
backendValue: 0,
|
||
shortLabel: 'Вс',
|
||
fullLabel: 'воскресенье',
|
||
),
|
||
];
|
||
|
||
DateTime defaultOnceScheduleTime({DateTime? now}) {
|
||
final base = now ?? DateTime.now();
|
||
final candidate = base.add(const Duration(hours: 4));
|
||
final roundedMinute = ((candidate.minute + 4) ~/ 5) * 5;
|
||
final normalized = DateTime(
|
||
candidate.year,
|
||
candidate.month,
|
||
candidate.day,
|
||
candidate.hour,
|
||
roundedMinute,
|
||
);
|
||
return normalized.isAfter(base)
|
||
? normalized
|
||
: normalized.add(const Duration(minutes: 5));
|
||
}
|
||
|
||
String? validateOnceSchedule(DateTime? scheduledAt, {DateTime? now}) {
|
||
if (scheduledAt == null) {
|
||
return 'Выберите дату и время';
|
||
}
|
||
final threshold = (now ?? DateTime.now()).add(const Duration(minutes: 1));
|
||
if (!scheduledAt.isAfter(threshold)) {
|
||
return 'Нужна дата хотя бы на минуту вперёд';
|
||
}
|
||
return null;
|
||
}
|
||
|
||
String? validateCronSchedule({
|
||
required int? hour,
|
||
required int? minute,
|
||
required Set<int> weekdays,
|
||
}) {
|
||
if (hour == null || hour < 0 || hour > 23) {
|
||
return 'Час должен быть от 0 до 23';
|
||
}
|
||
if (minute == null || minute < 0 || minute > 59) {
|
||
return 'Минута должна быть от 0 до 59';
|
||
}
|
||
if (weekdays.isEmpty) {
|
||
return 'Выберите хотя бы один день недели';
|
||
}
|
||
return null;
|
||
}
|
||
|
||
String serializeCronWeekdays(Set<int> weekdays) {
|
||
if (weekdays.length == scheduleWeekdayOptions.length) {
|
||
return '*';
|
||
}
|
||
final sorted = weekdays.toList()..sort();
|
||
return sorted.join(',');
|
||
}
|
||
|
||
String describeCronWeekdays(Set<int> weekdays) {
|
||
if (weekdays.isEmpty) {
|
||
return 'дни не выбраны';
|
||
}
|
||
if (weekdays.length == scheduleWeekdayOptions.length) {
|
||
return 'каждый день';
|
||
}
|
||
final weekdaySet = weekdays.toSet();
|
||
final selected = scheduleWeekdayOptions
|
||
.where((option) => weekdaySet.contains(option.backendValue))
|
||
.map((option) => option.shortLabel)
|
||
.toList();
|
||
return selected.join(', ');
|
||
}
|
||
|
||
String describeCronWeekdaysExpression(String? value) {
|
||
if (value == null || value.isEmpty || value == '*') {
|
||
return 'каждый день';
|
||
}
|
||
|
||
final weekdays = value
|
||
.split(',')
|
||
.map((item) => int.tryParse(item.trim()))
|
||
.whereType<int>()
|
||
.toSet();
|
||
if (weekdays.isEmpty) {
|
||
return value;
|
||
}
|
||
|
||
return describeCronWeekdays(weekdays);
|
||
}
|
||
|
||
String formatLocalScheduleDateTime(DateTime value) {
|
||
String pad(int number) => number.toString().padLeft(2, '0');
|
||
return '${pad(value.day)}.${pad(value.month)}.${value.year} ${pad(value.hour)}:${pad(value.minute)}';
|
||
}
|
||
|
||
String formatRunAtLabel(String? value) {
|
||
if (value == null || value.isEmpty) {
|
||
return 'время не указано';
|
||
}
|
||
|
||
final parsed = DateTime.tryParse(value);
|
||
if (parsed == null) {
|
||
return value;
|
||
}
|
||
|
||
return formatLocalScheduleDateTime(parsed.toLocal());
|
||
}
|