Fase 0: Fundament
Fase 0 legt het fundament: authenticatie, role-based routing, de basisstructuur van beide portalen, taalwissel en de technische onderlaag (monorepo, database, CI/CD). Hier beschrijven we de flows die een gebruiker doorloopt.
1. Registratie flow
Section titled “1. Registratie flow”Route: /register
Wat de gebruiker ziet
Section titled “Wat de gebruiker ziet”Een formulier met drie velden:
| Veld | Type | Validatie |
|---|---|---|
| Naam | text | Verplicht, min. 2 karakters |
| Verplicht, geldig emailadres | ||
| Wachtwoord | password | Verplicht, min. 8 karakters |
Onderaan het formulier:
- Knop “Account aanmaken”
- Link “Al een account? Inloggen” naar
/login
Wat er gebeurt bij bevestigen
Section titled “Wat er gebeurt bij bevestigen”- Client-side validatie: zijn alle velden correct ingevuld? Zo niet, inline foutmeldingen per veld.
- API call naar Supabase Auth via PKCE flow:
supabase.auth.signUp({ email, password }). - Er wordt een rij aangemaakt in de
clientstabel metonboarding_completed = false. - Supabase stuurt een verificatiemail (standaard Supabase flow).
- Na succesvolle registratie: redirect naar
/onboarding.
Foutmeldingen
Section titled “Foutmeldingen”| Situatie | Melding |
|---|---|
| Email al in gebruik | ”Dit emailadres is al geregistreerd. Probeer in te loggen.” |
| Ongeldig emailformaat | ”Voer een geldig emailadres in.” |
| Wachtwoord te kort | ”Wachtwoord moet minimaal 8 karakters zijn.” |
| Server error | ”Er ging iets mis. Probeer het later opnieuw.” |
Daarna
Section titled “Daarna”De gebruiker komt op /onboarding (beschreven in Fase 1a). Zolang onboarding_completed = false is, wordt de gebruiker bij elke login teruggestuurd naar onboarding.
2. Login flow
Section titled “2. Login flow”Route: /login
Wat de gebruiker ziet
Section titled “Wat de gebruiker ziet”Een formulier met twee velden:
| Veld | Type | Validatie |
|---|---|---|
| Verplicht | ||
| Wachtwoord | password | Verplicht |
Daaronder:
- Knop “Inloggen”
- Link “Wachtwoord vergeten?” naar
/forgot-password - Link “Nog geen account? Registreren” naar
/register
Admin login
Section titled “Admin login”Admins en medewerkers loggen in via dezelfde route. Naast email + wachtwoord ondersteunen admins ook magic link login:
- Admin klikt op “Inloggen met magic link”.
- Vult emailadres in.
- Ontvangt een eenmalige inloglink per email.
- Klikt op de link en wordt direct ingelogd.
De magic link verloopt na 1 uur.
Wat er gebeurt bij inloggen
Section titled “Wat er gebeurt bij inloggen”- Client-side validatie op ingevulde velden.
- API call:
supabase.auth.signInWithPassword({ email, password })ofsignInWithOtp({ email })voor magic link. - Supabase retourneert een sessie met JWT.
- De app checkt de rol van de gebruiker (zie sectie 4: Role-based redirect).
Foutmeldingen
Section titled “Foutmeldingen”| Situatie | Melding |
|---|---|
| Verkeerde combinatie | ”Email of wachtwoord is onjuist.” |
| Account niet geverifieerd | ”Bevestig eerst je emailadres via de link in je inbox.” |
| Te veel pogingen | ”Te veel inlogpogingen. Probeer het over een paar minuten.” |
| Server error | ”Er ging iets mis. Probeer het later opnieuw.” |
Foutmeldingen geven nooit prijs of een emailadres wel of niet bestaat.
Daarna
Section titled “Daarna”Redirect op basis van rol, zie sectie 4.
3. Wachtwoord reset flow
Section titled “3. Wachtwoord reset flow”Stap 1: Aanvraag
Section titled “Stap 1: Aanvraag”Route: /forgot-password
Wat de gebruiker ziet:
- Eenvoudig formulier met 1 veld: Email
- Knop “Verstuur resetlink”
- Link “Terug naar inloggen” naar
/login
Wat er gebeurt:
- API call:
supabase.auth.resetPasswordForEmail(email). - Supabase stuurt een email met een resetlink.
- De app toont altijd de melding: “Als dit emailadres bij ons bekend is, ontvang je een resetlink.” Dit voorkomt dat iemand kan testen of een emailadres geregistreerd is.
De resetlink verloopt na 1 uur.
Stap 2: Nieuw wachtwoord instellen
Section titled “Stap 2: Nieuw wachtwoord instellen”Route: /reset-password
De gebruiker komt hier via de link uit de email. Supabase voegt een token toe aan de URL.
Wat de gebruiker ziet:
- Veld “Nieuw wachtwoord” (min. 8 karakters)
- Veld “Bevestig wachtwoord”
- Knop “Wachtwoord opslaan”
Wat er gebeurt:
- De app leest het token uit de URL.
- Bij bevestigen:
supabase.auth.updateUser({ password }). - Na succes: redirect naar
/loginmet een succesmelding “Wachtwoord gewijzigd. Je kunt nu inloggen.”
Foutmeldingen:
| Situatie | Melding |
|---|---|
| Wachtwoorden komen niet overeen | ”Wachtwoorden komen niet overeen.” |
| Wachtwoord te kort | ”Wachtwoord moet minimaal 8 karakters zijn.” |
| Token verlopen | ”Deze resetlink is verlopen. Vraag een nieuwe aan.” |
| Ongeldig token | ”Ongeldige link. Vraag een nieuwe resetlink aan.” |
4. Role-based redirect na login
Section titled “4. Role-based redirect na login”Na een succesvolle login bepaalt de app waar de gebruiker naartoe gaat. Dit werkt op basis van het role veld in de users tabel.
| Rol | Redirect | Voorwaarde |
|---|---|---|
klant | /onboarding | Als onboarding_completed = false |
klant | /dashboard | Als onboarding voltooid |
admin | /admin | Altijd |
medewerker | /admin | Altijd |
Hoe het werkt
Section titled “Hoe het werkt”- Na login haalt de app het gebruikersprofiel op via
supabase.auth.getUser(). - De rol wordt gecheckt in de
userstabel. - Een auth guard op de router stuurt klanten weg van
/admin/*routes en admins weg van/dashboard/*routes. - Niet-ingelogde gebruikers worden altijd naar
/logingestuurd. - Klanten die de onboarding niet hebben afgerond worden naar
/onboardinggestuurd, ongeacht welke route ze proberen te bezoeken.
Edge cases
Section titled “Edge cases”- Een gebruiker probeert
/adminte bezoeken zonder admin-rol: redirect naar/dashboard(of/onboarding). - Een admin probeert
/dashboardte bezoeken: redirect naar/admin. - Een verlopen sessie: redirect naar
/login, de oorspronkelijke URL wordt bewaard zodat de gebruiker na login terugkomt.
5. Klant navigatie structuur
Section titled “5. Klant navigatie structuur”Na inloggen (en voltooide onboarding) ziet de klant een responsive layout met een zijnavigatie (desktop) of hamburger menu (mobiel).
Navigatie-items
Section titled “Navigatie-items”| Label | Route | Icoon | Omschrijving |
|---|---|---|---|
| Dashboard | /dashboard | Home | Startpagina met overzicht |
| Mijn honden | /dogs | Paw | Hondenprofielen |
| Boekingen | /bookings | Calendar | Boekingenoverzicht |
| Facturen | /invoices | Receipt | Factuuroverzicht |
| Account | /account | User | Persoonlijke instellingen |
Layout
Section titled “Layout”- Header: logo Dog Hotel Aruba, taalwissel (NL/EN), gebruikersnaam met dropdown (account, uitloggen).
- Sidebar (desktop): navigatie-items met iconen en labels.
- Mobiel: hamburger menu dat de sidebar opent als overlay.
- Content area: de pagina-inhoud.
- De actieve route is visueel gemarkeerd in de navigatie.
6. Admin navigatie structuur
Section titled “6. Admin navigatie structuur”Admins en medewerkers komen na login in de admin layout. Dit is een volledig apart deel van de app met eigen navigatie.
Navigatie-items
Section titled “Navigatie-items”| Label | Route | Icoon | Omschrijving |
|---|---|---|---|
| Dashboard | /admin | LayoutDashboard | Dagelijks overzicht |
| Klanten | /admin/clients | Users | Klantbeheer |
| Honden | /admin/dogs | Dog | Hondenoverzicht |
| Boekingen | /admin/bookings | Calendar | Boekingsbeheer |
| Facturen | /admin/invoices | Receipt | Facturatiebeheer |
| Instellingen | /admin/settings | Settings | Systeeminstellingen |
Layout
Section titled “Layout”- Header: logo, taalwissel, naam ingelogde medewerker met dropdown (account, uitloggen).
- Sidebar (desktop): navigatie-items. Bij bredere schermen altijd zichtbaar, bij smallere schermen inklapbaar.
- Mobiel: hamburger menu.
- De actieve route en actieve sectie zijn visueel gemarkeerd.
Verschil met klant layout
Section titled “Verschil met klant layout”- Ander kleurenschema of accent om duidelijk te maken dat je in het admin paneel zit.
- Geen overlap in routes:
/admin/*is strikt gescheiden van klantroutes.
7. Taalwissel
Section titled “7. Taalwissel”De app ondersteunt twee talen in Fase 0: Nederlands (NL) en Engels (EN).
Waar zichtbaar
Section titled “Waar zichtbaar”De taalwissel staat in de header van zowel het klantportaal als het adminpaneel. Het is een simpele toggle of dropdown met de twee taalopties.
Hoe het werkt
Section titled “Hoe het werkt”- De gebruiker klikt op de taalswitch en kiest NL of EN.
- Alle UI-teksten worden direct bijgewerkt via react-i18next. Er is geen pagina-reload nodig.
- De gekozen taal wordt opgeslagen in
localStoragevoor niet-ingelogde gebruikers. - Voor ingelogde klanten wordt de voorkeurstaal ook opgeslagen in het gebruikersprofiel (
preferred_languagein declientstabel), zodat het bij een volgende sessie automatisch goed staat. - Emails worden verstuurd in de voorkeurstaal van de gebruiker.
Standaardtaal
Section titled “Standaardtaal”- Niet ingelogd: de browser-taal wordt gedetecteerd. Als die NL of EN is, wordt die gebruikt. Anders valt het terug op EN.
- Ingelogd: de opgeslagen voorkeurstaal uit het profiel.
Technische basis (geen schermen)
Section titled “Technische basis (geen schermen)”Naast de gebruikersflows bevat Fase 0 ook het technische fundament. Geen schermen, maar wel relevant om te noemen:
Monorepo
Section titled “Monorepo”pnpm monorepo met de volgende structuur:
apps/webmet React, Vite, Tailwind CSS, shadcn/uiapps/apimet Hono op Cloudflare Workersapps/docsmet Starlight (deze documentatie)packages/sharedmet gedeelde TypeScript types en Zod schemas
Database
Section titled “Database”Supabase met de eerste migratie: tabellen clients, dogs, vaccinations, users, services. RLS policies op alle tabellen. De services tabel wordt gevuld met seed data (alle diensten en prijzen).
Hono API met:
- Auth middleware op alle routes (behalve
/healthen/payments/webhook) - CORS beperkt tot
APP_ORIGIN - Zod input validatie op alle endpoints
/healthendpoint zonder auth, retourneert{ status: "ok" }
GitHub Actions workflows voor automatische deploy:
apps/webnaar Cloudflare Pagesapps/apinaar Cloudflare Workersapps/docsnaar Cloudflare Pages- Typecheck en lint als quality gate voor elke PR