diff --git a/app/auth/callback/route.ts b/app/auth/callback/route.ts index c4a5471..b929c9b 100644 --- a/app/auth/callback/route.ts +++ b/app/auth/callback/route.ts @@ -12,25 +12,39 @@ import type { NextRequest } from 'next/server' export async function GET(request: NextRequest) { const requestUrl = new URL(request.url) const code = requestUrl.searchParams.get('code') + const token = requestUrl.searchParams.get('token') // Fallback for older templates + const token_hash = requestUrl.searchParams.get('token_hash') // Supabase uses this for email links const type = requestUrl.searchParams.get('type') // recovery, signup, etc const next = requestUrl.searchParams.get('next') ?? '/epd/clients' - // Debug logging - if (process.env.NODE_ENV === 'development') { - console.log('πŸ” Auth Callback Debug:', { - url: request.url, - code: !!code, - type, - next, - allParams: Object.fromEntries(requestUrl.searchParams) - }) - } + // Debug logging - ALWAYS log to diagnose redirect issues + console.log('πŸ” Auth Callback Debug:', { + url: request.url, + code: !!code, + token: !!token, + token_hash: !!token_hash, + codeValue: code?.substring(0, 10) + '...', + type, + next, + allParams: Object.fromEntries(requestUrl.searchParams) + }) - if (code) { + // Use code, token, or token_hash (different Supabase versions use different params) + const authCode = code || token || token_hash + + if (authCode) { const supabase = await createClient() // Exchange code for session - const { data, error } = await supabase.auth.exchangeCodeForSession(code) + const { data, error } = await supabase.auth.exchangeCodeForSession(authCode) + + if (error) { + console.error('❌ Auth Callback Exchange Error:', { + message: error.message, + status: error.status, + code: error.code + }) + } if (!error && data.user) { // PRIORITY: If next is /update-password, ALWAYS go there (password reset flow) diff --git a/docs/emails/reset-password.html b/docs/emails/reset-password.html index d8c736e..0700a4e 100644 --- a/docs/emails/reset-password.html +++ b/docs/emails/reset-password.html @@ -42,19 +42,19 @@
- + Wachtwoord opnieuw instellen
- +

Werkt de knop niet? Kopieer en plak deze link in je browser:

- {{ .ConfirmationURL }} + {{ .SiteURL }}/auth/callback?token_hash={{ .TokenHash }}&type=recovery&next=/update-password

diff --git a/docs/specs/fo-live-transcriptie-v1.md b/docs/specs/fo-live-transcriptie-v1.md new file mode 100644 index 0000000..46d158d --- /dev/null +++ b/docs/specs/fo-live-transcriptie-v1.md @@ -0,0 +1,681 @@ +# 🧩 Functioneel Ontwerp (FO) β€” Live Transcriptie & AI Verslag + +**Projectnaam:** Mini-ECD - Live Transcriptie Feature +**Versie:** v1.0 +**Datum:** 19-11-2024 +**Auteur:** Colin van der Heijden (AI Speedrun) + +--- + +## 1. Doel en relatie met het PRD +🎯 **Doel van dit document:** +Het Functioneel Ontwerp beschrijft **hoe** de live transcriptie en AI-verslag functionaliteit werkt binnen het Mini-ECD systeem. Dit document vertaalt de behoefte uit het PRD ("behandelaars willen minder tijd kwijt zijn aan typen") naar concrete gebruikerservaringen en schermflows. + +πŸ“˜ **Toelichting aan de lezer:** +Dit FO beschrijft twee nauw verbonden features: +1. **Live Transcriptie** - Real-time spraak-naar-tekst tijdens gesprekken (Deepgram) +2. **AI Verslag Structurering** - Transformatie van ruwe transcriptie naar gestructureerd verslag (Claude) + +Deze features zijn dΓ© showcase van "Software on Demand" - waar traditionele EPD's 30 minuten handmatig typen vereisen, doen wij dit in 2 minuten AI-tijd. + +**Relatie met PRD v1.2:** +- User Story US-02: "Intakeverslag schrijven met AI-ondersteuning" +- Epic E4: "AI Integration" (Week 3) +- Succes criterium: "<5s AI response time" + +--- + +## 2. Overzicht van de belangrijkste onderdelen +🎯 **Doel:** Inzicht in de nieuwe componenten binnen de bestaande EPD-structuur. + +**Nieuwe componenten:** +1. **Live Opname Interface** - Microfoon controles, timer, real-time feedback +2. **Transcriptie Editor** - TipTap editor met live text streaming +3. **AI Verslag Generator** - Claude-powered structurering +4. **Audio Management** - Opslaan/deleten opnames (privacy) + +**Bestaande componenten (aangepast):** +- Intake Editor - uitgebreid met opname-functionaliteit +- AI Rail - nieuwe actie "Structureer Verslag" +- Client Dashboard - toon opname-status + +--- + +## 3. User Stories + +🎯 **Doel:** Beschrijven wat gebruikers moeten kunnen doen vanuit hun perspectief. + +| ID | Rol | Doel / Actie | Verwachte waarde | Prioriteit | +|----|------|---------------|------------------|-------------| +| US-10 | Behandelaar | Live opname starten tijdens gesprek | Handen vrij, focus op cliΓ«nt | Hoog | +| US-11 | Behandelaar | Real-time zien wat er getranscribeerd wordt | Vertrouwen dat het werkt | Hoog | +| US-12 | Behandelaar | Opname pauzeren (telefoon, onderbreking) | Geen irrelevante tekst in verslag | Middel | +| US-13 | Behandelaar | Transcriptie handmatig corrigeren | Controle over eindresultaat | Hoog | +| US-14 | Behandelaar | AI laten structureren tot verslag | Geen handmatig herstructureren | Hoog | +| US-15 | Behandelaar | Audio bewaren voor verificatie | Terughoren bij onduidelijkheid | Laag | +| US-16 | Demo-bezoeker | Live demo zien werken | Geloven dat het echt werkt | Kritiek | +| US-17 | Privacy Officer | Audio auto-delete na X dagen | AVG compliance | Middel | + +--- + +## 4. Functionele werking per onderdeel + +### 4.1 Live Opname Interface + +**Functionaliteit:** +* Microfoon permissie vragen (browser native) +* Audio stream naar Deepgram websocket +* Real-time transcriptie ontvangen +* Visual feedback (recording indicator, timer) + +**Schermopbouw:** +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Intake: Lisa de Jong [🎀 OPNAME 00:12:34] β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ πŸ”΄ Aan het opnemen... β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ Laatst: "...moeilijk weer inslapen" β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ [TipTap Editor - Real-time transcriptie] β”‚ +β”‚ β”‚ +β”‚ Client geeft aan dat ze al 3 maanden last heeft van β”‚ +β”‚ slaapproblemen. Ze wordt 's nachts wakker en kan β”‚ +β”‚ moeilijk weer inslapen...β–ˆ β”‚ +β”‚ β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ [⏸️ Pauzeer] [⏹️ Stop & Bewaar] [πŸ—‘οΈ Annuleer] β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +**States:** +- **Initieel**: Knop "🎀 Start Opname" boven editor +- **Permission gevraagd**: Modal "Geef toegang tot microfoon" +- **Recording**: Rode indicator, timer loopt, tekst verschijnt +- **Paused**: Gele indicator, timer gestopt, "Hervatten" knop +- **Stopped**: Transcriptie volledig, "Bewerken" of "AI Structureren" + +**Interacties:** +1. **Start Opname** + - Klik 🎀 Start Opname + - Browser vraagt mic permission + - Websocket verbinding naar Deepgram + - Timer start (00:00:00) + - Rode πŸ”΄ indicator verschijnt + +2. **Tijdens Opname** + - Gebruiker praat + - Deepgram stuurt tekst chunks terug + - TipTap editor append tekst real-time + - Laatste paar woorden highlighted (fade effect) + - Handmatig typen/corrigeren is mogelijk + +3. **Pauzeren** + - Klik ⏸️ Pauzeer + - Websocket blijft open maar stuurt geen audio + - Timer stopt + - Indicator wordt geel 🟑 + - Klik opnieuw β†’ Hervatten + +4. **Stoppen** + - Klik ⏹️ Stop & Bewaar + - Websocket sluit + - Final transcriptie in editor + - Audio lokaal opgeslagen (optioneel) + - Overschakeling naar "Bewerk Modus" + +5. **Annuleren** + - Klik πŸ—‘οΈ Annuleer + - Confirmation: "Weet je het zeker?" + - Bij ja: transcriptie verwijderen + - Bij nee: doorgaan met opname + +**Error Scenarios:** +- **Geen mic permission**: "Geef toegang tot microfoon om op te nemen" +- **Internet weg**: "Verbinding verbroken. Opname gepauzeerd." +- **Deepgram quota**: "Opname limiet bereikt. Schakel over naar typen?" +- **Browser niet ondersteund**: "Je browser ondersteunt geen opname" + +--- + +### 4.2 Transcriptie Editor + +**Functionaliteit:** +* TipTap editor met live text streaming +* Highlight recent toegevoegde tekst +* Manual editing tijdens/na opname +* Auto-save elke 30 seconden +* Undo/redo werkt met live input + +**Schermopbouw:** +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ [TipTap Toolbar: B I U β€’ 1. " ] β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ Hoofdklacht: β”‚ +β”‚ Client geeft aan dat ze al 3 maanden last heeft van β”‚ +β”‚ slaapproblemen. Ze wordt gemiddeld 3-4 keer per nacht β”‚ +β”‚ wakker en kan dan moeilijk weer inslapen. β”‚ +β”‚ β”‚ +β”‚ Context: β”‚ +β”‚ De klachten zijn begonnen na een stressvolle periode β”‚ +β”‚ op het werk. [LIVE: Client vertelt dat...]β–ˆ β”‚ +β”‚ β”‚ +β”‚ [Laatste update: zojuist β€’ Auto-save actief] β”‚ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +**Features:** +- **Live highlighting**: Laatste 20 woorden in lichtblauw, fade naar normaal +- **Typing indicator**: β–ˆ cursor pulseert tijdens transcriptie +- **Manual override**: Behandelaar kan tijdens opname typen/corrigeren +- **Smart paragraphs**: Deepgram smart formatting maakt alinea's +- **Timestamps** (optioneel): [00:12:34] markers voor navigatie + +**States:** +- **Live transcriptie**: Tekst append + highlight +- **Paused**: Editor blijft bewerkbaar +- **Stopped**: Normale editor modus +- **Saving**: "Opslaan..." indicator +- **Saved**: "βœ“ Opgeslagen om 14:32" + +--- + +### 4.3 AI Verslag Generator + +**Functionaliteit:** +* Claude analyseert ruwe transcriptie +* Structureert in standaard verslag format +* Behoudt feitelijke informatie +* Voegt professionele tone toe + +**Workflow:** +``` +[Ruwe Transcriptie] + ↓ +[Klik "✨ AI Structureer Verslag"] + ↓ +[Claude verwerking 5-10s] + ↓ +[Preview in AI Rail] + ↓ +[Accepteren β†’ Vervangt origineel] +[OF] +[Aanpassen β†’ Handmatig bewerken] +``` + +**AI Rail Layout:** +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ ✨ AI VERSLAG β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ HOOFDKLACHT β”‚ +β”‚ CliΓ«nt presenteert zich met β”‚ +β”‚ slaapproblemen sinds 3 maanden. β”‚ +β”‚ Nachtelijk ontwaken 3-4x, moeilijk β”‚ +β”‚ hervatten slaap. β”‚ +β”‚ β”‚ +β”‚ ANAMNESE β”‚ +β”‚ β€’ Onset na werkstress β”‚ +β”‚ β€’ Geen eerdere slaapklachten β”‚ +β”‚ β€’ Normale slaaphygiΓ«ne β”‚ +β”‚ β”‚ +β”‚ OBSERVATIES β”‚ +β”‚ β€’ Alert en helder β”‚ +β”‚ β€’ Vermoeidheid zichtbaar β”‚ +β”‚ β”‚ +β”‚ PLAN β”‚ +β”‚ β€’ Vervolgafspraak over 2 weken β”‚ +β”‚ β€’ Slaapdagboek bijhouden β”‚ +β”‚ β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ [βœ“ Accepteer & Vervang] β”‚ +β”‚ [πŸ“‹ Kopieer] β”‚ +β”‚ [✏️ Handmatig Aanpassen] β”‚ +β”‚ [❌ Annuleer] β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +**Prompt Strategie (voor Claude):** +``` +Systeem: Je bent een ervaren GGZ-behandelaar die transcripties +omzet naar professionele verslagen. + +Instructies: +- Structureer in: Hoofdklacht, Anamnese, Observaties, Plan +- Behoud alle feitelijke informatie +- Gebruik professionele maar toegankelijke taal (B1) +- Geen interpretaties, alleen feiten +- Max 400 woorden + +Input: [ruwe transcriptie] +Output: [gestructureerd verslag in markdown] +``` + +**States:** +- **Idle**: Knop "✨ Structureer Verslag" beschikbaar +- **Processing**: Spinner + "AI analyseert..." (5-10s) +- **Preview**: Gestructureerd verslag in rail +- **Accepted**: Vervangt editor content +- **Error**: "Kon niet verwerken. Probeer opnieuw." + +**Acties:** +- **Accepteer & Vervang**: Overschrijft originele transcriptie +- **Kopieer**: Naar clipboard (voor elders plakken) +- **Handmatig Aanpassen**: Opent in split-view (links origineel, rechts AI) +- **Annuleer**: Verwerpt AI-versie, behoudt origineel + +--- + +### 4.4 Audio Management + +**Functionaliteit:** +* Originele audio opslaan (optioneel) +* Replay functionaliteit +* Privacy-compliant auto-delete +* Storage in Supabase + +**UI Elementen:** +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ AUDIO OPNAME β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ πŸ“ opname-2024-11-19-14-32.webm β”‚ +β”‚ Duur: 12:34 β€’ 5.2 MB β”‚ +β”‚ β”‚ +β”‚ [▢️ Afspelen] [πŸ“₯ Download] β”‚ +β”‚ β”‚ +β”‚ ⚠️ Privacy: β”‚ +β”‚ [ ] Audio bewaren voor verificatie β”‚ +β”‚ [ ] Auto-delete na 7 dagen β”‚ +β”‚ β”‚ +β”‚ [πŸ’Ύ Opslaan] [πŸ—‘οΈ Verwijderen] β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +**Privacy Flow:** +1. **Tijdens opname**: Audio lokaal in browser +2. **Streaming**: Naar Deepgram (niet bewaard door hen) +3. **Na stop**: + - Optie 1: Direct deleten (default) + - Optie 2: Uploaden naar Supabase Storage +4. **Auto-delete**: Cron job verwijdert na X dagen + +**Database Schema:** +```typescript +interface AudioRecording { + id: uuid + intake_note_id: uuid // FK naar intake_notes + storage_path: string // Supabase Storage path + duration_seconds: number + file_size_bytes: number + created_at: timestamp + expires_at: timestamp // Auto-delete datum + deleted: boolean +} +``` + +--- + +## 5. UI-overzicht (visuele structuur) + +### 5.1 Intake met Live Opname (Volledig scherm) + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Mini-ECD Logo | Intake: Lisa de Jong | [🎀 00:12:34] [βš™οΈ] β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ SIDEBAR NAV β”‚ β”‚ EDITOR GEBIED β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ > Overzicht β”‚ β”‚ [πŸ”΄ AAN HET OPNEMEN] β”‚ β”‚ +β”‚ β”‚ β€’ Intakes β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ Profiel β”‚ β”‚ [TipTap Editor met live tekst] β”‚ β”‚ +β”‚ β”‚ Plan β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ Afspraken β”‚ β”‚ Client geeft aan dat ze al... β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ [⏸️ Pauzeer] [⏹️ Stop] [πŸ—‘οΈ Annuleer]β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ [Toast Area: "βœ“ Auto-save om 14:32" ] β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### 5.2 AI Verslag Generatie (Split View) + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Mini-ECD Logo | Intake: Lisa de Jong | [✨ AI Actief] [βš™οΈ]β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ β”‚ EDITOR (Originele Transcriptie) β”‚ AI RAIL (Verslag) β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ Client geeft aan dat ze al 3 β”‚ ✨ GESTRUCTUREERD VERSLAG β”‚ +β”‚ β”‚ maanden last heeft van slaap- β”‚ β”‚ +β”‚ β”‚ problemen. Ze wordt gemiddeld β”‚ HOOFDKLACHT β”‚ +β”‚ β”‚ 3-4 keer per nacht wakker... β”‚ CliΓ«nt presenteert zich... β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ [250+ woorden ruwe tekst] β”‚ ANAMNESE β”‚ +β”‚ β”‚ β”‚ β€’ Onset na werkstress β”‚ +β”‚ β”‚ β”‚ β€’ Geen eerdere klachten β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ OBSERVATIES β”‚ +β”‚ β”‚ β”‚ β€’ Alert en helder β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ PLAN β”‚ +β”‚ β”‚ β”‚ β€’ Vervolgafspraak β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ [βœ“ Accepteer & Vervang] β”‚ +β”‚ β”‚ β”‚ [πŸ“‹ Kopieer] β”‚ +β”‚ β”‚ β”‚ [❌ Annuleer] β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +--- + +## 6. Interacties met AI (functionele beschrijving) + +### 6.1 Deepgram (Speech-to-Text) + +| Aspect | Detail | +|--------|--------| +| **Trigger** | Klik "🎀 Start Opname" | +| **Input** | Live audio stream van microfoon | +| **Verwerking** | Deepgram Nova-2 model, Nederlands, smart formatting | +| **Output** | Real-time text chunks β†’ append in editor | +| **Latency** | <500ms per chunk | +| **Feedback** | Live typing indicator + highlight recent text | +| **Cost** | €0,0043/min = €0,19 per 45-min gesprek | + +**Technische flow:** +``` +Browser Mic β†’ Websocket β†’ Deepgram API + ↓ + Text chunks + ↓ + TipTap Editor +``` + +### 6.2 Claude (Verslag Structurering) + +| Aspect | Detail | +|--------|--------| +| **Trigger** | Klik "✨ Structureer Verslag" | +| **Input** | Ruwe transcriptie (TipTap JSON) | +| **Verwerking** | Claude 3.5 Sonnet met GGZ-verslag prompt | +| **Output** | Markdown gestructureerd verslag (4 secties) | +| **Latency** | 5-10 seconden | +| **Feedback** | Spinner β†’ Preview in AI Rail β†’ Accept/Edit | +| **Cost** | ~2000 tokens = €0,006 per verslag | + +**Prompt template:** +```typescript +const PROMPT = ` +Je bent een ervaren GGZ-behandelaar. Structureer deze +transcriptie in een professioneel verslag. + +STRUCTUUR: +1. HOOFDKLACHT - Waarom komt cliΓ«nt? +2. ANAMNESE - Achtergrond, ontstaan, context +3. OBSERVATIES - Wat viel op tijdens gesprek +4. PLAN - Vervolgstappen + +REGELS: +- Behoud alle feiten +- B1 taalniveau (professioneel maar helder) +- Geen interpretaties +- Max 400 woorden + +TRANSCRIPTIE: +${transcriptText} + +OUTPUT (markdown format): +`; +``` + +--- + +## 7. Gebruikersrollen en rechten + +| Rol | Toegang | Beperkingen | +|-----|---------|-------------| +| **Behandelaar** | Volledige opname + AI features | Alleen eigen cliΓ«nten | +| **Demo User** | Volledige opname + AI features | Alleen demo-data (fictief) | +| **Manager** | Alleen transcripties lezen | Geen opname starten | +| **Auditor** | Toegang tot audio (indien bewaard) | Read-only | + +**Privacy niveau per rol:** +- Behandelaar: Kan audio bewaren/deleten +- Demo: Audio auto-delete na sessie +- Manager: Geen audio toegang +- Auditor: Alleen bij specifieke permissie + +--- + +## 8. Demo Scenario (10 minuten) + +### Scenario: GGZ Professional Demo + +**Setup:** +- Demo account ingelogd +- Client "Demo Persoon" vooraf aangemaakt +- Microfoon getest (backup: pre-recorded audio) + +**Flow:** + +**[0:00 - 1:00] Intro & Context** +> "Traditioneel EPD: 30 minuten typen na gesprek. Wij: 2 minuten AI-tijd. Laten we het live zien." + +**[1:00 - 3:00] Live Opname Demo** +1. Navigeer naar Intakes tab +2. Klik "🎀 Start Opname" +3. Browser vraagt microfoon β†’ Toestaan +4. Begin praten (vooraf script): + ``` + "Client geeft aan dat ze de afgelopen 3 maanden + last heeft van slaapproblemen. Ze wordt gemiddeld + 3 tot 4 keer per nacht wakker en kan dan moeilijk + weer inslapen. De klachten zijn begonnen na een + stressvolle periode op het werk..." + ``` +5. Toon live verschijnende tekst in editor +6. Demonstreer Pauzeer functie (5 sec stilte) +7. Hervatten, nog 30 seconden verder praten +8. Stop opname + +**[3:00 - 5:00] AI Verslag Generatie** +1. Toon ruwe transcriptie (250+ woorden) +2. Klik "✨ Structureer Verslag" +3. Wacht 5 seconden (toon spinner) +4. AI Rail toont gestructureerd verslag: + - Hoofdklacht + - Anamnese (bullets) + - Observaties + - Plan +5. Klik "Accepteer & Vervang" +6. Editor toont nu professioneel verslag + +**[5:00 - 7:00] ROI Pitch** +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ TRADITIONEEL β†’ AI SPEEDRUN β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ 45 min gesprek 45 min gesprek β”‚ +β”‚ 30 min typen 2 min AI check β”‚ +β”‚ = 75 min totaal = 47 min totaal β”‚ +β”‚ β”‚ +β”‚ Kost: €31,25 Kost: €19,58 β”‚ +β”‚ (behandelaar @€25/u) (+€0,19 AI) β”‚ +β”‚ β”‚ +β”‚ BESPARING: 37% tijd, 38% kosten β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +**[7:00 - 9:00] Extra Features Tour** +- Toon audio bewaar-optie (privacy toggle) +- Toon auto-delete na 7 dagen +- Toon handmatig correctie tijdens opname +- Toon pause/resume workflow + +**[9:00 - 10:00] Q&A & Next Steps** +> "Dit is week 2 van de build. Volgende week: behandelplan AI-generatie. Follow op LinkedIn!" + +--- + +## 9. Edge Cases & Error Handling + +| Scenario | Systeem Gedrag | User Feedback | +|----------|---------------|---------------| +| **Geen microfoon** | Disable opname-knop | "Geen microfoon gedetecteerd" | +| **Mic permission denied** | Show instructie | "Geef toegang via browser-instellingen" | +| **Internet valt weg** | Pause + buffer lokaal | "Verbinding verbroken, hervatten?" | +| **Deepgram quota op** | Switch naar manual | "Opname limiet bereikt, typ handmatig" | +| **Browser refresh tijdens opname** | Verlies opname | Warning: "Opname gaat verloren bij refresh" | +| **Claude API timeout** | Retry 1x, dan fail | "Verwerking duurde te lang, probeer opnieuw" | +| **Audio te groot (>25MB)** | Reject upload | "Audio te groot, max 45 minuten" | +| **Geen spraak gedetecteerd** | Timer stopt na 30s stilte | "Geen spraak gedetecteerd, pauzeren?" | + +--- + +## 10. Performance & Kosten + +### 10.1 Performance Targets + +| Metric | Target | Measurement | +|--------|--------|-------------| +| **Deepgram latency** | <500ms per chunk | Websocket timestamp | +| **Editor update** | <100ms per chunk | React render time | +| **Claude verslag** | <10s total | API call duration | +| **Audio upload** | <5s voor 45 min | Supabase Storage speed | +| **UI responsiveness** | 60fps tijdens opname | Chrome DevTools | + +### 10.2 Cost Breakdown + +**Per gesprek (45 minuten):** +``` +Deepgram transcriptie: €0,19 +Claude structurering: €0,006 +Supabase storage: €0,001 (indien bewaard) +────────────────────────────── +Totaal per gesprek: €0,197 + +Traditioneel alternatief: +30 min behandelaar tijd @ €25/uur = €12,50 + +ROI: 6.244% besparing +``` + +**Maandelijks (100 gesprekken):** +``` +Deepgram: €19,00 +Claude: €0,60 +Storage: €0,10 +────────────────── +Totaal: €19,70/maand + +Traditioneel: €1.250/maand (behandelaar-tijd) +Besparing: €1.230/maand (98%) +``` + +**Binnen €50/maand budget?** +βœ… Ja, zelfs met 250 gesprekken/maand = €49,25 + +--- + +## 11. Privacy & AVG Compliance + +### 11.1 Data Flow + +``` +1. Audio opname β†’ Browser (lokaal) +2. Stream β†’ Deepgram (real-time, niet bewaard) +3. Transcriptie β†’ Supabase EU (bewaard) +4. Audio β†’ Optioneel Supabase Storage (bewaard) +5. Auto-delete β†’ Cron job (na X dagen) +``` + +### 11.2 Privacy Measures + +| Aspect | Implementatie | +|--------|---------------| +| **Audio storage** | Opt-in (default: niet bewaren) | +| **Deepgram policy** | Audio niet bewaard, GDPR compliant | +| **Supabase region** | EU (Frankfurt/London) | +| **Encryption** | At-rest + in-transit (TLS) | +| **Auto-delete** | Configureerbaar (7/14/30 dagen) | +| **Access control** | RLS policies (alleen eigen data) | +| **Audit log** | Wie heeft wanneer audio beluisterd | + +### 11.3 Disclaimer (demo) + +> **Demo Privacy Notice:** +> Deze demo gebruikt fictieve data. Voor productie: +> - Audio wordt niet bewaard door Deepgram +> - Opslag in EU-regio servers +> - Automatische verwijdering na [X] dagen +> - Volledige AVG-compliance + +--- + +## 12. Technische Dependencies + +| Component | Library/Service | Version | Kritiek | +|-----------|----------------|---------|---------| +| **Speech-to-Text** | Deepgram Nova-2 | Latest | Ja | +| **Editor** | TipTap (ProseMirror) | v2.x | Ja | +| **AI Structuur** | Claude 3.5 Sonnet | Latest | Ja | +| **Websocket** | Native Browser API | - | Ja | +| **Audio Storage** | Supabase Storage | - | Nee | +| **UI Components** | shadcn/ui | - | Nee | + +**Fallback strategie:** +- Deepgram down β†’ Manual typing +- Claude down β†’ Save transcriptie, later structureren +- Websocket fail β†’ Polling fallback (degraded UX) + +--- + +## 13. Bijlagen & Referenties + +**Gerelateerde documenten:** +- PRD v1.2 (Product Requirements Document) +- TO v1.2 (Technisch Ontwerp) - wordt aangevuld met dit FO +- UX Stylesheet - kleuren voor recording UI +- Bouwplan v1.1 - Epic E4 (Week 3) + +**Externe referenties:** +- [Deepgram Docs](https://developers.deepgram.com/docs/getting-started-with-live-streaming-audio) +- [TipTap Docs](https://tiptap.dev/docs/editor/getting-started) +- [Claude API](https://docs.anthropic.com/claude/reference) +- [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API) + +**Demo assets:** +- Intake script (Nederlands, 3 minuten) +- Pre-recorded backup audio +- ROI calculator spreadsheet +- LinkedIn post templates (Week 2-3) + +--- + +**Versiehistorie:** + +| Versie | Datum | Auteur | Wijziging | +|--------|-------|--------|-----------| +| v1.0 | 19-11-2024 | Colin | InitiΓ«le versie - Live Transcriptie & AI Verslag | + +--- + +**Einde Functioneel Ontwerp - Live Transcriptie Feature** diff --git a/lib/auth/client.ts b/lib/auth/client.ts index 7933467..0091edf 100644 --- a/lib/auth/client.ts +++ b/lib/auth/client.ts @@ -128,10 +128,12 @@ export async function signUpWithPassword(email: string, password: string) { */ export async function resetPasswordForEmail(email: string) { const supabase = createClient() - // Redirect to callback route - Supabase will automatically add type=recovery parameter - // The callback route will detect type=recovery and redirect to /update-password + // Force Supabase to bounce back via the auth callback with next=/update-password + const params = new URLSearchParams({ next: '/update-password' }) + const redirectTo = `${window.location.origin}/auth/callback?${params.toString()}` + const { error } = await supabase.auth.resetPasswordForEmail(email, { - redirectTo: `${window.location.origin}/auth/callback` + redirectTo }) if (error) throw error