Compare commits

..

4 Commits

Author SHA1 Message Date
t.indorf 3fa3b9b108 Merge branch 'codex/fix-invite-activation' 2026-05-19 11:14:28 +02:00
t.indorf 84714b8b32 Fix server action state exports 2026-05-19 11:09:53 +02:00
t.indorf 811468a22b Merge branch 'codex/improve-invite-email' 2026-05-19 10:51:57 +02:00
t.indorf 968d108c28 Improve invite email layout 2026-05-19 10:29:42 +02:00
7 changed files with 52 additions and 50 deletions
-2
View File
@@ -53,8 +53,6 @@ export type InviteState = {
}; };
}; };
export const initialInviteState: InviteState = { attempt: 0 };
function echoCheckboxValues(formData: FormData): InviteState["values"] { function echoCheckboxValues(formData: FormData): InviteState["values"] {
return { return {
acceptPrivacyPolicy: formData.get("acceptPrivacyPolicy") === "on", acceptPrivacyPolicy: formData.get("acceptPrivacyPolicy") === "on",
+3 -1
View File
@@ -6,7 +6,9 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { acceptInviteAction, initialInviteState } from "./actions"; import { acceptInviteAction, type InviteState } from "./actions";
const initialInviteState: InviteState = { attempt: 0 };
// ===================================================================== // =====================================================================
// InviteForm · Client Component // InviteForm · Client Component
-2
View File
@@ -70,8 +70,6 @@ export type OnboardingState = {
}; };
}; };
export const initialOnboardingState: OnboardingState = { attempt: 0 };
function echoValues(formData: FormData): OnboardingState["values"] { function echoValues(formData: FormData): OnboardingState["values"] {
const min = formData.get("notdienstMinPerChildPerMonth"); const min = formData.get("notdienstMinPerChildPerMonth");
return { return {
+3 -4
View File
@@ -7,10 +7,9 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { import { completeOnboardingAction, type OnboardingState } from "./actions";
completeOnboardingAction,
initialOnboardingState, const initialOnboardingState: OnboardingState = { attempt: 0 };
} from "./actions";
export function OnboardingForm() { export function OnboardingForm() {
const [state, formAction, pending] = useActionState( const [state, formAction, pending] = useActionState(
-2
View File
@@ -52,8 +52,6 @@ export type RegisterState = {
}; };
}; };
export const initialRegisterState: RegisterState = { attempt: 0 };
function echoValues(formData: FormData): RegisterState["values"] { function echoValues(formData: FormData): RegisterState["values"] {
return { return {
firstName: String(formData.get("firstName") ?? ""), firstName: String(formData.get("firstName") ?? ""),
+3 -1
View File
@@ -7,7 +7,9 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { initialRegisterState, registerAction } from "./actions"; import { registerAction, type RegisterState } from "./actions";
const initialRegisterState: RegisterState = { attempt: 0 };
export function RegisterForm() { export function RegisterForm() {
const [state, formAction, pending] = useActionState( const [state, formAction, pending] = useActionState(
+43 -38
View File
@@ -4,7 +4,6 @@ import {
Container, Container,
Head, Head,
Heading, Heading,
Hr,
Html, Html,
Preview, Preview,
Section, Section,
@@ -30,15 +29,16 @@ export function InviteEmail({
<Preview>Aktiviere deinen Kita-Planer Account für {kitaName}</Preview> <Preview>Aktiviere deinen Kita-Planer Account für {kitaName}</Preview>
<Body style={styles.body}> <Body style={styles.body}>
<Container style={styles.container}> <Container style={styles.container}>
<Section style={styles.header}> <Section style={styles.card}>
<Text style={styles.brand}>Kita-Planer</Text> <Section style={styles.brandBar}>
<Text style={styles.brand}>Kita-Planer</Text>
</Section>
<Text style={styles.kicker}>Einladung zur Kita-App</Text>
<Heading style={styles.heading}>Willkommen bei {kitaName}</Heading> <Heading style={styles.heading}>Willkommen bei {kitaName}</Heading>
<Text style={styles.lead}> <Text style={styles.lead}>
Hallo {parentName}, dein Eltern-Account wurde vorbereitet. Hallo {parentName}, dein Eltern-Account wurde vorbereitet.
</Text> </Text>
</Section>
<Section style={styles.card}>
<Text style={styles.text}> <Text style={styles.text}>
Über den folgenden Link kannst du dein Passwort setzen und deinen Über den folgenden Link kannst du dein Passwort setzen und deinen
Account aktivieren. Danach hast du Zugriff auf die Kita-Planung, Account aktivieren. Danach hast du Zugriff auf die Kita-Planung,
@@ -56,7 +56,6 @@ export function InviteEmail({
<Text style={styles.linkText}>{inviteLink}</Text> <Text style={styles.linkText}>{inviteLink}</Text>
</Section> </Section>
<Hr style={styles.hr} />
<Text style={styles.footer}> <Text style={styles.footer}>
Diese Einladung wurde von deiner Kita erstellt. Wenn du sie nicht Diese Einladung wurde von deiner Kita erstellt. Wenn du sie nicht
erwartet hast, kannst du diese E-Mail ignorieren. erwartet hast, kannst du diese E-Mail ignorieren.
@@ -76,54 +75,64 @@ const styles = {
padding: "32px 20px", padding: "32px 20px",
fontFamily: fontStack, fontFamily: fontStack,
}, },
header: { card: {
padding: "28px 28px 20px", 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, backgroundColor: colors.brandGreen,
borderRadius: "8px 8px 0 0",
fontFamily: fontStack, fontFamily: fontStack,
}, },
brand: { brand: {
margin: "0 0 28px", margin: 0,
color: colors.eyebrow, color: colors.eyebrow,
fontSize: "13px", fontSize: "12px",
fontWeight: 700, fontWeight: 800,
letterSpacing: "0.08em", letterSpacing: "0.08em",
textTransform: "uppercase" as const, textTransform: "uppercase" as const,
fontFamily: fontStack, fontFamily: fontStack,
}, },
kicker: {
margin: "0 0 10px",
color: colors.brandGreenAccent,
fontSize: "12px",
fontWeight: 800,
letterSpacing: "0.06em",
textTransform: "uppercase" as const,
fontFamily: fontStack,
},
heading: { heading: {
margin: "0 0 12px", margin: "0 0 14px",
color: colors.textHeader, color: colors.textPrimary,
fontSize: "30px", fontSize: "28px",
lineHeight: "1.18", lineHeight: "34px",
fontWeight: 800, fontWeight: 800,
fontFamily: fontStack, fontFamily: fontStack,
}, },
lead: { lead: {
margin: 0, margin: "0 0 22px",
color: colors.lead, color: colors.textBody,
fontSize: "16px", fontSize: "16px",
lineHeight: "1.55", lineHeight: "24px",
fontFamily: fontStack,
},
card: {
padding: "28px",
backgroundColor: colors.cardBg,
borderRadius: "0 0 8px 8px",
border: `1px solid ${colors.border}`,
borderTop: "0",
fontFamily: fontStack, fontFamily: fontStack,
}, },
text: { text: {
margin: "0 0 24px", margin: "0 0 24px",
color: colors.textBody, color: colors.textBody,
fontSize: "16px", fontSize: "15px",
lineHeight: "1.65", lineHeight: "24px",
fontFamily: fontStack, fontFamily: fontStack,
}, },
button: { button: {
display: "inline-block", display: "inline-block",
padding: "14px 22px", padding: "14px 20px",
backgroundColor: colors.brandGreen, backgroundColor: colors.brandGreen,
color: "#ffffff", color: "#ffffff",
borderRadius: "6px", borderRadius: "6px",
@@ -136,26 +145,22 @@ const styles = {
margin: "28px 0 8px", margin: "28px 0 8px",
color: colors.textMuted, color: colors.textMuted,
fontSize: "13px", fontSize: "13px",
lineHeight: "1.5", lineHeight: "20px",
fontFamily: fontStack, fontFamily: fontStack,
}, },
linkText: { linkText: {
margin: 0, margin: 0,
color: colors.link, color: colors.link,
fontSize: "13px", fontSize: "13px",
lineHeight: "1.5", lineHeight: "20px",
wordBreak: "break-all" as const, wordBreak: "break-all" as const,
fontFamily: fontStack, fontFamily: fontStack,
}, },
hr: {
margin: "24px 0",
borderColor: colors.hr,
},
footer: { footer: {
margin: 0, margin: "20px 0 0",
color: colors.textFaint, color: colors.textFaint,
fontSize: "12px", fontSize: "12px",
lineHeight: "1.5", lineHeight: "18px",
textAlign: "center" as const, textAlign: "center" as const,
fontFamily: fontStack, fontFamily: fontStack,
}, },