'use client'; import { useCallback, useEffect, useState } from "react"; import { useParams } from "next/navigation"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card"; import { Button } from "~/components/ui/button"; import { useToast } from "~/hooks/use-toast"; import { Plus, Trash2 } from "lucide-react"; import Link from "next/link"; import { useActiveStudy } from "~/context/active-study"; import { hasPermission } from "~/lib/permissions-client"; import { PERMISSIONS } from "~/lib/permissions"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "~/components/ui/alert-dialog"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "~/components/ui/table"; import { getApiUrl } from "~/lib/fetch-utils"; interface Participant { id: number; name: string; studyId: number; createdAt: string; } export default function ParticipantsList() { const [participants, setParticipants] = useState([]); const [isLoading, setIsLoading] = useState(true); const { id } = useParams(); const { toast } = useToast(); const { activeStudy } = useActiveStudy(); const canCreateParticipant = activeStudy && hasPermission(activeStudy.permissions, PERMISSIONS.CREATE_PARTICIPANT); const canDeleteParticipant = activeStudy && hasPermission(activeStudy.permissions, PERMISSIONS.DELETE_PARTICIPANT); const canViewNames = activeStudy && hasPermission(activeStudy.permissions, PERMISSIONS.VIEW_PARTICIPANT_NAMES); const fetchParticipants = useCallback(async () => { try { const response = await fetch(getApiUrl(`/api/studies/${id}/participants`), { method: "GET", headers: { "Content-Type": "application/json", }, }); if (!response.ok) throw new Error("Failed to fetch participants"); const data = await response.json(); setParticipants(data.data || []); } catch (error) { console.error("Error fetching participants:", error); toast({ title: "Error", description: "Failed to load participants", variant: "destructive", }); } finally { setIsLoading(false); } }, [toast, id]); useEffect(() => { fetchParticipants(); }, [fetchParticipants]); const handleDelete = async (participantId: number) => { try { const response = await fetch(getApiUrl(`/api/studies/${id}/participants`), { method: "DELETE", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ participantId }), }); if (!response.ok) throw new Error("Failed to delete participant"); setParticipants(participants.filter(p => p.id !== participantId)); toast({ title: "Success", description: "Participant deleted successfully", }); } catch (error) { console.error("Error deleting participant:", error); toast({ title: "Error", description: "Failed to delete participant", variant: "destructive", }); } }; if (isLoading) { return (

Loading participants...

); } return (

Participants

Manage study participants and their data

{canCreateParticipant && ( )}
Study Participants All participants enrolled in {activeStudy?.title} {participants.length > 0 ? ( ID Name Added {canDeleteParticipant && Actions} {participants.map((participant) => ( {participant.id} {canViewNames ? participant.name : `Participant ${participant.id}`} {new Date(participant.createdAt).toLocaleDateString()} {canDeleteParticipant && ( Delete Participant Are you sure you want to delete this participant? This action cannot be undone. Cancel handleDelete(participant.id)} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > Delete )} ))}
) : (
No participants added yet {canCreateParticipant && ( <> .{" "} Add your first participant )}
)}
); }