initial commit

This commit is contained in:
2025-08-27 13:42:07 +02:00
commit 36ae378cb9
136 changed files with 5696 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
import 'dart:ui' show FontFeature;
import 'package:flutter/material.dart';
import '../models/work_day.dart';
String monthTitle(DateTime m) {
const months = [
'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'
];
return '${months[m.month - 1]} ${m.year}';
}
String leftDayLabel(DateTime d) => '${ddmm(d)} ${weekdayShort(d)}';
String rightDayLabel(DateTime d) => '${weekdayShort(d)} ${ddmm(d)}';
String weekdayShort(DateTime d) {
switch (d.weekday) {
case DateTime.monday: return 'Mo';
case DateTime.tuesday: return 'Di';
case DateTime.wednesday: return 'Mi';
case DateTime.thursday: return 'Do';
case DateTime.friday: return 'Fr';
case DateTime.saturday: return 'Sa';
case DateTime.sunday: return 'So';
default: return '';
}
}
String ddmm(DateTime d) =>
'${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}';
Widget mono(String s) =>
Text(s, style: const TextStyle(fontFeatures: [FontFeature.tabularFigures()]));
String fmtTimeOfDay(TimeOfDay t) =>
'${t.hour.toString().padLeft(2, '0')}:${t.minute.toString().padLeft(2, '0')}';
int minutesBetween(TimeOfDay a, TimeOfDay b) {
final aMin = a.hour * 60 + a.minute;
final bMin = b.hour * 60 + b.minute;
final d = bMin - aMin;
if (d <= 0) return 0;
if (d >= 24 * 60) return 24 * 60;
return d;
}
String minutesToHHMM(int minutes) {
final h = minutes ~/ 60;
final m = minutes % 60;
return '${h.toString().padLeft(2, '0')}:${m.toString().padLeft(2, '0')}';
}
String minutesToSignedHHMM(int minutes) {
final sign = minutes < 0 ? '-' : '+';
final absMin = minutes.abs();
final h = absMin ~/ 60;
final m = absMin % 60;
return '$sign${h.toString().padLeft(2, '0')}:${m.toString().padLeft(2, '0')}';
}
TimeOfDay? parseHHMM(dynamic v) {
if (v == null) return null;
final s = v.toString().trim();
if (s.isEmpty) return null;
final parts = s.split(':');
if (parts.length < 2) return null;
final h = int.tryParse(parts[0]) ?? 0;
final m = int.tryParse(parts[1]) ?? 0;
if (h < 0 || h > 23 || m < 0 || m > 59) return null;
return TimeOfDay(hour: h, minute: m);
}
TimeOfDay? parseTextHHMM(String s) {
final t = s.trim();
if (t.length != 5 || t[2] != ':') return null;
final h = int.tryParse(t.substring(0, 2));
final m = int.tryParse(t.substring(3, 5));
if (h == null || m == null) return null;
if (h < 0 || h > 23 || m < 0 || m > 59) return null;
return TimeOfDay(hour: h, minute: m);
}
String ymd(DateTime d) =>
'${d.year.toString().padLeft(4, '0')}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}';
// Füllt alle Tage des Monats auf
List<WorkDay> fillMonth(DateTime monthStart, List<WorkDay> existing) {
final map = <String, WorkDay>{for (final w in existing) ymd(w.date): w};
final nextMonth = DateTime(monthStart.year, monthStart.month + 1, 1);
final out = <WorkDay>[];
for (DateTime d = monthStart; d.isBefore(nextMonth); d = d.add(const Duration(days: 1))) {
final key = ymd(d);
final wd = map[key];
if (wd != null) {
out.add(wd);
} else {
final isWeekend = d.weekday == DateTime.saturday || d.weekday == DateTime.sunday;
final target = isWeekend ? 0 : 8 * 60;
out.add(WorkDay(date: d, intervals: const [], targetMinutes: target));
}
}
return out;
}
+42
View File
@@ -0,0 +1,42 @@
import '../utils/helpers.dart';
Map<String, String> buildHolidayMapAT(int year) {
final Map<String, String> m = {};
void add(DateTime d, String name) => m[ymd(d)] = name;
add(DateTime(year, 1, 1), 'Neujahr');
add(DateTime(year, 1, 6), 'Heilige Drei Könige');
add(DateTime(year, 5, 1), 'Staatsfeiertag');
add(DateTime(year, 8, 15), 'Mariä Himmelfahrt');
add(DateTime(year, 10, 26), 'Nationalfeiertag');
add(DateTime(year, 11, 1), 'Allerheiligen');
add(DateTime(year, 12, 8), 'Mariä Empfängnis');
add(DateTime(year, 12, 25), 'Christtag');
add(DateTime(year, 12, 26), 'Stefanitag');
final easter = _easterSunday(year);
add(easter.add(const Duration(days: 1)), 'Ostermontag');
add(easter.add(const Duration(days: 39)), 'Christi Himmelfahrt');
add(easter.add(const Duration(days: 50)), 'Pfingstmontag');
add(easter.add(const Duration(days: 60)), 'Fronleichnam');
return m;
}
DateTime _easterSunday(int year) {
final a = year % 19;
final b = year ~/ 100;
final c = year % 100;
final d = b ~/ 4;
final e = b % 4;
final f = (b + 8) ~/ 25;
final g = (b - f + 1) ~/ 3;
final h = (19 * a + b - d - g + 15) % 30;
final i = c ~/ 4;
final k = c % 4;
final l = (32 + 2 * e + 2 * i - h - k) % 7;
final m = (a + 11 * h + 22 * l) ~/ 451;
final month = (h + l - 7 * m + 114) ~/ 31; // 3=March, 4=April
final day = ((h + l - 7 * m + 114) % 31) + 1;
return DateTime(year, month, day);
}
+26
View File
@@ -0,0 +1,26 @@
import 'package:flutter/services.dart';
class HHmmInputFormatter extends TextInputFormatter {
const HHmmInputFormatter();
@override
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
String digits = newValue.text.replaceAll(RegExp(r'[^0-9]'), '');
if (digits.isEmpty) {
return const TextEditingValue(text: '', selection: TextSelection.collapsed(offset: 0));
}
if (digits.length > 4) digits = digits.substring(0, 4);
String text;
if (digits.length <= 2) {
text = digits;
} else {
final hh = digits.substring(0, 2);
final mm = digits.substring(2);
text = '$hh:$mm'; // ← Interpolation statt + ':' +
}
final offset = text.length;
return TextEditingValue(text: text, selection: TextSelection.collapsed(offset: offset));
}
}