Skip to content

Commit

Permalink
feat: participant selection on export url modal
Browse files Browse the repository at this point in the history
for protocols view. allows for selecting which participants to export urls for for each protocol
  • Loading branch information
buckhalt committed Jan 25, 2024
1 parent d8817f1 commit f44798a
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use client';

import { useEffect, useState } from 'react';

import { Button } from '~/components/ui/Button';
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '~/components/ui/dropdown-menu';
import type { Participant } from '@prisma/client';
import { ScrollArea } from '~/components/ui/scroll-area';

type ParticipantSelectionDropdownProps = {
participants: Participant[];
disabled: boolean;
setParticipantsToExport: (participants: Participant[]) => void;
};

export function ParticipantSelectionDropdown({
participants,
disabled,
}: ParticipantSelectionDropdownProps) {
const [selectedParticipants, setSelectedParticipants] = useState<
Participant[]
>([]);

useEffect(() => {
setSelectedParticipants(participants);
}, [participants]);

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button disabled={disabled} variant="outline">
Select Participants
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-auto">
<ScrollArea className="h-72 w-auto">
<DropdownMenuLabel>Participants</DropdownMenuLabel>
<div className="flex flex-row gap-2 p-2">
<div className="text-sm">
{selectedParticipants.length} selected
</div>
<Button
size="xs"
onClick={() => setSelectedParticipants(participants)}
>
Select All
</Button>
<Button
variant="destructive"
size="xs"
onClick={() => setSelectedParticipants([])}
>
Clear
</Button>
</div>

<DropdownMenuSeparator />

{/* loop through all participants and render a dropdown menu checkbox item for them */}
{participants.map((participant) => {
return (
<DropdownMenuCheckboxItem
key={participant.id}
checked={selectedParticipants.includes(participant)}
onCheckedChange={(checked) => {
if (checked) {
setSelectedParticipants([
...selectedParticipants,
participant,
]);
} else {
setSelectedParticipants(
selectedParticipants.filter(
(selectedParticipant) =>
selectedParticipant !== participant,
),
);
}
}}
>
{participant.identifier}
</DropdownMenuCheckboxItem>
);
})}
</ScrollArea>
</DropdownMenuContent>
</DropdownMenu>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { DeleteAllParticipantsButton } from '~/app/(dashboard)/dashboard/partici
import AddParticipantButton from '~/app/(dashboard)/dashboard/participants/_components/AddParticipantButton';
import { useState } from 'react';
import { DeleteParticipantsDialog } from '~/app/(dashboard)/dashboard/participants/_components/DeleteParticipantsDialog';
import { RecruitmentModal } from '~/app/(dashboard)/dashboard/_components/ParticipantsTable/RecruitmentModal';
import { RecruitmentModal } from '~/app/(dashboard)/dashboard/_components/RecruitmentModal';

export const ParticipantsTable = ({
initialData,
Expand Down Expand Up @@ -43,7 +43,10 @@ export const ParticipantsTable = ({
<AddParticipantButton existingParticipants={participants} />
<ImportCSVModal />
<DeleteAllParticipantsButton />
<RecruitmentModal />
<RecruitmentModal
description="Generate a CSV of participation URLs for all participants by
protocol."
/>
</div>
{isLoading && <div>Loading...</div>}
<DeleteParticipantsDialog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DeleteProtocolsDialog } from '~/app/(dashboard)/dashboard/protocols/_co
import { useState } from 'react';
import type { ProtocolWithInterviews } from '~/shared/types';
import { AnonymousRecruitmentModal } from './AnonymousRecruitmentModal';
import { RecruitmentModal } from '../RecruitmentModal';

export const ProtocolsTable = ({
initialData,
Expand Down Expand Up @@ -36,7 +37,13 @@ export const ProtocolsTable = ({

return (
<>
<AnonymousRecruitmentModal />
<div className="flex gap-2">
<AnonymousRecruitmentModal />
<RecruitmentModal
allowSelectParticipants
description="Generate a CSV of participation URLs for selected participants by protocol."
/>
</div>
{isLoading && <div>Loading...</div>}
<DataTable<ProtocolWithInterviews, string>
columns={ProtocolColumns}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import type { Protocol } from '@prisma/client';
import type { Participant, Protocol } from '@prisma/client';
import { useState, useEffect } from 'react';
import {
Dialog,
Expand All @@ -19,15 +19,23 @@ import {

import { Button } from '~/components/ui/Button';
import { api } from '~/trpc/client';
import ExportCSVParticipants from '../../participants/_components/ExportCSVParticipants';
import ExportCSVParticipants from '../participants/_components/ExportCSVParticipants';
import { ParticipantSelectionDropdown } from './ParticipantSelectionDropdown';

export const RecruitmentModal = () => {
export const RecruitmentModal = ({
allowSelectParticipants,
description,
}: {
allowSelectParticipants?: boolean;
description: string;
}) => {
const { data: protocolData, isLoading: isLoadingProtocols } =
api.protocol.get.all.useQuery();
const [protocols, setProtocols] = useState<Protocol[]>([]);
const [participants, setParticipants] = useState<Participant[]>([]);
const [selectedProtocol, setSelectedProtocol] = useState<Protocol>();

const { data: participants, isLoading: isLoadingParticipants } =
const { data: participantData, isLoading: isLoadingParticipants } =
api.participant.get.all.useQuery();

useEffect(() => {
Expand All @@ -36,6 +44,12 @@ export const RecruitmentModal = () => {
}
}, [protocolData]);

useEffect(() => {
if (participantData) {
setParticipants(participantData);
}
}, [participantData]);

return (
<Dialog onOpenChange={() => setSelectedProtocol(undefined)}>
<DialogTrigger asChild>
Expand All @@ -44,12 +58,10 @@ export const RecruitmentModal = () => {
<DialogContent>
<DialogHeader>
<DialogTitle>Participation URLs</DialogTitle>
<DialogDescription>
Generate a CSV of participation URLs for all participants by
protocol.
</DialogDescription>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>
<div className="flex gap-4">
<div className="flex flex-col gap-4">
{/* Protocol selection */}
<Select
onValueChange={(value) => {
const protocol = protocols.find(
Expand All @@ -72,6 +84,15 @@ export const RecruitmentModal = () => {
))}
</SelectContent>
</Select>
{/* Participant selection if enabled */}
{allowSelectParticipants && (
<ParticipantSelectionDropdown
participants={participants}
disabled={!selectedProtocol}
setParticipantsToExport={setParticipants}
/>
)}

<ExportCSVParticipants
protocolId={selectedProtocol?.id}
participants={participants}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { type Participant } from '@prisma/client';
import { Download } from 'lucide-react';
import { unparse } from 'papaparse';
import { useState } from 'react';
import { Button } from '~/components/ui/Button';
Expand Down Expand Up @@ -64,7 +65,8 @@ function ExportCSVParticipants({

return (
<Button disabled={disabled || isExporting} onClick={handleExport}>
{isExporting ? 'Exporting...' : 'Export'}
<Download className="mr-2 h-4 w-4" />
{isExporting ? 'Exporting...' : 'Export Participation URLs'}
</Button>
);
}
Expand Down

0 comments on commit f44798a

Please sign in to comment.