feat: Redesign Landing, Auth, and Dashboard Pages

Also fixed schema type exports and seed script errors.
This commit is contained in:
2026-02-01 22:28:19 -05:00
parent 816b2b9e31
commit dbfdd91dea
300 changed files with 17239 additions and 5952 deletions

81
src/components/participants/ParticipantForm.tsx Normal file → Executable file
View File

@@ -114,39 +114,39 @@ export function ParticipantForm({
{ label: "Studies", href: "/studies" },
...(contextStudyId
? [
{
label: participant?.study?.name ?? "Study",
href: `/studies/${contextStudyId}`,
},
{
label: "Participants",
href: `/studies/${contextStudyId}/participants`,
},
...(mode === "edit" && participant
? [
{
label: participant.name ?? participant.participantCode,
href: `/studies/${contextStudyId}/participants/${participant.id}`,
},
{ label: "Edit" },
]
: [{ label: "New Participant" }]),
]
{
label: participant?.study?.name ?? "Study",
href: `/studies/${contextStudyId}`,
},
{
label: "Participants",
href: `/studies/${contextStudyId}/participants`,
},
...(mode === "edit" && participant
? [
{
label: participant.name ?? participant.participantCode,
href: `/studies/${contextStudyId}/participants/${participant.id}`,
},
{ label: "Edit" },
]
: [{ label: "New Participant" }]),
]
: [
{
label: "Participants",
href: `/studies/${contextStudyId}/participants`,
},
...(mode === "edit" && participant
? [
{
label: participant.name ?? participant.participantCode,
href: `/studies/${contextStudyId}/participants/${participant.id}`,
},
{ label: "Edit" },
]
: [{ label: "New Participant" }]),
]),
{
label: "Participants",
href: `/studies/${contextStudyId}/participants`,
},
...(mode === "edit" && participant
? [
{
label: participant.name ?? participant.participantCode,
href: `/studies/${contextStudyId}/participants/${participant.id}`,
},
{ label: "Edit" },
]
: [{ label: "New Participant" }]),
]),
];
useBreadcrumbsEffect(breadcrumbs);
@@ -203,7 +203,7 @@ export function ParticipantForm({
email: data.email ?? undefined,
demographics,
});
router.push(`/participants/${newParticipant.id}`);
router.push(`/studies/${data.studyId}/participants/${newParticipant.id}`);
} else {
const updatedParticipant = await updateParticipantMutation.mutateAsync({
id: participantId!,
@@ -212,7 +212,7 @@ export function ParticipantForm({
email: data.email ?? undefined,
demographics,
});
router.push(`/participants/${updatedParticipant.id}`);
router.push(`/studies/${contextStudyId}/participants/${updatedParticipant.id}`);
}
} catch (error) {
setError(
@@ -385,11 +385,11 @@ export function ParticipantForm({
form.setValue(
"gender",
value as
| "male"
| "female"
| "non_binary"
| "prefer_not_to_say"
| "other",
| "male"
| "female"
| "non_binary"
| "prefer_not_to_say"
| "other",
)
}
>
@@ -505,7 +505,8 @@ export function ParticipantForm({
error={error}
onDelete={mode === "edit" ? onDelete : undefined}
isDeleting={isDeleting}
sidebar={sidebar}
isDeleting={isDeleting}
// sidebar={sidebar} // Removed for cleaner UI per user request
submitText={mode === "create" ? "Register Participant" : "Save Changes"}
>
{formFields}

38
src/components/participants/ParticipantsTable.tsx Normal file → Executable file
View File

@@ -1,7 +1,7 @@
"use client";
import { type ColumnDef } from "@tanstack/react-table";
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
import { ArrowUpDown, MoreHorizontal, Copy, Eye, Edit, Mail, Trash2 } from "lucide-react";
import * as React from "react";
import { formatDistanceToNow } from "date-fns";
@@ -27,6 +27,7 @@ import { api } from "~/trpc/react";
export type Participant = {
id: string;
studyId: string;
participantCode: string;
email: string | null;
name: string | null;
@@ -75,7 +76,7 @@ export const columns: ColumnDef<Participant>[] = [
cell: ({ row }) => (
<div className="font-mono text-sm">
<Link
href={`/participants/${row.original.id}`}
href={`/studies/${row.original.studyId ?? ""}/participants/${row.original.id}`}
className="hover:underline"
>
{row.getValue("participantCode")}
@@ -176,6 +177,13 @@ export const columns: ColumnDef<Participant>[] = [
enableHiding: false,
cell: ({ row }) => {
const participant = row.original;
// Use studyId from participant or fallback might be needed but for now presume row has it?
// Wait, the Participant type definition above doesn't have studyId!
// I need to add studyId to the type definition in this file or rely on context if I'm inside the component,
// but 'columns' is defined outside.
// Best practice: Add studyId to the Participant type.
const studyId = participant.studyId;
return (
<DropdownMenu>
@@ -190,26 +198,27 @@ export const columns: ColumnDef<Participant>[] = [
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(participant.id)}
>
Copy participant ID
<Copy className="mr-2 h-4 w-4" />
Copy ID
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href={`/participants/${participant.id}`}>View details</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href={`/participants/${participant.id}/edit`}>
<Link href={`/studies/${studyId}/participants/${participant.id}/edit`}>
<Edit className="mr-2 h-4 w-4" />
Edit participant
</Link>
</Link >
</DropdownMenuItem >
<DropdownMenuItem disabled>
<Mail className="mr-2 h-4 w-4" />
Send consent
</DropdownMenuItem>
{!participant.consentGiven && (
<DropdownMenuItem>Send consent form</DropdownMenuItem>
)}
<DropdownMenuSeparator />
<DropdownMenuItem className="text-red-600">
Remove participant
<Trash2 className="mr-2 h-4 w-4" />
Remove
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</DropdownMenuContent >
</DropdownMenu >
);
},
},
@@ -250,6 +259,7 @@ export function ParticipantsTable({ studyId }: ParticipantsTableProps = {}) {
return participantsData.participants.map(
(p): Participant => ({
id: p.id,
studyId: p.studyId,
participantCode: p.participantCode,
email: p.email,
name: p.name,

0
src/components/participants/ParticipantsView.tsx Normal file → Executable file
View File