Apply linen design system across app
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import Link from "next/link";
|
||||
import { UserRole } from "@prisma/client";
|
||||
import { Info, Calendar, ArrowRight, ShieldAlert } from "lucide-react";
|
||||
import { TerminStatus, TerminType, UserRole } from "@prisma/client";
|
||||
import { ArrowRight, ShieldAlert } from "lucide-react";
|
||||
import { Manrope } from "next/font/google";
|
||||
|
||||
import { requireRole } from "@/lib/auth-utils";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
@@ -10,6 +11,11 @@ import { NotdienstEntry } from "./notdienst-entry";
|
||||
|
||||
export const metadata = { title: "Notdienst · Kita-Planer" };
|
||||
|
||||
const manrope = Manrope({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500", "600", "700"],
|
||||
});
|
||||
|
||||
export default async function NotdienstPage() {
|
||||
// ELTERN können eintragen; Admins/Koordinatoren haben hier eigentlich
|
||||
// andere Ansichten (Plan-Generierung), aber wir lassen sie zur Demonstration
|
||||
@@ -44,7 +50,7 @@ export default async function NotdienstPage() {
|
||||
: [];
|
||||
|
||||
const targetData = getTargetMonthData();
|
||||
const { targetYear, targetMonth, monthName, isLocked } = targetData;
|
||||
const { targetYear, targetMonth, isLocked } = targetData;
|
||||
const targetMonthStart = new Date(Date.UTC(targetYear, targetMonth - 1, 1));
|
||||
const targetMonthEnd = new Date(Date.UTC(targetYear, targetMonth, 1));
|
||||
|
||||
@@ -52,7 +58,7 @@ export default async function NotdienstPage() {
|
||||
const childIds = children.map((child) => child.id);
|
||||
|
||||
// Bisherige Einträge für den Zielmonat laden, haushaltsweit über die Kinder.
|
||||
const [availabilities, availabilityCountForCurrentOrNextMonth] =
|
||||
const [availabilities, termine] =
|
||||
children.length > 0
|
||||
? await Promise.all([
|
||||
prisma.notdienstAvailability.findMany({
|
||||
@@ -66,74 +72,88 @@ export default async function NotdienstPage() {
|
||||
},
|
||||
select: { date: true },
|
||||
}),
|
||||
prisma.notdienstAvailability.count({
|
||||
prisma.termin.findMany({
|
||||
where: {
|
||||
kitaId,
|
||||
childId: { in: childIds },
|
||||
date: {
|
||||
gte: new Date(new Date().getFullYear(), new Date().getMonth(), 1),
|
||||
lt: new Date(new Date().getFullYear(), new Date().getMonth() + 2, 1),
|
||||
},
|
||||
status: TerminStatus.CONFIRMED,
|
||||
startDate: { lt: targetMonthEnd },
|
||||
endDate: { gte: targetMonthStart },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
type: true,
|
||||
startDate: true,
|
||||
endDate: true,
|
||||
allDay: true,
|
||||
},
|
||||
orderBy: { startDate: "asc" },
|
||||
}),
|
||||
])
|
||||
: [[], 0];
|
||||
: [[], []];
|
||||
|
||||
const selectedDates = availabilities.map((a) => formatDateKey(a.date));
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-4xl px-4 py-8 sm:px-8">
|
||||
<div className="mb-6 flex flex-col justify-between gap-4 sm:flex-row sm:items-center">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold tracking-tight flex items-center gap-2">
|
||||
<Calendar className="h-6 w-6 text-primary" />
|
||||
Notdienst planen
|
||||
</h1>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
Gib hier an, an welchen Tagen du im Notdienst unterstützen kannst.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-1 sm:items-end">
|
||||
<span className="rounded-full bg-primary/10 px-3 py-1 text-sm font-medium text-primary">
|
||||
Zielmonat: {monthName}
|
||||
</span>
|
||||
{isLocked && (
|
||||
<span className="text-xs font-medium text-destructive flex items-center gap-1">
|
||||
<Info className="h-3 w-3" />
|
||||
Eingabefrist abgelaufen
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`${manrope.className} mx-auto max-w-7xl px-4 py-8 sm:px-8`}>
|
||||
{children.length === 0 ? (
|
||||
<NotdienstLockout />
|
||||
) : (
|
||||
<NotdienstEntry
|
||||
hasAnyAvailability={availabilityCountForCurrentOrNextMonth > 0}
|
||||
targetYear={targetYear}
|
||||
targetMonth={targetMonth}
|
||||
isLocked={isLocked}
|
||||
requiredDaysTotal={requiredDaysTotal}
|
||||
initialSelectedDates={selectedDates}
|
||||
childrenIds={childIds}
|
||||
calendarItems={termine.map((termin) => ({
|
||||
id: termin.id,
|
||||
title: termin.title,
|
||||
category: getTerminCategory(termin.type),
|
||||
startDate: termin.startDate,
|
||||
endDate: termin.endDate,
|
||||
allDay: termin.allDay,
|
||||
note: termin.description,
|
||||
}))}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getTerminCategory(type: TerminType) {
|
||||
switch (type) {
|
||||
case TerminType.SCHLIESSTAG:
|
||||
return "closure" as const;
|
||||
case TerminType.TEAMTAG:
|
||||
return "team" as const;
|
||||
case TerminType.KITA_FEST:
|
||||
case TerminType.MITMACH_TAG:
|
||||
return "social" as const;
|
||||
case TerminType.ELTERNABEND:
|
||||
case TerminType.MITGLIEDERVERSAMMLUNG:
|
||||
case TerminType.ELTERNCAFE:
|
||||
return "parents" as const;
|
||||
case TerminType.GEBURTSTAG_INTERN:
|
||||
case TerminType.GEBURTSTAG_EXTERN:
|
||||
return "birthday" as const;
|
||||
default:
|
||||
return "social" as const;
|
||||
}
|
||||
}
|
||||
|
||||
function NotdienstLockout() {
|
||||
return (
|
||||
<div className="flex min-h-[420px] items-center justify-center rounded-lg border border-dashed bg-muted/20 p-8 text-center">
|
||||
<div className="flex min-h-[420px] items-center justify-center rounded-lg border border-dashed border-[var(--hairline)] bg-[var(--surface-2)] p-8 text-center">
|
||||
<div className="mx-auto max-w-lg">
|
||||
<div className="mx-auto flex h-20 w-20 items-center justify-center rounded-full bg-amber-100 text-amber-700 shadow-sm">
|
||||
<ShieldAlert className="h-10 w-10" />
|
||||
<div className="mx-auto flex h-20 w-20 items-center justify-center rounded-2xl bg-[var(--accent-soft)] text-[var(--accent-deep)]">
|
||||
<ShieldAlert className="h-10 w-10 [stroke-width:1.7]" />
|
||||
</div>
|
||||
<h2 className="mt-6 text-2xl font-semibold tracking-tight">
|
||||
<h2 className="mt-6 text-2xl font-medium tracking-[-0.02em] text-[var(--ink)]">
|
||||
Erst Kinder hinterlegen
|
||||
</h2>
|
||||
<p className="mt-3 text-sm leading-6 text-muted-foreground">
|
||||
<p className="mt-3 text-sm leading-6 text-[var(--ink-soft)]">
|
||||
Bitte lege zuerst deine Kinder im Profil an, bevor du Notdienste
|
||||
übernimmst.
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user