Files
triqura-ecd/app/epd/patients/[id]/screening/components/document-card.tsx
2025-11-23 10:13:00 +01:00

198 lines
6.6 KiB
TypeScript

'use client';
import { useState, useTransition } from 'react';
import { useRouter } from 'next/navigation';
import type { ScreeningDocument } from '@/lib/types/screening';
import { Loader2, Trash2, UploadCloud } from 'lucide-react';
import { cn } from '@/lib/utils';
const DOCUMENT_BUCKET = 'screening-documents';
const BASE_STORAGE_URL = `${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/${DOCUMENT_BUCKET}`;
const documentTypeOptions = [
'verwijsbrief',
'verhuisbericht',
'indicatie',
'overig',
];
interface DocumentCardProps {
patientId: string;
screeningId: string;
documents: ScreeningDocument[];
}
export function DocumentCard({ patientId, screeningId, documents }: DocumentCardProps) {
const router = useRouter();
const [file, setFile] = useState<File | null>(null);
const [documentType, setDocumentType] = useState('verwijsbrief');
const [error, setError] = useState<string | null>(null);
const [isUploading, startUpload] = useTransition();
const [deletingId, setDeletingId] = useState<string | null>(null);
const handleUpload = () => {
if (!file) {
setError('Selecteer eerst een bestand.');
return;
}
setError(null);
startUpload(async () => {
const formData = new FormData();
formData.append('file', file);
formData.append('documentType', documentType);
try {
const response = await fetch(`/api/screenings/${screeningId}/documents`, {
method: 'POST',
body: formData,
});
if (!response.ok) {
const data = await response.json().catch(() => ({}));
throw new Error(data.error || 'Upload mislukt');
}
setFile(null);
router.refresh();
} catch (err) {
console.error(err);
setError(err instanceof Error ? err.message : 'Upload mislukt');
}
});
};
const handleDelete = async (documentId: string) => {
setDeletingId(documentId);
try {
const response = await fetch(
`/api/screenings/${screeningId}/documents/${documentId}`,
{
method: 'DELETE',
}
);
if (!response.ok) {
const data = await response.json().catch(() => ({}));
throw new Error(data.error || 'Verwijderen mislukt');
}
router.refresh();
} catch (err) {
console.error(err);
setError(err instanceof Error ? err.message : 'Verwijderen mislukt');
} finally {
setDeletingId(null);
}
};
return (
<section className="bg-white rounded-lg border border-slate-200 p-6 shadow-sm">
<div className="flex items-center gap-3 mb-4">
<div className="p-2 rounded-md bg-slate-100 text-slate-600">
<UploadCloud className="h-5 w-5" />
</div>
<div>
<h3 className="text-lg font-semibold text-slate-900">Documenten</h3>
<p className="text-sm text-slate-500">
Upload verwijsbrieven, indicaties en andere stukken.
</p>
</div>
</div>
<div className="space-y-4">
<div className="border border-dashed border-slate-300 rounded-lg p-4">
<div className="flex flex-col gap-3">
<input
type="file"
onChange={(event) => {
const selected = event.target.files?.[0] ?? null;
setFile(selected);
setError(null);
}}
/>
<div className="flex flex-col sm:flex-row sm:items-center gap-3">
<select
value={documentType}
onChange={(event) => setDocumentType(event.target.value)}
className="h-10 rounded-md border border-slate-300 px-3 text-sm"
>
{documentTypeOptions.map((option) => (
<option key={option} value={option}>
{option.charAt(0).toUpperCase() + option.slice(1)}
</option>
))}
</select>
<button
type="button"
onClick={handleUpload}
disabled={isUploading}
className={cn(
'inline-flex items-center justify-center gap-2 rounded-md bg-teal-600 px-4 py-2 text-sm font-medium text-white hover:bg-teal-700 disabled:opacity-60'
)}
>
{isUploading && <Loader2 className="h-4 w-4 animate-spin" />}
Upload document
</button>
</div>
{error && <p className="text-sm text-red-600">{error}</p>}
{file && (
<p className="text-xs text-slate-500">
Geselecteerd: {file.name} {(file.size / 1024).toFixed(1)} KB
</p>
)}
</div>
</div>
<div className="space-y-3">
{documents.length === 0 && (
<p className="text-sm text-slate-500">
Nog geen documenten geregistreerd.
</p>
)}
{documents.map((doc) => {
const publicUrl = `${BASE_STORAGE_URL}/${doc.file_path}`;
return (
<div
key={doc.id}
className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 border border-slate-200 rounded-lg p-3"
>
<div>
<p className="text-sm font-medium text-slate-900">{doc.file_name}</p>
<p className="text-xs text-slate-500">
{doc.document_type} {(doc.file_size / 1024).toFixed(1)} KB {' '}
{doc.uploaded_by_name || 'Onbekend'}
</p>
</div>
<div className="flex items-center gap-3">
<a
href={publicUrl}
target="_blank"
rel="noopener noreferrer"
className="text-sm text-teal-600 hover:text-teal-700"
>
Download
</a>
<button
type="button"
onClick={() => handleDelete(doc.id)}
disabled={deletingId === doc.id}
className="inline-flex items-center gap-1 rounded-md border border-red-200 px-3 py-1.5 text-xs font-medium text-red-600 hover:bg-red-50 disabled:opacity-60"
>
{deletingId === doc.id ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Trash2 className="h-4 w-4" />
)}
Verwijderen
</button>
</div>
</div>
);
})}
</div>
</div>
</section>
);
}