Files
triqura-ecd/docs/architecture/component-organization.md
colinislit dce3943963 feat: rapportage UI refactor, speech streaming, docs & seed data
Rapportage:
- Refactor workspace into modular components (quick-actions, timeline-card, timeline-sidebar)
- Add updateReport action for inline editing
- Improve report timeline with better UX

Speech:
- Add Deepgram token API endpoint
- Add use-deepgram-streaming hook
- Add confidence-text component

Docs:
- Add architecture documentation
- Add performance optimization plan
- Add speech specs and seed data docs

Scripts & Data:
- Add seed-reports script and migration
- Update AGENTS.md guidelines

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 14:07:08 +01:00

10 KiB

Component Organisatie Strategie

Overzicht

Dit project gebruikt de colocation pattern voor component organisatie, een best practice in Next.js App Router architectuur.

Twee Component Locaties

1. Centrale Components (/components)

Doel: Herbruikbare, generieke components die door meerdere delen van de app gebruikt worden.

Structuur:

components/
├── ui/                          # Algemene UI componenten (shadcn/ui)
│   ├── button.tsx
│   ├── dialog.tsx
│   ├── dropdown-menu.tsx
│   └── ...
├── speech-recorder-streaming.tsx  # Herbruikbare feature component
├── confidence-text.tsx           # Herbruikbare display component
└── rich-text-editor.tsx         # Herbruikbare editor component

Criteria voor centrale components:

  • Gebruikt in 2+ verschillende features/routes
  • Geen specifieke business logic voor één feature
  • Generiek en configureerbaar via props
  • Zou in een component library kunnen zitten

Voorbeelden:

// ✅ Gebruikt in behandeladvies, rapportage, en andere features
import { SpeechRecorderStreaming } from '@/components/speech-recorder-streaming';

// ✅ Generieke UI component
import { Button } from '@/components/ui/button';

2. Route-Specifieke Components (/app/.../components)

Doel: Feature-specifieke components die alleen gebruikt worden binnen één route of feature.

Structuur:

app/
└── epd/
    ├── components/              # Gedeeld binnen EPD module
    │   └── epd-sidebar.tsx
    └── patients/
        ├── components/          # Gedeeld binnen patients feature
        │   ├── patient-list.tsx
        │   └── patient-form.tsx
        └── [id]/
            └── rapportage/
                └── components/  # Specifiek voor rapportage feature
                    ├── report-composer.tsx
                    ├── report-timeline.tsx
                    └── rapportage-workspace.tsx

Criteria voor route-specifieke components:

  • Gebruikt alleen binnen één feature/route
  • Bevat feature-specifieke business logic
  • Tight coupling met de parent route
  • Geen hergebruik in andere features

Voorbeelden:

// ✅ Alleen gebruikt in rapportage feature
import { ReportComposer } from './components/report-composer';

// ✅ Specifieke business logic voor behandeladvies
import { TreatmentAdviceForm } from './components/treatment-advice-form';

Hiërarchie & Scope

Components worden georganiseerd op basis van hun reuse scope:

┌─────────────────────────────────────────────────────────┐
│ /components                                              │
│ ↳ App-wide herbruikbare components                      │
│   (gebruikt in 2+ features)                             │
└─────────────────────────────────────────────────────────┘
         ↓ imports van
┌─────────────────────────────────────────────────────────┐
│ /app/epd/components                                      │
│ ↳ EPD module-wide components                            │
│   (gedeeld tussen patient, intake, rapportage)          │
└─────────────────────────────────────────────────────────┘
         ↓ imports van
┌─────────────────────────────────────────────────────────┐
│ /app/epd/patients/components                            │
│ ↳ Patient feature components                            │
│   (gedeeld tussen patient routes)                       │
└─────────────────────────────────────────────────────────┘
         ↓ imports van
┌─────────────────────────────────────────────────────────┐
│ /app/epd/patients/[id]/rapportage/components           │
│ ↳ Rapportage page-specifieke components                │
│   (alleen gebruikt in rapportage)                       │
└─────────────────────────────────────────────────────────┘

Statistieken (Huidige State)

  • Centrale components: 18 components
  • Route-specifieke components: 56 components
  • Duplicaten: 0

Voordelen van Deze Aanpak

1. Betere Code Organisation

  • Components staan dichtbij waar ze gebruikt worden
  • Makkelijker te vinden en te onderhouden
  • Duidelijke scope en ownership

2. Betere Performance

  • Kleinere bundles per route (code splitting)
  • Alleen relevante components worden geladen
  • Tree-shaking werkt beter

