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:
@@ -9,7 +9,7 @@
|
||||
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { headers, cookies } from 'next/headers';
|
||||
import { authFetch, getBaseUrl } from '@/lib/server/api-client';
|
||||
import type {
|
||||
Intake,
|
||||
CreateIntakeInput,
|
||||
@@ -21,42 +21,6 @@ import type {
|
||||
* Get the base URL for API calls in server actions
|
||||
* Uses headers() to get the host from the request
|
||||
*/
|
||||
function getBaseUrl(): string {
|
||||
// Try environment variable first
|
||||
if (process.env.NEXT_PUBLIC_APP_URL) {
|
||||
return process.env.NEXT_PUBLIC_APP_URL;
|
||||
}
|
||||
|
||||
// Try to get from headers (works in server components/actions)
|
||||
try {
|
||||
const headersList = headers();
|
||||
const host = headersList.get('host');
|
||||
const protocol = headersList.get('x-forwarded-proto') || 'http';
|
||||
if (host) {
|
||||
return `${protocol}://${host}`;
|
||||
}
|
||||
} catch {
|
||||
// Headers not available, fallback to localhost
|
||||
}
|
||||
|
||||
// Fallback to localhost
|
||||
return 'http://localhost:3000';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cookies as a string for fetch headers
|
||||
*/
|
||||
async function getCookieHeader(): Promise<string> {
|
||||
try {
|
||||
const cookieStore = await cookies();
|
||||
return cookieStore
|
||||
.getAll()
|
||||
.map((cookie) => `${cookie.name}=${cookie.value}`)
|
||||
.join('; ');
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all intakes for a specific patient
|
||||
@@ -69,13 +33,8 @@ export async function getIntakesByPatientId(patientId: string): Promise<Intake[]
|
||||
const url = new URL('/api/intakes', baseUrl);
|
||||
url.searchParams.set('patientId', patientId);
|
||||
|
||||
const cookieHeader = await getCookieHeader();
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
const response = await authFetch(url.toString(), {
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
...(cookieHeader && { Cookie: cookieHeader }),
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -112,13 +71,8 @@ export async function getIntakeById(intakeId: string): Promise<Intake | null> {
|
||||
try {
|
||||
const baseUrl = getBaseUrl();
|
||||
const url = `${baseUrl}/api/intakes/${intakeId}`;
|
||||
const cookieHeader = await getCookieHeader();
|
||||
|
||||
const response = await fetch(url, {
|
||||
const response = await authFetch(url, {
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
...(cookieHeader && { Cookie: cookieHeader }),
|
||||
},
|
||||
});
|
||||
|
||||
if (response.status === 404) {
|
||||
@@ -163,13 +117,10 @@ export async function createIntake(input: CreateIntakeInput): Promise<Intake> {
|
||||
try {
|
||||
const baseUrl = getBaseUrl();
|
||||
const url = `${baseUrl}/api/intakes`;
|
||||
const cookieHeader = await getCookieHeader();
|
||||
|
||||
const response = await fetch(url, {
|
||||
const response = await authFetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(cookieHeader && { Cookie: cookieHeader }),
|
||||
},
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
@@ -230,13 +181,10 @@ export async function updateIntake(
|
||||
try {
|
||||
const baseUrl = getBaseUrl();
|
||||
const url = `${baseUrl}/api/intakes/${intakeId}`;
|
||||
const cookieHeader = await getCookieHeader();
|
||||
|
||||
const response = await fetch(url, {
|
||||
const response = await authFetch(url, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(cookieHeader && { Cookie: cookieHeader }),
|
||||
},
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
@@ -332,4 +280,3 @@ export async function deleteIntake(intakeId: string, patientId: string): Promise
|
||||
throw error instanceof Error ? error : new Error('Failed to delete intake');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user