Implement Dusk theme system and polish
This commit is contained in:
parent
e269a155da
commit
9280939756
38 changed files with 1144 additions and 270 deletions
|
|
@ -55,14 +55,14 @@ export default async function CheckInPage({ searchParams }: CheckInPageProps) {
|
|||
);
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(167,201,87,0.22),_transparent_32%),linear-gradient(180deg,_#f5f4ee_0%,_#eef2e6_100%)] px-6 py-10 text-slate-900 sm:px-8">
|
||||
<main className="app-page">
|
||||
<div className="mx-auto flex max-w-6xl flex-col gap-8">
|
||||
<StatusToastBridge toast={statusToast} paramKeys={["error", "status"]} />
|
||||
|
||||
<header className="flex flex-col gap-5 rounded-[2rem] border border-black/10 bg-white/75 p-6 shadow-[0_18px_60px_rgba(71,85,105,0.12)] backdrop-blur sm:flex-row sm:items-start sm:justify-between sm:p-8">
|
||||
<header className="app-page-header">
|
||||
<div>
|
||||
<div className="flex flex-wrap items-center gap-3 text-xs font-semibold uppercase tracking-[0.24em] text-slate-500">
|
||||
<Link href="/dashboard" className="transition hover:text-slate-900">
|
||||
<div className="app-page-breadcrumb">
|
||||
<Link href="/dashboard" className="app-page-link">
|
||||
Dashboard
|
||||
</Link>
|
||||
<span>/</span>
|
||||
|
|
@ -71,7 +71,7 @@ export default async function CheckInPage({ searchParams }: CheckInPageProps) {
|
|||
<h1 className="mt-3 font-[family-name:var(--font-display)] text-4xl leading-tight">
|
||||
Ochtendcheck-in van vandaag
|
||||
</h1>
|
||||
<p className="mt-4 max-w-2xl text-base leading-8 text-slate-700">
|
||||
<p className="app-page-copy">
|
||||
Houd je start rustig en klein. Je legt alleen een energiescore en een
|
||||
globale slaapindruk vast voor vandaag.
|
||||
</p>
|
||||
|
|
@ -99,12 +99,12 @@ export default async function CheckInPage({ searchParams }: CheckInPageProps) {
|
|||
<CheckInForm todayCheckIn={checkInStatus?.todayCheckIn ?? null} />
|
||||
|
||||
<aside className="space-y-5">
|
||||
<Card className="rounded-[1.75rem] border border-border/60 bg-card/90 py-0 shadow-[0_12px_40px_rgba(71,85,105,0.08)]">
|
||||
<Card className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Vandaag
|
||||
</p>
|
||||
<CardTitle className="text-lg text-slate-900">
|
||||
<CardTitle className="text-lg text-foreground">
|
||||
{checkInStatus?.todayCheckIn ? "Check-in staat al klaar" : "Nog geen check-in"}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
|
@ -123,7 +123,7 @@ export default async function CheckInPage({ searchParams }: CheckInPageProps) {
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="rounded-[1.75rem] border border-primary/15 bg-primary py-0 text-primary-foreground shadow-[0_12px_40px_rgba(22,58,43,0.18)]">
|
||||
<Card tone="primary" elevation="raised" className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-primary-foreground/75">
|
||||
Bewuste grens
|
||||
|
|
|
|||
|
|
@ -77,19 +77,19 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
|
|||
);
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(167,201,87,0.22),_transparent_32%),linear-gradient(180deg,_#f5f4ee_0%,_#eef2e6_100%)] px-6 py-10 text-slate-900 sm:px-8">
|
||||
<main className="app-page">
|
||||
<div className="mx-auto flex max-w-6xl flex-col gap-8">
|
||||
<StatusToastBridge toast={statusToast} />
|
||||
|
||||
<header className="flex flex-col gap-5 rounded-[2rem] border border-black/10 bg-white/75 p-6 shadow-[0_18px_60px_rgba(71,85,105,0.12)] backdrop-blur sm:flex-row sm:items-start sm:justify-between sm:p-8">
|
||||
<header className="app-page-header">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-slate-500">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Protected route
|
||||
</p>
|
||||
<h1 className="mt-3 font-[family-name:var(--font-display)] text-4xl leading-tight">
|
||||
Dashboard placeholder voor release 1
|
||||
</h1>
|
||||
<p className="mt-4 max-w-2xl text-base leading-8 text-slate-700">
|
||||
<p className="app-page-copy">
|
||||
Je sessie is server-side gevalideerd en het minimale profielbundle is
|
||||
nu beschikbaar. Daarmee staat de fundering voor onboarding, settings
|
||||
en de eerste energieflows klaar.
|
||||
|
|
@ -135,12 +135,12 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
|
|||
</header>
|
||||
|
||||
<section className="grid gap-5 md:grid-cols-3">
|
||||
<Card className="rounded-[1.75rem] border border-border/60 bg-card/90 py-0 shadow-[0_12px_40px_rgba(71,85,105,0.08)]">
|
||||
<Card className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Auth
|
||||
</p>
|
||||
<CardTitle className="text-lg text-slate-900">Cookie-based sessie actief</CardTitle>
|
||||
<CardTitle className="text-lg text-foreground">Cookie-based sessie actief</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pb-6">
|
||||
<CardDescription className="text-sm leading-7 text-muted-foreground">
|
||||
|
|
@ -149,12 +149,12 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="rounded-[1.75rem] border border-border/60 bg-card/90 py-0 shadow-[0_12px_40px_rgba(71,85,105,0.08)]">
|
||||
<Card className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Profiel
|
||||
</p>
|
||||
<CardTitle className="text-lg text-slate-900">{profileTitle}</CardTitle>
|
||||
<CardTitle className="text-lg text-foreground">{profileTitle}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pb-6">
|
||||
<CardDescription className="text-sm leading-7 text-muted-foreground">
|
||||
|
|
@ -164,12 +164,12 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="rounded-[1.75rem] border border-border/60 bg-card/90 py-0 shadow-[0_12px_40px_rgba(71,85,105,0.08)]">
|
||||
<Card className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Onboarding
|
||||
</p>
|
||||
<CardTitle className="text-lg text-slate-900">{onboardingState}</CardTitle>
|
||||
<CardTitle className="text-lg text-foreground">{onboardingState}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pb-6">
|
||||
<CardDescription className="text-sm leading-7 text-muted-foreground">
|
||||
|
|
@ -179,12 +179,12 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="rounded-[1.75rem] border border-border/60 bg-card/90 py-0 shadow-[0_12px_40px_rgba(71,85,105,0.08)]">
|
||||
<Card className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Instellingen
|
||||
</p>
|
||||
<CardTitle className="text-lg text-slate-900">
|
||||
<CardTitle className="text-lg text-foreground">
|
||||
Punten {formatToggleState(settings.showEnergyPoints, "zichtbaar", "verborgen")}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
|
@ -198,12 +198,12 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
|
|||
|
||||
<CheckInCard todayCheckIn={checkInStatus?.todayCheckIn ?? null} />
|
||||
|
||||
<Card className="rounded-[1.75rem] border border-border/60 bg-card/90 py-0 shadow-[0_12px_40px_rgba(71,85,105,0.08)]">
|
||||
<Card className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Dagplanning
|
||||
</p>
|
||||
<CardTitle className="text-lg text-slate-900">
|
||||
<CardTitle className="text-lg text-foreground">
|
||||
{planningStatus?.activities.length
|
||||
? `${planningStatus.activities.length} activiteiten voor vandaag`
|
||||
: "Nog niets gepland voor vandaag"}
|
||||
|
|
@ -230,12 +230,12 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
|
|||
<EnergyMeterCard meter={planningMeter} tone="subtle" />
|
||||
|
||||
{isTestWizardEnabled() ? (
|
||||
<Card className="rounded-[1.75rem] border border-border/60 bg-card/90 py-0 shadow-[0_12px_40px_rgba(71,85,105,0.08)]">
|
||||
<Card className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Wizard core
|
||||
</p>
|
||||
<CardTitle className="text-lg text-slate-900">Interne testwizard actief</CardTitle>
|
||||
<CardTitle className="text-lg text-foreground">Interne testwizard actief</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pb-6">
|
||||
<CardDescription className="text-sm leading-7 text-muted-foreground">
|
||||
|
|
@ -247,11 +247,11 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
|
|||
</section>
|
||||
|
||||
{!profile.onboardingCompleted ? (
|
||||
<Card className="rounded-[1.75rem] border border-amber-900/15 bg-amber-50 py-0 text-amber-950 shadow-[0_12px_40px_rgba(146,64,14,0.08)]">
|
||||
<Card className="border-warning/32 bg-warning/16 py-0 text-foreground shadow-[var(--shadow-1)]">
|
||||
<CardContent className="flex flex-col gap-4 px-6 py-5 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p className="font-semibold">Je onboarding is nog niet afgerond.</p>
|
||||
<p className="mt-1 max-w-2xl text-sm leading-7 text-amber-900">
|
||||
<p className="mt-1 max-w-2xl text-sm leading-7 text-foreground/82">
|
||||
Je kunt de korte flow later alsnog afronden om je basisinstellingen
|
||||
en eerste voorkeuren vast te leggen.
|
||||
</p>
|
||||
|
|
@ -259,8 +259,8 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
|
|||
<Link
|
||||
href="/onboarding"
|
||||
className={cn(
|
||||
buttonVariants({ size: "lg" }),
|
||||
"h-11 shrink-0 rounded-full bg-amber-950 px-5 text-amber-50 hover:bg-amber-900",
|
||||
buttonVariants({ variant: "warning", size: "lg" }),
|
||||
"h-11 shrink-0 rounded-full px-5",
|
||||
)}
|
||||
>
|
||||
Rond onboarding af
|
||||
|
|
@ -268,7 +268,7 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
|
|||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<Card className="rounded-[1.75rem] border border-primary/10 bg-primary py-0 text-primary-foreground shadow-[0_12px_40px_rgba(22,58,43,0.18)]">
|
||||
<Card tone="primary" elevation="raised" className="py-0">
|
||||
<CardContent className="flex flex-col gap-4 px-6 py-5 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p className="font-semibold">Je instellingen kun je nu ook los beheren.</p>
|
||||
|
|
|
|||
266
app/globals.css
266
app/globals.css
|
|
@ -5,42 +5,52 @@
|
|||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
:root {
|
||||
--font-display: "Iowan Old Style", "Palatino Linotype", "URW Palladio L",
|
||||
Palatino, Georgia, serif;
|
||||
--font-body: "Inter", "Aptos", "Segoe UI", "Helvetica Neue", Arial,
|
||||
sans-serif;
|
||||
--background: #f5f4ee;
|
||||
--foreground: #0f172a;
|
||||
--card: rgb(255 255 255 / 0.84);
|
||||
--card-foreground: #0f172a;
|
||||
--popover: #ffffff;
|
||||
--popover-foreground: #0f172a;
|
||||
--primary: #163a2b;
|
||||
--primary-foreground: #effaf3;
|
||||
--secondary: #e5ecde;
|
||||
--secondary-foreground: #163a2b;
|
||||
--muted: #eef2e6;
|
||||
--muted-foreground: #51606f;
|
||||
--accent: #dbe7d1;
|
||||
--accent-foreground: #163a2b;
|
||||
--destructive: #b91c1c;
|
||||
--border: rgb(15 23 42 / 0.1);
|
||||
--input: rgb(15 23 42 / 0.12);
|
||||
--ring: #5d8a67;
|
||||
--chart-1: #163a2b;
|
||||
--chart-2: #5d8a67;
|
||||
--chart-3: #90a955;
|
||||
--chart-4: #b7c5a4;
|
||||
--chart-5: #d7dfce;
|
||||
--radius: 1rem;
|
||||
--sidebar: #fbfaf5;
|
||||
--sidebar-foreground: #0f172a;
|
||||
--sidebar-primary: #163a2b;
|
||||
--sidebar-primary-foreground: #effaf3;
|
||||
--sidebar-accent: #dbe7d1;
|
||||
--sidebar-accent-foreground: #163a2b;
|
||||
--sidebar-border: rgb(15 23 42 / 0.08);
|
||||
--sidebar-ring: #5d8a67;
|
||||
--font-body: var(--font-inter-tight), ui-sans-serif, system-ui, sans-serif;
|
||||
--font-display: var(--font-body);
|
||||
--font-mono: var(--font-plex-mono), ui-monospace, monospace;
|
||||
|
||||
--background: oklch(97% 0.008 80);
|
||||
--foreground: oklch(22% 0.03 262);
|
||||
--card: oklch(99% 0.004 80);
|
||||
--card-foreground: oklch(22% 0.03 262);
|
||||
--popover: oklch(100% 0 0);
|
||||
--popover-foreground: oklch(22% 0.03 262);
|
||||
|
||||
--primary: oklch(44% 0.11 262);
|
||||
--primary-foreground: oklch(98% 0.01 262);
|
||||
--secondary: oklch(92% 0.03 262);
|
||||
--secondary-foreground: oklch(44% 0.11 262);
|
||||
--muted: oklch(95% 0.012 82);
|
||||
--muted-foreground: oklch(58% 0.015 262);
|
||||
--accent: oklch(92% 0.03 262);
|
||||
--accent-foreground: oklch(44% 0.11 262);
|
||||
|
||||
--destructive: oklch(58% 0.16 25);
|
||||
--success: oklch(62% 0.09 155);
|
||||
--warning: oklch(72% 0.1 70);
|
||||
|
||||
--border: oklch(22% 0.03 262 / 0.1);
|
||||
--input: oklch(22% 0.03 262 / 0.12);
|
||||
--ring: oklch(44% 0.11 262);
|
||||
|
||||
--chart-1: oklch(44% 0.11 262);
|
||||
--chart-2: oklch(60% 0.09 262);
|
||||
--chart-3: oklch(70% 0.1 50);
|
||||
--chart-4: oklch(62% 0.09 155);
|
||||
--chart-5: oklch(80% 0.03 262);
|
||||
|
||||
--radius: 14px;
|
||||
--shadow-1: 0 1px 3px oklch(22% 0.03 262 / 0.1), 0 10px 30px oklch(22% 0.03 262 / 0.06);
|
||||
--shadow-2: 0 4px 16px oklch(22% 0.03 262 / 0.14), 0 18px 45px oklch(22% 0.03 262 / 0.12);
|
||||
--shadow-3: 0 24px 60px oklch(22% 0.03 262 / 0.18);
|
||||
--sidebar: oklch(96.5% 0.01 80);
|
||||
--sidebar-foreground: oklch(22% 0.03 262);
|
||||
--sidebar-primary: oklch(44% 0.11 262);
|
||||
--sidebar-primary-foreground: oklch(98% 0.01 262);
|
||||
--sidebar-accent: oklch(92% 0.03 262);
|
||||
--sidebar-accent-foreground: oklch(44% 0.11 262);
|
||||
--sidebar-border: oklch(22% 0.03 262 / 0.08);
|
||||
--sidebar-ring: oklch(44% 0.11 262);
|
||||
}
|
||||
|
||||
* {
|
||||
|
|
@ -79,6 +89,8 @@ a {
|
|||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-warning: var(--warning);
|
||||
--color-success: var(--success);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
|
|
@ -97,47 +109,60 @@ a {
|
|||
--color-card: var(--card);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-background: var(--background);
|
||||
--radius-sm: calc(var(--radius) * 0.6);
|
||||
--radius-md: calc(var(--radius) * 0.8);
|
||||
--font-mono: var(--font-plex-mono), ui-monospace, monospace;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 11px;
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) * 1.4);
|
||||
--radius-2xl: calc(var(--radius) * 1.8);
|
||||
--radius-3xl: calc(var(--radius) * 2.2);
|
||||
--radius-4xl: calc(var(--radius) * 2.6);
|
||||
--radius-xl: 21px;
|
||||
--radius-2xl: 25px;
|
||||
--radius-3xl: 32px;
|
||||
--radius-4xl: 40px;
|
||||
--radius-full: 9999px;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: #111927;
|
||||
--foreground: #eef6f0;
|
||||
--card: rgb(17 25 39 / 0.84);
|
||||
--card-foreground: #eef6f0;
|
||||
--popover: #152131;
|
||||
--popover-foreground: #eef6f0;
|
||||
--primary: #d9f2de;
|
||||
--primary-foreground: #133225;
|
||||
--secondary: #243244;
|
||||
--secondary-foreground: #eef6f0;
|
||||
--muted: #243244;
|
||||
--muted-foreground: #b1bec8;
|
||||
--accent: #31485b;
|
||||
--accent-foreground: #eef6f0;
|
||||
--destructive: #ef4444;
|
||||
--border: rgb(255 255 255 / 0.12);
|
||||
--input: rgb(255 255 255 / 0.14);
|
||||
--ring: #88b593;
|
||||
--chart-1: #d9f2de;
|
||||
--chart-2: #88b593;
|
||||
--chart-3: #90a955;
|
||||
--chart-4: #51606f;
|
||||
--chart-5: #243244;
|
||||
--sidebar: #152131;
|
||||
--sidebar-foreground: #eef6f0;
|
||||
--sidebar-primary: #d9f2de;
|
||||
--sidebar-primary-foreground: #133225;
|
||||
--sidebar-accent: #243244;
|
||||
--sidebar-accent-foreground: #eef6f0;
|
||||
--sidebar-border: rgb(255 255 255 / 0.12);
|
||||
--sidebar-ring: #88b593;
|
||||
--background: oklch(17% 0.02 262);
|
||||
--foreground: oklch(96% 0.008 80);
|
||||
--card: oklch(22% 0.025 262);
|
||||
--card-foreground: oklch(96% 0.008 80);
|
||||
--popover: oklch(22% 0.025 262);
|
||||
--popover-foreground: oklch(96% 0.008 80);
|
||||
|
||||
--primary: oklch(78% 0.08 262);
|
||||
--primary-foreground: oklch(20% 0.03 262);
|
||||
--secondary: oklch(28% 0.03 262);
|
||||
--secondary-foreground: oklch(96% 0.008 80);
|
||||
--muted: oklch(26% 0.025 262);
|
||||
--muted-foreground: oklch(70% 0.015 262);
|
||||
--accent: oklch(30% 0.04 262);
|
||||
--accent-foreground: oklch(96% 0.008 80);
|
||||
|
||||
--destructive: oklch(70% 0.16 25);
|
||||
--success: oklch(74% 0.09 155);
|
||||
--warning: oklch(80% 0.1 70);
|
||||
|
||||
--border: oklch(100% 0 0 / 0.1);
|
||||
--input: oklch(100% 0 0 / 0.1);
|
||||
--ring: oklch(78% 0.08 262);
|
||||
|
||||
--chart-1: oklch(78% 0.08 262);
|
||||
--chart-2: oklch(60% 0.09 262);
|
||||
--chart-3: oklch(80% 0.1 70);
|
||||
--chart-4: oklch(74% 0.09 155);
|
||||
--chart-5: oklch(36% 0.03 262);
|
||||
|
||||
--shadow-1: 0 1px 3px oklch(0% 0 0 / 0.24), 0 10px 30px oklch(0% 0 0 / 0.2);
|
||||
--shadow-2: 0 4px 16px oklch(0% 0 0 / 0.28), 0 18px 45px oklch(0% 0 0 / 0.24);
|
||||
--shadow-3: 0 24px 60px oklch(0% 0 0 / 0.32);
|
||||
|
||||
--sidebar: oklch(20% 0.022 262);
|
||||
--sidebar-foreground: oklch(96% 0.008 80);
|
||||
--sidebar-primary: oklch(78% 0.08 262);
|
||||
--sidebar-primary-foreground: oklch(20% 0.03 262);
|
||||
--sidebar-accent: oklch(28% 0.03 262);
|
||||
--sidebar-accent-foreground: oklch(96% 0.008 80);
|
||||
--sidebar-border: oklch(100% 0 0 / 0.1);
|
||||
--sidebar-ring: oklch(78% 0.08 262);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
|
|
@ -152,12 +177,107 @@ a {
|
|||
@apply font-sans;
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
color-scheme: light;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
font-family: var(--font-body), sans-serif;
|
||||
line-height: 1.7;
|
||||
font-variant-numeric: tabular-nums;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
html.dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
h1 {
|
||||
letter-spacing: -0.03em;
|
||||
line-height: 1.08;
|
||||
}
|
||||
h2 {
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.2;
|
||||
}
|
||||
h3 {
|
||||
line-height: 1.3;
|
||||
}
|
||||
code,
|
||||
kbd,
|
||||
samp,
|
||||
pre {
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.app-page {
|
||||
@apply min-h-screen px-6 py-10 text-foreground sm:px-8;
|
||||
background-color: var(--background);
|
||||
background-image:
|
||||
radial-gradient(
|
||||
circle at top,
|
||||
color-mix(in oklab, var(--primary) 16%, transparent) 0%,
|
||||
transparent 34%
|
||||
),
|
||||
linear-gradient(
|
||||
180deg,
|
||||
color-mix(in oklab, var(--background) 96%, var(--secondary) 4%) 0%,
|
||||
color-mix(in oklab, var(--background) 86%, var(--secondary) 14%) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.dark .app-page {
|
||||
background-image:
|
||||
radial-gradient(
|
||||
circle at top,
|
||||
color-mix(in oklab, var(--primary) 22%, transparent) 0%,
|
||||
transparent 38%
|
||||
),
|
||||
linear-gradient(
|
||||
180deg,
|
||||
color-mix(in oklab, var(--background) 96%, var(--secondary) 4%) 0%,
|
||||
color-mix(in oklab, var(--background) 88%, var(--secondary) 12%) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.app-page-header {
|
||||
@apply flex flex-col gap-5 rounded-[var(--radius-4xl)] p-6 sm:flex-row sm:items-start sm:justify-between sm:p-8;
|
||||
border: 1px solid color-mix(in oklab, var(--border) 88%, transparent);
|
||||
background: color-mix(in oklab, var(--card) 84%, transparent);
|
||||
box-shadow: var(--shadow-2);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.app-page-breadcrumb {
|
||||
@apply flex flex-wrap items-center gap-3 text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground;
|
||||
}
|
||||
|
||||
.app-page-link {
|
||||
@apply transition hover:text-foreground;
|
||||
}
|
||||
|
||||
.app-page-copy {
|
||||
@apply mt-4 max-w-2xl text-base leading-8 text-muted-foreground;
|
||||
}
|
||||
|
||||
.app-panel-primary {
|
||||
@apply text-primary-foreground;
|
||||
border: 1px solid color-mix(in oklab, var(--primary) 16%, transparent);
|
||||
background: var(--primary);
|
||||
box-shadow: var(--shadow-2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
scroll-behavior: auto !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,22 @@
|
|||
import type { Metadata } from "next";
|
||||
import { IBM_Plex_Mono, Inter_Tight } from "next/font/google";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import "./globals.css";
|
||||
|
||||
const fontBody = Inter_Tight({
|
||||
subsets: ["latin"],
|
||||
variable: "--font-inter-tight",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
const fontMono = IBM_Plex_Mono({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500"],
|
||||
variable: "--font-plex-mono",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Inspannings Monitor",
|
||||
description:
|
||||
|
|
@ -14,10 +29,21 @@ export default function RootLayout({
|
|||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="nl">
|
||||
<body className="min-h-screen">
|
||||
{children}
|
||||
<Toaster position="top-right" richColors closeButton />
|
||||
<html
|
||||
lang="nl"
|
||||
suppressHydrationWarning
|
||||
className={`${fontBody.variable} ${fontMono.variable}`}
|
||||
>
|
||||
<body className="min-h-screen antialiased">
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="dark"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
{children}
|
||||
<Toaster position="top-right" richColors closeButton />
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default async function LoginPage({ searchParams }: LoginPageProps) {
|
|||
footer={
|
||||
<p>
|
||||
Nog geen account?{" "}
|
||||
<Link href={signUpHref} className="font-semibold text-emerald-900">
|
||||
<Link href={signUpHref} className="font-semibold text-primary">
|
||||
Maak er een aan
|
||||
</Link>
|
||||
</p>
|
||||
|
|
@ -50,7 +50,7 @@ export default async function LoginPage({ searchParams }: LoginPageProps) {
|
|||
<StatusToastBridge toast={statusToast} paramKeys={["error", "status"]} />
|
||||
|
||||
{!authState.isConfigured ? (
|
||||
<Alert className="rounded-[1.5rem] border-sky-200 bg-sky-50 text-sky-950 [&_svg]:text-sky-700">
|
||||
<Alert variant="info">
|
||||
<AlertDescription className="leading-7 text-current">
|
||||
Voeg eerst je Supabase-gegevens toe in `.env.local` op basis van `.env.example`.
|
||||
</AlertDescription>
|
||||
|
|
@ -60,7 +60,7 @@ export default async function LoginPage({ searchParams }: LoginPageProps) {
|
|||
<input type="hidden" name="next" value={next} />
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email" className="text-slate-800">
|
||||
<Label htmlFor="email" className="text-foreground">
|
||||
E-mailadres
|
||||
</Label>
|
||||
<Input
|
||||
|
|
@ -74,7 +74,7 @@ export default async function LoginPage({ searchParams }: LoginPageProps) {
|
|||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password" className="text-slate-800">
|
||||
<Label htmlFor="password" className="text-foreground">
|
||||
Wachtwoord
|
||||
</Label>
|
||||
<Input
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default async function OnboardingPage({ searchParams }: OnboardingPagePro
|
|||
);
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(167,201,87,0.22),_transparent_32%),linear-gradient(180deg,_#f5f4ee_0%,_#eef2e6_100%)] px-6 py-10 text-slate-900 sm:px-8">
|
||||
<main className="app-page">
|
||||
<div className="mx-auto flex max-w-6xl flex-col gap-8">
|
||||
<StatusToastBridge toast={statusToast} paramKeys={["error", "status"]} />
|
||||
<OnboardingFlow profileBundle={profileBundle} />
|
||||
|
|
|
|||
29
app/page.tsx
29
app/page.tsx
|
|
@ -51,11 +51,11 @@ export default async function Home({ searchParams }: HomePageProps) {
|
|||
);
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(167,201,87,0.22),_transparent_32%),linear-gradient(180deg,_#f5f4ee_0%,_#eef2e6_100%)] text-slate-900">
|
||||
<main className="app-page">
|
||||
<div className="mx-auto flex min-h-screen w-full max-w-6xl flex-col px-6 py-10 sm:px-8 lg:px-10">
|
||||
<header className="mb-10 flex items-center justify-between border-b border-black/10 pb-5">
|
||||
<header className="mb-10 flex items-center justify-between border-b border-border/70 pb-5">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-slate-600">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Inspannings Monitor
|
||||
</p>
|
||||
<h1 className="font-[family-name:var(--font-display)] text-3xl leading-tight sm:text-5xl">
|
||||
|
|
@ -104,7 +104,7 @@ export default async function Home({ searchParams }: HomePageProps) {
|
|||
</>
|
||||
)
|
||||
) : (
|
||||
<span className="rounded-full border border-amber-900/15 bg-amber-50 px-4 py-2 text-sm font-medium text-amber-900 shadow-sm">
|
||||
<span className="rounded-full border border-warning/30 bg-warning/14 px-4 py-2 text-sm font-medium text-foreground shadow-[var(--shadow-1)]">
|
||||
Supabase nog niet geconfigureerd
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -114,9 +114,9 @@ export default async function Home({ searchParams }: HomePageProps) {
|
|||
<StatusToastBridge toast={statusToast} paramKeys={["error", "status"]} />
|
||||
|
||||
<section className="grid gap-6 lg:grid-cols-[1.35fr_0.95fr]">
|
||||
<Card className="rounded-[2rem] border border-border/60 bg-card/90 py-0 shadow-[0_18px_60px_rgba(71,85,105,0.12)] backdrop-blur">
|
||||
<Card elevation="raised" className="rounded-[var(--radius-4xl)] py-0 backdrop-blur">
|
||||
<CardContent className="p-6 sm:p-8">
|
||||
<p className="mb-4 max-w-2xl text-lg leading-8 text-slate-700">
|
||||
<p className="mb-4 max-w-2xl text-lg leading-8 text-muted-foreground">
|
||||
De projectbasis staat nu, inclusief de eerste auth-laag via Supabase.
|
||||
Release 1 blijft bewust smal: publieke landing, aparte login/signup
|
||||
routes en een eerste protected dashboard als basis voor de volgende stories.
|
||||
|
|
@ -125,7 +125,8 @@ export default async function Home({ searchParams }: HomePageProps) {
|
|||
{loopSteps.map((step, index) => (
|
||||
<Card
|
||||
key={step.title}
|
||||
className="rounded-[1.5rem] border border-border/50 bg-background/80 py-0 shadow-none"
|
||||
tone="subtle"
|
||||
className="rounded-[var(--radius-2xl)] py-0 shadow-none"
|
||||
>
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.22em] text-muted-foreground">
|
||||
|
|
@ -146,7 +147,7 @@ export default async function Home({ searchParams }: HomePageProps) {
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="rounded-[2rem] border border-primary/10 bg-primary py-0 text-primary-foreground shadow-[0_18px_60px_rgba(22,58,43,0.18)]">
|
||||
<Card tone="primary" elevation="raised" className="rounded-[var(--radius-4xl)] py-0">
|
||||
<CardHeader className="px-6 pt-7 sm:px-8">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-primary-foreground/75">
|
||||
Release 1 focus
|
||||
|
|
@ -156,7 +157,7 @@ export default async function Home({ searchParams }: HomePageProps) {
|
|||
{releaseFocus.map((item) => (
|
||||
<Card
|
||||
key={item}
|
||||
className="rounded-[1.5rem] border border-white/10 bg-white/8 py-0 text-primary-foreground shadow-none"
|
||||
className="rounded-[var(--radius-2xl)] border-white/10 bg-white/8 py-0 text-primary-foreground shadow-none"
|
||||
>
|
||||
<CardContent className="px-4 py-3 text-sm leading-7">{item}</CardContent>
|
||||
</Card>
|
||||
|
|
@ -174,31 +175,31 @@ export default async function Home({ searchParams }: HomePageProps) {
|
|||
</Card>
|
||||
</section>
|
||||
|
||||
<Card className="mt-8 rounded-[2rem] border border-border/60 bg-card/80 py-0 shadow-[0_10px_45px_rgba(71,85,105,0.08)] backdrop-blur">
|
||||
<Card tone="subtle" className="mt-8 rounded-[var(--radius-4xl)] py-0 backdrop-blur">
|
||||
<CardContent className="grid gap-5 p-6 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Volgende story
|
||||
</p>
|
||||
<p className="mt-2 font-semibold text-slate-900">ST-201 Ochtendcheck-in</p>
|
||||
<p className="mt-2 font-semibold text-foreground">ST-201 Ochtendcheck-in</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Doelgroep
|
||||
</p>
|
||||
<p className="mt-2 font-semibold text-slate-900">Volwassen individuele gebruikers</p>
|
||||
<p className="mt-2 font-semibold text-foreground">Volwassen individuele gebruikers</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Positionering
|
||||
</p>
|
||||
<p className="mt-2 font-semibold text-slate-900">Wellness / self-management</p>
|
||||
<p className="mt-2 font-semibold text-foreground">Wellness / self-management</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Status
|
||||
</p>
|
||||
<p className="mt-2 font-semibold text-slate-900">Auth, onboarding en settings actief</p>
|
||||
<p className="mt-2 font-semibold text-foreground">Auth, onboarding en settings actief</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -70,14 +70,14 @@ export default async function PlanningPage({ searchParams }: PlanningPageProps)
|
|||
);
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(167,201,87,0.22),_transparent_32%),linear-gradient(180deg,_#f5f4ee_0%,_#eef2e6_100%)] px-6 py-10 text-slate-900 sm:px-8">
|
||||
<main className="app-page">
|
||||
<div className="mx-auto flex max-w-6xl flex-col gap-8">
|
||||
<StatusToastBridge toast={statusToast} paramKeys={["error", "status"]} />
|
||||
|
||||
<header className="flex flex-col gap-5 rounded-[2rem] border border-black/10 bg-white/75 p-6 shadow-[0_18px_60px_rgba(71,85,105,0.12)] backdrop-blur sm:flex-row sm:items-start sm:justify-between sm:p-8">
|
||||
<header className="app-page-header">
|
||||
<div>
|
||||
<div className="flex flex-wrap items-center gap-3 text-xs font-semibold uppercase tracking-[0.24em] text-slate-500">
|
||||
<Link href="/dashboard" className="transition hover:text-slate-900">
|
||||
<div className="app-page-breadcrumb">
|
||||
<Link href="/dashboard" className="app-page-link">
|
||||
Dashboard
|
||||
</Link>
|
||||
<span>/</span>
|
||||
|
|
@ -86,7 +86,7 @@ export default async function PlanningPage({ searchParams }: PlanningPageProps)
|
|||
<h1 className="mt-3 font-[family-name:var(--font-display)] text-4xl leading-tight">
|
||||
Plan vandaag bewust klein
|
||||
</h1>
|
||||
<p className="mt-4 max-w-2xl text-base leading-8 text-slate-700">
|
||||
<p className="app-page-copy">
|
||||
Voeg alleen activiteiten toe die vandaag echt relevant zijn. Houd de lijst licht,
|
||||
zodat je later goed kunt bijsturen zonder druk op te bouwen.
|
||||
</p>
|
||||
|
|
@ -118,12 +118,12 @@ export default async function PlanningPage({ searchParams }: PlanningPageProps)
|
|||
/>
|
||||
|
||||
<aside className="space-y-5">
|
||||
<Card className="rounded-[1.75rem] border border-border/60 bg-card/90 py-0 shadow-[0_12px_40px_rgba(71,85,105,0.08)]">
|
||||
<Card className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Vandaag
|
||||
</p>
|
||||
<CardTitle className="text-lg text-slate-900">
|
||||
<CardTitle className="text-lg text-foreground">
|
||||
{planningPageData.activities.length === 0
|
||||
? "Start met een eerste activiteit"
|
||||
: `${planningPageData.activities.length} activiteiten ingepland`}
|
||||
|
|
@ -149,7 +149,7 @@ export default async function PlanningPage({ searchParams }: PlanningPageProps)
|
|||
|
||||
<EnergyMeterCard meter={planningMeter} />
|
||||
|
||||
<Card className="rounded-[1.75rem] border border-primary/15 bg-primary py-0 text-primary-foreground shadow-[0_12px_40px_rgba(22,58,43,0.18)]">
|
||||
<Card tone="primary" elevation="raised" className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-primary-foreground/75">
|
||||
Bewuste grens
|
||||
|
|
|
|||
|
|
@ -57,14 +57,14 @@ export default async function SettingsPage({ searchParams }: SettingsPageProps)
|
|||
"Ingelogde gebruiker";
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(167,201,87,0.22),_transparent_32%),linear-gradient(180deg,_#f5f4ee_0%,_#eef2e6_100%)] px-6 py-10 text-slate-900 sm:px-8">
|
||||
<main className="app-page">
|
||||
<div className="mx-auto flex max-w-6xl flex-col gap-8">
|
||||
<StatusToastBridge toast={statusToast} paramKeys={["error", "status"]} />
|
||||
|
||||
<header className="flex flex-col gap-5 rounded-[2rem] border border-black/10 bg-white/75 p-6 shadow-[0_18px_60px_rgba(71,85,105,0.12)] backdrop-blur sm:flex-row sm:items-start sm:justify-between sm:p-8">
|
||||
<header className="app-page-header">
|
||||
<div>
|
||||
<div className="flex flex-wrap items-center gap-3 text-xs font-semibold uppercase tracking-[0.24em] text-slate-500">
|
||||
<Link href="/dashboard" className="transition hover:text-slate-900">
|
||||
<div className="app-page-breadcrumb">
|
||||
<Link href="/dashboard" className="app-page-link">
|
||||
Dashboard
|
||||
</Link>
|
||||
<span>/</span>
|
||||
|
|
@ -73,7 +73,7 @@ export default async function SettingsPage({ searchParams }: SettingsPageProps)
|
|||
<h1 className="mt-3 font-[family-name:var(--font-display)] text-4xl leading-tight">
|
||||
Instellingen
|
||||
</h1>
|
||||
<p className="mt-4 max-w-2xl text-base leading-8 text-slate-700">
|
||||
<p className="app-page-copy">
|
||||
Pas je basisvoorkeuren rustig aan. Alles blijft beperkt tot jouw eigen
|
||||
account en de wellness-first scope van release 1.
|
||||
</p>
|
||||
|
|
@ -101,12 +101,12 @@ export default async function SettingsPage({ searchParams }: SettingsPageProps)
|
|||
<SettingsForm profileBundle={profileBundle} />
|
||||
|
||||
<aside className="space-y-5">
|
||||
<Card className="rounded-[1.75rem] border border-border/60 bg-card/90 py-0 shadow-[0_12px_40px_rgba(71,85,105,0.08)]">
|
||||
<Card className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Account
|
||||
</p>
|
||||
<CardTitle className="text-lg text-slate-900">{profileTitle}</CardTitle>
|
||||
<CardTitle className="text-lg text-foreground">{profileTitle}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pb-6">
|
||||
<CardDescription className="text-sm leading-7 text-muted-foreground">
|
||||
|
|
@ -115,12 +115,12 @@ export default async function SettingsPage({ searchParams }: SettingsPageProps)
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="rounded-[1.75rem] border border-border/60 bg-card/90 py-0 shadow-[0_12px_40px_rgba(71,85,105,0.08)]">
|
||||
<Card className="py-0">
|
||||
<CardHeader className="pb-0">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
||||
Huidige status
|
||||
</p>
|
||||
<CardTitle className="text-lg text-slate-900">
|
||||
<CardTitle className="text-lg text-foreground">
|
||||
Onboarding {profileBundle.profile.onboardingCompleted ? "afgerond" : "later afronden"}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default async function SignUpPage({ searchParams }: SignUpPageProps) {
|
|||
footer={
|
||||
<p>
|
||||
Heb je al een account?{" "}
|
||||
<Link href={loginHref} className="font-semibold text-emerald-900">
|
||||
<Link href={loginHref} className="font-semibold text-primary">
|
||||
Log dan in
|
||||
</Link>
|
||||
</p>
|
||||
|
|
@ -50,7 +50,7 @@ export default async function SignUpPage({ searchParams }: SignUpPageProps) {
|
|||
<StatusToastBridge toast={statusToast} paramKeys={["error", "status"]} />
|
||||
|
||||
{!authState.isConfigured ? (
|
||||
<Alert className="rounded-[1.5rem] border-sky-200 bg-sky-50 text-sky-950 [&_svg]:text-sky-700">
|
||||
<Alert variant="info">
|
||||
<AlertDescription className="leading-7 text-current">
|
||||
Voeg eerst je Supabase-gegevens toe in `.env.local` op basis van `.env.example`.
|
||||
</AlertDescription>
|
||||
|
|
@ -60,7 +60,7 @@ export default async function SignUpPage({ searchParams }: SignUpPageProps) {
|
|||
<input type="hidden" name="next" value={next} />
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email" className="text-slate-800">
|
||||
<Label htmlFor="email" className="text-foreground">
|
||||
E-mailadres
|
||||
</Label>
|
||||
<Input
|
||||
|
|
@ -74,7 +74,7 @@ export default async function SignUpPage({ searchParams }: SignUpPageProps) {
|
|||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password" className="text-slate-800">
|
||||
<Label htmlFor="password" className="text-foreground">
|
||||
Wachtwoord
|
||||
</Label>
|
||||
<Input
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export default async function WizardTestPage() {
|
|||
}
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(167,201,87,0.22),_transparent_32%),linear-gradient(180deg,_#f5f4ee_0%,_#eef2e6_100%)] px-6 py-10 text-slate-900 sm:px-8">
|
||||
<main className="app-page">
|
||||
<div className="mx-auto flex max-w-6xl flex-col gap-8">
|
||||
<TestWizardFlow />
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue