feat: add household emergency duty alarms and deadline worker
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import type { NotdienstAlarm } from "@prisma/client";
|
||||
import {
|
||||
addDays,
|
||||
dateSchema,
|
||||
dateKey,
|
||||
todayKey,
|
||||
minuteClock,
|
||||
} from "@/lib/staff/calculations";
|
||||
|
||||
export { dateKey, todayKey };
|
||||
const berlin = new Intl.DateTimeFormat("en-CA", {
|
||||
timeZone: "Europe/Berlin",
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hourCycle: "h23",
|
||||
});
|
||||
export function berlinInstant(date: string, minute: number) {
|
||||
dateSchema.parse(date);
|
||||
const target = new Date(`${date}T00:00:00Z`).getTime() + minute * 60_000;
|
||||
let value = target;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const parts = Object.fromEntries(
|
||||
berlin.formatToParts(new Date(value)).map((p) => [p.type, p.value]),
|
||||
);
|
||||
const represented = Date.parse(
|
||||
`${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}:00Z`,
|
||||
);
|
||||
const diff = target - represented;
|
||||
if (!diff) return new Date(value);
|
||||
value += diff;
|
||||
}
|
||||
throw new Error(
|
||||
"Diese Uhrzeit existiert wegen der Zeitumstellung nicht. Bitte eine andere Uhrzeit wählen.",
|
||||
);
|
||||
}
|
||||
export function endOfAlarm(
|
||||
date: string,
|
||||
wholeDay: boolean,
|
||||
endMinute?: number | null,
|
||||
) {
|
||||
return wholeDay
|
||||
? berlinInstant(addDays(date, 1), 0)
|
||||
: berlinInstant(date, endMinute!);
|
||||
}
|
||||
export function durationLabel(
|
||||
alarm: Pick<NotdienstAlarm, "wholeDay" | "startMinute" | "endMinute">,
|
||||
) {
|
||||
return alarm.wholeDay
|
||||
? "Ganzer Kita-Tag"
|
||||
: `${minuteClock(alarm.startMinute!)}–${minuteClock(alarm.endMinute!)} Uhr`;
|
||||
}
|
||||
export function instantLabel(value: Date | string) {
|
||||
return new Intl.DateTimeFormat("de-DE", {
|
||||
timeZone: "Europe/Berlin",
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}).format(new Date(value));
|
||||
}
|
||||
export function alarmLabel(
|
||||
alarm: Pick<NotdienstAlarm, "status" | "deadline" | "expiresAt">,
|
||||
now = new Date(),
|
||||
) {
|
||||
if (alarm.status === "CANCELLED") return "Storniert";
|
||||
if (alarm.status === "CONFIRMED") return "Übernahme bestätigt";
|
||||
if (alarm.status === "DECLINED") return "Klärung erforderlich – Absage";
|
||||
if (alarm.status === "LATE_CONFIRMATION")
|
||||
return "Verspätete Zusage – Freigabe erforderlich";
|
||||
if (alarm.status === "REVIEW_REQUIRED")
|
||||
return "Klärung erforderlich – bisherige Rückmeldungen prüfen";
|
||||
if (alarm.deadline <= now || alarm.expiresAt <= now)
|
||||
return "Klärung erforderlich – Frist abgelaufen";
|
||||
return "Antwort ausstehend";
|
||||
}
|
||||
export function needsClarification(
|
||||
alarm: Pick<NotdienstAlarm, "status" | "deadline" | "expiresAt">,
|
||||
now = new Date(),
|
||||
) {
|
||||
return (
|
||||
!["CONFIRMED", "CANCELLED"].includes(alarm.status) &&
|
||||
(alarm.status !== "PENDING" ||
|
||||
alarm.deadline <= now ||
|
||||
alarm.expiresAt <= now)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user