feat: rapportage API, speech recorder refactor, docs reorganisatie

Multiple features and improvements:

Reports/Rapportage API:
- Created REST API endpoints: /api/reports (GET/POST), /api/reports/[id] (GET/PATCH/DELETE)
- Added /api/reports/classify endpoint for AI classification
- Supabase migrations: reports table + RLS policies
- Server utilities: api-client.ts for DRY fetch logic
- Type definitions: lib/types/report.ts with Zod schemas
- Removed old rapportage-modal component (replaced by split-view)

Speech Recorder Refactor:
- Moved speech-recorder from intake-specific to shared components/
- Updated treatment-advice-form to use new location
- Updated intake actions for speech functionality

UI Components (shadcn):
- Added dialog, dropdown-menu, toast, toaster components
- Added use-toast hook for toast notifications

Documentation:
- Reorganized docs/release/ → docs/reports/ for better structure
- Archived old specs to docs/specs/archive/
- Added screening-system.mdx documentation
- Added rapportage-split-view-design.md
- Added UI screenshots for troubleshooting

Dependencies:
- Updated package.json and pnpm-lock.yaml
- Regenerated database.types.ts from Supabase

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
colinislit
2025-11-24 14:14:06 +01:00
parent 0130e28f1e
commit a789bebe96
37 changed files with 5034 additions and 1977 deletions

View File

@@ -0,0 +1,109 @@
-- Migration: create reports table for universal reporting
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE TABLE IF NOT EXISTS reports (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
patient_id UUID NOT NULL REFERENCES patients(id) ON DELETE CASCADE,
created_by UUID REFERENCES practitioners(id),
type TEXT NOT NULL CHECK (type IN (
'behandeladvies',
'vrije_notitie',
'intake',
'voortgang',
'crisis',
'contact'
)),
content TEXT NOT NULL CHECK (char_length(content) >= 20),
ai_confidence NUMERIC(3,2) CHECK (ai_confidence >= 0 AND ai_confidence <= 1),
ai_reasoning TEXT,
structured_data JSONB DEFAULT '{}'::jsonb,
audio_url TEXT,
audio_duration_seconds INTEGER,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ,
updated_by UUID REFERENCES practitioners(id),
version TEXT,
parent_report_id UUID REFERENCES reports(id),
deleted_at TIMESTAMPTZ,
CONSTRAINT reports_content_length CHECK (char_length(content) <= 5000)
);
CREATE INDEX IF NOT EXISTS idx_reports_patient_id ON reports(patient_id)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_reports_type ON reports(type)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_reports_created_at ON reports(created_at DESC)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_reports_created_by ON reports(created_by);
CREATE INDEX IF NOT EXISTS idx_reports_patient_created ON reports(patient_id, created_at DESC)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_reports_content_fts ON reports USING gin(to_tsvector('dutch', content))
WHERE deleted_at IS NULL;
ALTER TABLE reports ENABLE ROW LEVEL SECURITY;
CREATE POLICY reports_select_policy ON reports
FOR SELECT
USING (
EXISTS (
SELECT 1
FROM practitioners p
WHERE p.id = reports.created_by
AND p.user_id = auth.uid()
)
);
CREATE POLICY reports_insert_policy ON reports
FOR INSERT
WITH CHECK (
EXISTS (
SELECT 1
FROM practitioners p
WHERE p.id = reports.created_by
AND p.user_id = auth.uid()
)
);
CREATE POLICY reports_update_policy ON reports
FOR UPDATE
USING (
EXISTS (
SELECT 1
FROM practitioners p
WHERE p.id = reports.created_by
AND p.user_id = auth.uid()
)
)
WITH CHECK (
EXISTS (
SELECT 1
FROM practitioners p
WHERE p.id = reports.created_by
AND p.user_id = auth.uid()
)
);
CREATE POLICY reports_delete_policy ON reports
FOR UPDATE
USING (
EXISTS (
SELECT 1
FROM practitioners p
WHERE p.id = reports.created_by
AND p.user_id = auth.uid()
)
AND deleted_at IS NULL
)
WITH CHECK (deleted_at IS NOT NULL);
COMMENT ON TABLE reports IS 'Universal reporting system - AI-powered classification';
COMMENT ON COLUMN reports.type IS 'Report type: behandeladvies, vrije_notitie, etc.';
COMMENT ON COLUMN reports.ai_confidence IS 'AI classification confidence (0.0-1.0)';
COMMENT ON COLUMN reports.structured_data IS 'Extracted entities (DSM codes, medication, etc.)';
COMMENT ON COLUMN reports.audio_url IS 'Supabase Storage URL for audio recording';

View File

@@ -0,0 +1,33 @@
-- Migration: Fix RLS policies for reports table (prototype - open access)
-- All authenticated users can read/write all reports
-- Drop existing restrictive policies
DROP POLICY IF EXISTS reports_select_policy ON reports;
DROP POLICY IF EXISTS reports_insert_policy ON reports;
DROP POLICY IF EXISTS reports_update_policy ON reports;
DROP POLICY IF EXISTS reports_delete_policy ON reports;
-- Create simple policies: all authenticated users can do everything
CREATE POLICY reports_select_policy ON reports
FOR SELECT
USING (auth.role() = 'authenticated');
CREATE POLICY reports_insert_policy ON reports
FOR INSERT
WITH CHECK (auth.role() = 'authenticated');
CREATE POLICY reports_update_policy ON reports
FOR UPDATE
USING (auth.role() = 'authenticated')
WITH CHECK (auth.role() = 'authenticated');
CREATE POLICY reports_delete_policy ON reports
FOR UPDATE
USING (auth.role() = 'authenticated' AND deleted_at IS NULL)
WITH CHECK (auth.role() = 'authenticated' AND deleted_at IS NOT NULL);
-- Comment for documentation
COMMENT ON POLICY reports_select_policy ON reports IS 'Prototype: All authenticated users can read all reports';
COMMENT ON POLICY reports_insert_policy ON reports IS 'Prototype: All authenticated users can create reports';
COMMENT ON POLICY reports_update_policy ON reports IS 'Prototype: All authenticated users can update reports';
COMMENT ON POLICY reports_delete_policy ON reports IS 'Prototype: All authenticated users can soft-delete reports';