feat: implement emergency duty (Notdienst) swapping for parents

This commit is contained in:
t.indorf
2026-05-23 19:18:30 +02:00
parent 4cd16773c1
commit 3ce5638449
9 changed files with 1638 additions and 119 deletions
@@ -0,0 +1,178 @@
import {
Body,
Button,
Container,
Head,
Heading,
Html,
Preview,
Section,
Text,
} from "@react-email/components";
import { baseBodyStyle, colors, fontStack } from "./_styles";
type NotdienstAssignmentChangeEmailProps = {
parentName: string;
heading: string;
preview: string;
body: string;
dateLabel: string;
link: string;
};
export function NotdienstAssignmentChangeEmail({
parentName,
heading,
preview,
body,
dateLabel,
link,
}: NotdienstAssignmentChangeEmailProps) {
return (
<Html lang="de">
<Head />
<Preview>{preview}</Preview>
<Body style={styles.body}>
<Container style={styles.container}>
<Section style={styles.card}>
<Section style={styles.brandBar}>
<Text style={styles.brand}>Kita-Planer</Text>
</Section>
<Text style={styles.kicker}>Notdienst-Einteilung</Text>
<Heading style={styles.heading}>{heading}</Heading>
<Text style={styles.lead}>Hallo {parentName},</Text>
<Text style={styles.text}>{body}</Text>
<Section style={styles.dateBadge}>
<Text style={styles.dateText}>{dateLabel}</Text>
</Section>
<Button href={link} style={styles.button}>
Zum Kita-Planer
</Button>
<Text style={styles.fallbackText}>
Falls der Button nicht funktioniert, kopiere diesen Link in deinen Browser:
</Text>
<Text style={styles.linkText}>{link}</Text>
</Section>
<Text style={styles.footer}>
Diese Nachricht wurde automatisch vom Kita-Planer deiner Kita versendet.
</Text>
</Container>
</Body>
</Html>
);
}
const styles = {
body: baseBodyStyle,
container: {
width: "100%",
maxWidth: "600px",
margin: "0 auto",
padding: "32px 20px",
fontFamily: fontStack,
},
card: {
padding: "0 28px 28px",
backgroundColor: colors.cardBg,
borderRadius: "10px",
border: `1px solid ${colors.border}`,
boxShadow: "0 10px 26px rgba(31, 59, 45, 0.08)",
overflow: "hidden",
fontFamily: fontStack,
},
brandBar: {
margin: "0 -28px 28px",
padding: "14px 28px",
backgroundColor: colors.brandGreen,
fontFamily: fontStack,
},
brand: {
margin: 0,
color: colors.eyebrow,
fontSize: "12px",
fontWeight: 800,
letterSpacing: "0.08em",
textTransform: "uppercase" as const,
fontFamily: fontStack,
},
kicker: {
margin: "0 0 10px",
color: colors.brandGreenAccent,
fontSize: "12px",
fontWeight: 800,
letterSpacing: "0.06em",
textTransform: "uppercase" as const,
fontFamily: fontStack,
},
heading: {
margin: "0 0 14px",
color: colors.textPrimary,
fontSize: "28px",
lineHeight: "34px",
fontWeight: 800,
fontFamily: fontStack,
},
lead: {
margin: "0 0 18px",
color: colors.textBody,
fontSize: "16px",
lineHeight: "24px",
fontFamily: fontStack,
},
text: {
margin: "0 0 22px",
color: colors.textBody,
fontSize: "15px",
lineHeight: "24px",
fontFamily: fontStack,
},
dateBadge: {
margin: "0 0 24px",
padding: "16px 18px",
backgroundColor: colors.bgAlt,
border: `1px solid ${colors.border}`,
borderRadius: "8px",
},
dateText: {
margin: 0,
color: colors.textPrimary,
fontSize: "17px",
lineHeight: "24px",
fontWeight: 800,
fontFamily: fontStack,
},
button: {
display: "inline-block",
padding: "12px 18px",
backgroundColor: colors.brandGreenAccent,
color: "#ffffff",
borderRadius: "8px",
textDecoration: "none",
fontWeight: 700,
fontFamily: fontStack,
},
fallbackText: {
margin: "22px 0 6px",
color: colors.textMuted,
fontSize: "12px",
lineHeight: "18px",
fontFamily: fontStack,
},
linkText: {
margin: 0,
color: colors.link,
fontSize: "12px",
lineHeight: "18px",
wordBreak: "break-all" as const,
fontFamily: fontStack,
},
footer: {
margin: "18px 0 0",
color: colors.textFaint,
fontSize: "12px",
lineHeight: "18px",
textAlign: "center" as const,
fontFamily: fontStack,
},
};
+199
View File
@@ -0,0 +1,199 @@
import {
Body,
Button,
Container,
Head,
Heading,
Html,
Preview,
Section,
Text,
} from "@react-email/components";
import { baseBodyStyle, colors, fontStack } from "./_styles";
type NotdienstSwapEmailProps = {
parentName: string;
heading: string;
preview: string;
kitaName: string;
body: string;
targetDateLabel: string;
offeredDateLabel: string;
link: string;
};
export function NotdienstSwapEmail({
parentName,
heading,
preview,
kitaName,
body,
targetDateLabel,
offeredDateLabel,
link,
}: NotdienstSwapEmailProps) {
return (
<Html lang="de">
<Head />
<Preview>{preview}</Preview>
<Body style={styles.body}>
<Container style={styles.container}>
<Section style={styles.card}>
<Section style={styles.brandBar}>
<Text style={styles.brand}>Kita-Planer</Text>
</Section>
<Text style={styles.kicker}>Notdienst-Tausch</Text>
<Heading style={styles.heading}>{heading}</Heading>
<Text style={styles.lead}>Hallo {parentName},</Text>
<Text style={styles.text}>
in der Kita <strong>{kitaName}</strong>: {body}
</Text>
<Section style={styles.dateBox}>
<Text style={styles.dateLabel}>Angefragter Notdienst</Text>
<Text style={styles.dateText}>{targetDateLabel}</Text>
<Text style={styles.dateLabel}>Angebotener Tauschdienst</Text>
<Text style={styles.dateText}>{offeredDateLabel}</Text>
</Section>
<Button href={link} style={styles.button}>
Zum Kalender
</Button>
<Text style={styles.fallbackText}>
Falls der Button nicht funktioniert, kopiere diesen Link in deinen Browser:
</Text>
<Text style={styles.linkText}>{link}</Text>
</Section>
<Text style={styles.footer}>
Diese Nachricht wurde automatisch vom Kita-Planer deiner Kita versendet.
</Text>
</Container>
</Body>
</Html>
);
}
const styles = {
body: baseBodyStyle,
container: {
width: "100%",
maxWidth: "600px",
margin: "0 auto",
padding: "32px 20px",
fontFamily: fontStack,
},
card: {
padding: "0 28px 28px",
backgroundColor: colors.cardBg,
borderRadius: "10px",
border: `1px solid ${colors.border}`,
boxShadow: "0 10px 26px rgba(31, 59, 45, 0.08)",
overflow: "hidden",
fontFamily: fontStack,
},
brandBar: {
margin: "0 -28px 28px",
padding: "14px 28px",
backgroundColor: colors.brandGreen,
fontFamily: fontStack,
},
brand: {
margin: 0,
color: colors.eyebrow,
fontSize: "12px",
fontWeight: 800,
letterSpacing: "0.08em",
textTransform: "uppercase" as const,
fontFamily: fontStack,
},
kicker: {
margin: "0 0 10px",
color: colors.brandGreenAccent,
fontSize: "12px",
fontWeight: 800,
letterSpacing: "0.06em",
textTransform: "uppercase" as const,
fontFamily: fontStack,
},
heading: {
margin: "0 0 14px",
color: colors.textPrimary,
fontSize: "28px",
lineHeight: "34px",
fontWeight: 800,
fontFamily: fontStack,
},
lead: {
margin: "0 0 18px",
color: colors.textBody,
fontSize: "16px",
lineHeight: "24px",
fontFamily: fontStack,
},
text: {
margin: "0 0 22px",
color: colors.textBody,
fontSize: "15px",
lineHeight: "24px",
fontFamily: fontStack,
},
dateBox: {
margin: "0 0 24px",
padding: "18px",
backgroundColor: colors.bgAlt,
border: `1px solid ${colors.border}`,
borderRadius: "8px",
},
dateLabel: {
margin: "0 0 4px",
color: colors.textMuted,
fontSize: "12px",
fontWeight: 700,
textTransform: "uppercase" as const,
letterSpacing: "0.04em",
fontFamily: fontStack,
},
dateText: {
margin: "0 0 16px",
color: colors.textPrimary,
fontSize: "16px",
lineHeight: "24px",
fontWeight: 700,
fontFamily: fontStack,
},
button: {
display: "inline-block",
padding: "12px 18px",
backgroundColor: colors.brandGreenAccent,
color: "#ffffff",
borderRadius: "8px",
textDecoration: "none",
fontWeight: 700,
fontFamily: fontStack,
},
fallbackText: {
margin: "22px 0 6px",
color: colors.textMuted,
fontSize: "12px",
lineHeight: "18px",
fontFamily: fontStack,
},
linkText: {
margin: 0,
color: colors.link,
fontSize: "12px",
lineHeight: "18px",
wordBreak: "break-all" as const,
fontFamily: fontStack,
},
footer: {
margin: "18px 0 0",
color: colors.textFaint,
fontSize: "12px",
lineHeight: "18px",
textAlign: "center" as const,
fontFamily: fontStack,
},
};