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