feat: Betreuung, Kinderprofile und Aufnahmeverwaltung implementieren

Dashboard mit Abholslots und Filter-Scrolling, Anwesenheit mit Audit-Verlauf sowie Elternanmeldung, Warteliste und bestätigte Aufnahme ergänzen.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Paul SchoeneckandCopilot committed 2026-11-03 08:01:25 +01:00
1 parent 10af6159b4
commit c1513c040b
36 files changed
+2593

No files matched your search

+49
View File
@@ -0,0 +1,49 @@
export function localClock(now = new Date()) {
const parts = new Intl.DateTimeFormat("en-GB", {
timeZone: process.env.APP_TIMEZONE ?? "Europe/Berlin",
year: "numeric", month: "2-digit", day: "2-digit",
hour: "2-digit", minute: "2-digit", hourCycle: "h23",
}).formatToParts(now);
const get = (type: Intl.DateTimeFormatPartTypes) => parts.find((p) => p.type === type)!.value;
const day = `${get("year")}-${get("month")}-${get("day")}`;
const weekday = new Date(`${day}T12:00:00Z`).getUTCDay() || 7;
return { day, time: `${get("hour")}:${get("minute")}`, weekday };
}
export function pickupState(pickup: string | null, time: string, end?: string | null) {
if (!pickup) return "none";
const minutes = (value: string) => Number(value.slice(0, 2)) * 60 + Number(value.slice(3, 5));
const delta = minutes(pickup) - minutes(time);
if (minutes(end ?? pickup) < minutes(time)) return "overdue";
if (delta <= 15) return "due";
return "later";
}
export function displayDay(day: string) {
return new Intl.DateTimeFormat("de-DE", {
weekday: "long", day: "numeric", month: "long", timeZone: "UTC",
}).format(new Date(`${day}T12:00:00Z`));
}
export function timeInZone(instant: string, timeZone: string) {
return new Intl.DateTimeFormat("en-GB", { timeZone, hour: "2-digit", minute: "2-digit", hourCycle: "h23" })
.format(new Date(instant));
}
export function localInstant(day: string, time: string, timeZone: string): Date {
const wall = Date.parse(`${day}T${time}:00Z`);
const format = (date: Date) => new Intl.DateTimeFormat("sv-SE", {
timeZone, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit",
minute: "2-digit", second: "2-digit", hourCycle: "h23",
}).format(date).replace(" ", "T");
const offsets = new Set([-36, 0, 36].map((hours) => {
const sample = new Date(wall + hours * 3_600_000);
return Date.parse(`${format(sample)}Z`) - sample.getTime();
}));
const candidates = [...offsets].map((offset) => new Date(wall - offset))
.filter((date) => format(date) === `${day}T${time}:00`);
if (candidates.length !== 1) {
throw new Error("Diese Uhrzeit ist wegen der Zeitumstellung nicht eindeutig oder existiert nicht. Bitte eine andere Uhrzeit wählen.");
}
return candidates[0];
}