Add nav avatar, product icon, spec page and about page updates

- Show profile avatar and formatted name in top nav
- Add product icon to left of brand name in top nav
- Add blue border to ProfileAvatar, add xs size
- Add protected /specificatie page with 5 chapter summaries
- Replace planning button with CV link on about page
- Add Specificatie button on about page
- Show app version + git SHA on about page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-04-20 03:00:43 +02:00
parent f9f04a9f2d
commit 682305267a
9 changed files with 209 additions and 32 deletions

View file

@ -64,12 +64,22 @@ export default async function Home({ searchParams }: HomePageProps) {
title="Over de maker en de app"
description="Inspannings Monitor is een rustige wellness-first webapp voor volwassenen die hun energie willen plannen, uitvoeren en evalueren zonder medische claims of overmatige frictie."
aside={
<Link
href="/planning"
className="inline-flex items-center rounded-full border border-border/80 bg-card/84 px-4 py-2 text-sm font-medium text-foreground shadow-[var(--shadow-1)] transition-colors hover:bg-secondary"
>
Bekijk planning
</Link>
<div className="flex flex-wrap gap-2">
<Link
href="https://jp-visser.nl"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center rounded-full border border-border/80 bg-card/84 px-4 py-2 text-sm font-medium text-foreground shadow-[var(--shadow-1)] transition-colors hover:bg-secondary"
>
Curriculum vitae
</Link>
<Link
href="/specificatie"
className="inline-flex items-center rounded-full border border-border/80 bg-card/84 px-4 py-2 text-sm font-medium text-foreground shadow-[var(--shadow-1)] transition-colors hover:bg-secondary"
>
Specificatie
</Link>
</div>
}
/>

138
app/specificatie/page.tsx Normal file
View file

@ -0,0 +1,138 @@
import { redirect } from "next/navigation";
import { getAuthState } from "@/lib/auth/session";
import { AppShell } from "@/components/navigation/app-shell";
import { PageIntro } from "@/components/navigation/page-intro";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
export const dynamic = "force-dynamic";
const chapters = [
{
number: "01",
title: "Productkader en positionering",
version: "v06",
summary:
"Legt vast wat Inspannings Monitor wel en niet is. De app positioneert zich expliciet als wellness/self-management tool, zonder medische claims of zorgverlenerrollen. De intended use is energieplanning en dagstructuur voor volwassenen. Non-intended use — zoals diagnostiek, behandeling of klinische besluitvorming — is bewust uitgesloten en wordt bewaakt via claim-guardrails in tekst, UI en onboarding.",
points: [
"Productnaam: Inspannings Monitor",
"Positionering: wellness / self-management",
"Doelgroep: volwassen individuele gebruikers",
"Voertaal release 1: Nederlands (nl-NL)",
"Geen medische claims, geen zorgverlenerrollen",
"Expliciete non-intended use vastgelegd als guardrail",
],
},
{
number: "02",
title: "Functionele specificatie MVP",
version: "v06",
summary:
"Beschrijft de MVP in toetsbare functionele requirements. De kern is een plan-doe-evalueer-dagflow: ochtendcheck-in met energiescore en slaapkwaliteit, activiteitenplanning met een licht energiebudget, en een dagafsluiting. Elke stap is bewust klein gehouden om de cognitieve belasting laag te houden. Release 1 bevat geen AI, geen deelfunctionaliteit en geen medische workflows.",
points: [
"Ochtendcheck-in: energiescore (110) en slaapkwaliteit",
"Dagbudget afgeleid van energiescore",
"Activiteitenplanning met duur, impact en prioriteit",
"Statussen: gepland → uitgevoerd / aangepast / overgeslagen",
"Dashboard met dag-overzicht en energiemeter",
"Geen sharing, AI of medische workflows in de MVP",
],
},
{
number: "03",
title: "Privacy, security en safety baseline",
version: "v02",
summary:
"Bundelt de minimale randvoorwaarden voor privacy, informatiebeveiliging en productveiligheid. Gebruikersdata blijft strikt gescheiden via Row Level Security in Supabase. Authenticatie vereist e-mailverificatie. Gevoelige sleutels worden nooit in de frontend geplaatst. De baseline is ontworpen als minimumset die uitbreidbaar is als het product richting een medisch spoor beweegt.",
points: [
"Row Level Security: gebruikers zien alleen eigen data",
"Supabase Auth met verplichte e-mailverificatie",
"Service-role key alleen server-side, nooit in de frontend",
"HTTPS-only via Vercel, geen gevoelige data in URL-params",
"Geen opslag van medische of klinische gegevens",
"Baseline uitbreidbaar richting NEN 7510 / MDR bij medisch spoor",
],
},
{
number: "04",
title: "Roadmap wellness naar medisch",
version: "v02",
summary:
"Laat zien hoe Inspannings Monitor gecontroleerd kan doorgroeien naar een medisch product, zonder dat dit ongemerkt in de wellness-release binnensluipt. De roadmap onderscheidt drie fasen: verankerde wellness-MVP, optionele zorgverlenerlaag en een apart medisch productspoor. Elke fase kent eigen vereisten voor regelgeving, documentatie en technische architectuur.",
points: [
"Fase 1: wellness MVP — volledig buiten MDR-scope",
"Fase 2: optionele zorgverlenerlaag met expliciete scope-bewaking",
"Fase 3: apart medisch productspoor met eigen regulatoir traject",
"Faseovergangen vereisen bewuste go/no-go beslissing",
"Geen sluipende scope-uitbreiding zonder documentatieverandering",
"Pad opengehouden zonder verplichting om het te bewandelen",
],
},
{
number: "05",
title: "Technische architectuur en implementatie",
version: "v01",
summary:
"Beschrijft de technische implementatielaag van de wellness-first MVP als zelfstandig architectuurdocument. De stack is bewust compact gehouden: Next.js App Router met Server Actions, Supabase voor auth en data, en Vercel voor hosting en CI/CD. De architectuur is opgezet met duidelijke laagscheiding zodat uitbreiding naar een medisch spoor later geïsoleerd kan plaatsvinden.",
points: [
"Stack: Next.js 16 (App Router) + React 19 + TypeScript",
"Database en auth: Supabase (PostgreSQL + Row Level Security)",
"UI: shadcn/ui + Tailwind CSS, Dusk dark-mode thema",
"Hosting en deploys: Vercel met GitHub Actions CI",
"Server Actions voor alle formmutaties, geen aparte API-laag",
"Migraties via supabase/migrations, seeding via npm-script",
],
},
];
export default async function SpecificatiePage() {
const authState = await getAuthState();
if (!authState.isAuthenticated) {
redirect("/login");
}
return (
<AppShell contentClassName="space-y-8">
<PageIntro
eyebrow="Documentatie"
title="Productspecificatie"
description="Samenvatting van de vijf kerndocumenten die de scope, vereisten en architectuur van Inspannings Monitor vastleggen."
/>
<div className="space-y-6">
{chapters.map((chapter) => (
<Card key={chapter.number} elevation="raised" className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Hoofdstuk {chapter.number} · {chapter.version}
</p>
<CardTitle className="text-2xl text-foreground">
{chapter.title}
</CardTitle>
<CardDescription className="max-w-3xl text-sm leading-7 text-muted-foreground">
{chapter.summary}
</CardDescription>
</CardHeader>
<CardContent className="pb-6">
<div className="mt-4 grid gap-2 sm:grid-cols-2">
{chapter.points.map((point) => (
<Card key={point} tone="subtle" className="pb-0 shadow-none">
<CardContent className="px-4 py-3 text-sm leading-6 text-muted-foreground">
{point}
</CardContent>
</Card>
))}
</div>
</CardContent>
</Card>
))}
</div>
</AppShell>
);
}