3. Betere Developer Experience

  • Minder zoeken in grote component directories
  • Duidelijk wanneer een component herbruikbaar is
  • Makkelijker refactoren

4. Schaalbaarheid

  • Nieuwe features kunnen onafhankelijk components toevoegen
  • Geen "god component folder" met 100+ bestanden
  • Teams kunnen parallel werken zonder conflicts

Decision Tree: Waar plaats ik een component?

Wordt de component gebruikt in 2+ verschillende features?
│
├─ Ja → Is het een generieke UI component (button, dialog, etc)?
│       │
│       ├─ Ja → /components/ui/{name}.tsx
│       │
│       └─ Nee → /components/{name}.tsx
│
└─ Nee → Wordt het gedeeld binnen een feature module?
          │
          ├─ Ja → /app/{feature}/components/{name}.tsx
          │
          └─ Nee → /app/{feature}/{subfeature}/components/{name}.tsx

Voorbeelden

Goed: SpeechRecorderStreaming in centrale folder

Waarom? Gebruikt in meerdere features:

// app/epd/patients/[id]/rapportage/components/report-composer.tsx
import { SpeechRecorderStreaming } from '@/components/speech-recorder-streaming';

// app/epd/patients/[id]/intakes/[intakeId]/behandeladvies/components/treatment-advice-form.tsx
import { SpeechRecorderStreaming } from '@/components/speech-recorder-streaming';

Goed: ReportComposer in rapportage/components

Waarom? Alleen gebruikt in rapportage feature:

// app/epd/patients/[id]/rapportage/page.tsx
import { ReportComposer } from './components/report-composer';

Fout: Generieke Button in route folder

// ❌ NIET DOEN
// app/epd/patients/components/button.tsx
export function Button() { ... }

// ✅ WEL DOEN
// components/ui/button.tsx
export function Button() { ... }

Fout: Feature-specifieke component in centrale folder

// ❌ NIET DOEN
// components/report-composer.tsx (alleen gebruikt in rapportage)

// ✅ WEL DOEN
// app/epd/patients/[id]/rapportage/components/report-composer.tsx

Refactoring Workflow

Wanneer een route-component herbruikbaar wordt:

  1. Identificeer hergebruik

    # Check waar component gebruikt wordt
    grep -r "import.*ComponentName" app/
    
  2. Verplaats naar centrale folder

    mv app/feature/components/component.tsx components/
    
  3. Update alle imports

    // Van:
    import { Component } from '../components/component';
    
    // Naar:
    import { Component } from '@/components/component';
    
  4. Generaliseer indien nodig

    • Verwijder feature-specifieke logic
    • Maak configureerbaar via props
    • Update TypeScript types

Wanneer een centrale component feature-specifiek wordt:

(Dit komt zelden voor, maar kan gebeuren)

  1. Check of component echt nergens anders gebruikt wordt
  2. Verplaats naar meest specifieke route waar het gebruikt wordt
  3. Update imports

Server vs Client Components

// Server Component (default in app/)
export default function ReportPage() { ... }

// Client Component (expliciet markeren)
'use client';
export function ReportComposer() { ... }

Route-specifieke components kunnen zowel server als client components zijn. Centrale components zijn meestal client components (interactief).

Composition Pattern

Route-specifieke components kunnen centrale components gebruiken:

// app/epd/patients/[id]/rapportage/components/report-composer.tsx
import { SpeechRecorderStreaming } from '@/components/speech-recorder-streaming';
import { Button } from '@/components/ui/button';

export function ReportComposer() {
  return (
    <div>
      <SpeechRecorderStreaming />
      <Button>Save</Button>
    </div>
  );
}

Best Practices

  1. Start route-specifiek - Begin met components in route folders, verplaats alleen naar centraal als er echt hergebruik is
  2. Gebruik absolute imports - @/components/... voor centrale, relative voor route-specifieke
  3. Avoid premature abstraction - Wacht tot een component 2x gebruikt wordt voordat je het generaliseert
  4. Keep it colocated - Plaats components zo dichtbij mogelijk bij waar ze gebruikt worden
  5. Document reusability - Als een component generiek is, documenteer dan het gebruik in JSDoc

Tools & Commands

Find all components in a route:

find app/epd/patients/[id]/rapportage -name "*.tsx" -type f

Check component usage:

grep -r "import.*ComponentName" app/

Count components per location:

find components -name "*.tsx" | wc -l
find app -path "*/components/*" -name "*.tsx" | wc -l

References


Last Updated: 2024-11-24 Status: Active pattern in gebruik