From e10d1021fcb1862adc1a889606727618ceb061c4 Mon Sep 17 00:00:00 2001 From: Taesung Hwang <44419552+taesungh@users.noreply.github.com> Date: Wed, 24 Jan 2024 00:56:27 -0800 Subject: [PATCH] Integrate check-in action with Participants table (#341) - When **Check In** action is used, show a modal with a confirmation - Make POST request to check-in endpoint upon confirmation - Several tasks still left as TODO - Need to add mutation for showing checked in on each day - Need to add actual instructions for check-in associates - Need to add Flashbar for notifications after modal completion - Will use a similar approach for the walk-in promotion modal --- .../app/admin/participants/Participants.tsx | 32 ++++++++++-- .../participants/components/CheckInModal.tsx | 52 +++++++++++++++++++ .../components/ParticipantAction.tsx | 16 +++++- .../components/ParticipantsTable.tsx | 21 +++++++- apps/site/src/lib/admin/useParticipants.ts | 14 ++++- 5 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 apps/site/src/app/admin/participants/components/CheckInModal.tsx diff --git a/apps/site/src/app/admin/participants/Participants.tsx b/apps/site/src/app/admin/participants/Participants.tsx index 6ce58134..087cb239 100644 --- a/apps/site/src/app/admin/participants/Participants.tsx +++ b/apps/site/src/app/admin/participants/Participants.tsx @@ -1,16 +1,40 @@ "use client"; -import useParticipants from "@/lib/admin/useParticipants"; +import { useState } from "react"; +import useParticipants, { Participant } from "@/lib/admin/useParticipants"; + +import CheckInModal from "./components/CheckInModal"; import ParticipantsTable from "./components/ParticipantsTable"; function Participants() { - const { participants, loading } = useParticipants(); + const { participants, loading, checkInParticipant } = useParticipants(); + const [currentParticipant, setCurrentParticipant] = + useState(null); + + const initiateCheckIn = (participant: Participant): void => { + setCurrentParticipant(participant); + }; + + const sendCheckIn = async (participant: Participant): Promise => { + await checkInParticipant(participant); + setCurrentParticipant(null); + // TODO: Flashbar notification + }; return ( <> - ; - {/* TODO: modal */} + + setCurrentParticipant(null)} + onConfirm={sendCheckIn} + participant={currentParticipant} + /> + {/* TODO: walk-in promotion modal */} ); } diff --git a/apps/site/src/app/admin/participants/components/CheckInModal.tsx b/apps/site/src/app/admin/participants/components/CheckInModal.tsx new file mode 100644 index 00000000..f88c013a --- /dev/null +++ b/apps/site/src/app/admin/participants/components/CheckInModal.tsx @@ -0,0 +1,52 @@ +import Box from "@cloudscape-design/components/box"; +import Button from "@cloudscape-design/components/button"; +import Modal from "@cloudscape-design/components/modal"; +import SpaceBetween from "@cloudscape-design/components/space-between"; +import TextContent from "@cloudscape-design/components/text-content"; + +import { Participant } from "@/lib/admin/useParticipants"; + +interface ActionModalProps { + onDismiss: () => void; + onConfirm: (participant: Participant) => void; + participant: Participant | null; +} + +function CheckInModal({ onDismiss, onConfirm, participant }: ActionModalProps) { + if (participant === null) { + return ; + } + + return ( + + + + + + + } + header={`Check In ${participant?.first_name} ${participant?.last_name}`} + > + + +
    + {/* TODO: actual instructions for check-in associates */} +
  • Create a badge for the participant ...
  • +
  • Ask participant to sign the SPFB sheet ...
  • +
+
+ {/* TODO: badge barcode input */} +
+
+ ); +} + +export default CheckInModal; diff --git a/apps/site/src/app/admin/participants/components/ParticipantAction.tsx b/apps/site/src/app/admin/participants/components/ParticipantAction.tsx index f4c2c064..87af7ea9 100644 --- a/apps/site/src/app/admin/participants/components/ParticipantAction.tsx +++ b/apps/site/src/app/admin/participants/components/ParticipantAction.tsx @@ -2,10 +2,22 @@ import Button from "@cloudscape-design/components/button"; import { Participant } from "@/lib/admin/useParticipants"; -function ParticipantAction({ _id }: Participant) { +interface ParticipantActionProps { + participant: Participant; + initiateCheckIn: (participant: Participant) => void; +} +function ParticipantAction({ + participant, + initiateCheckIn, +}: ParticipantActionProps) { // TODO: waitlist promotion + return ( - ); diff --git a/apps/site/src/app/admin/participants/components/ParticipantsTable.tsx b/apps/site/src/app/admin/participants/components/ParticipantsTable.tsx index 1a7895f0..f7169db6 100644 --- a/apps/site/src/app/admin/participants/components/ParticipantsTable.tsx +++ b/apps/site/src/app/admin/participants/components/ParticipantsTable.tsx @@ -1,3 +1,5 @@ +import { useCallback } from "react"; + import Box from "@cloudscape-design/components/box"; import Header from "@cloudscape-design/components/header"; import SpaceBetween from "@cloudscape-design/components/space-between"; @@ -13,13 +15,28 @@ import RoleBadge from "./RoleBadge"; interface ParticipantsTableProps { participants: Participant[]; loading: boolean; + initiateCheckIn: (participant: Participant) => void; } -function ParticipantsTable({ participants, loading }: ParticipantsTableProps) { +function ParticipantsTable({ + participants, + loading, + initiateCheckIn, +}: ParticipantsTableProps) { // TODO: sorting // TODO: search functionality // TODO: role and status filters + const ActionCell = useCallback( + (participant: Participant) => ( + + ), + [initiateCheckIn], + ); + const emptyMessage = ( @@ -66,7 +83,7 @@ function ParticipantsTable({ participants, loading }: ParticipantsTableProps) { { id: "action", header: "Action", - cell: ParticipantAction, + cell: ActionCell, }, ]} header={ diff --git a/apps/site/src/lib/admin/useParticipants.ts b/apps/site/src/lib/admin/useParticipants.ts index 43205868..f5a76410 100644 --- a/apps/site/src/lib/admin/useParticipants.ts +++ b/apps/site/src/lib/admin/useParticipants.ts @@ -34,8 +34,18 @@ function useParticipants() { fetcher, ); - // TODO: implement check-in mutation - return { participants: data ?? [], loading: isLoading, error }; + const checkInParticipant = async (participant: Participant) => { + console.log("Checking in", participant); + // TODO: implement mutation for showing checked in on each day + await axios.post(`/api/admin/checkin/${participant._id}`); + }; + + return { + participants: data ?? [], + loading: isLoading, + error, + checkInParticipant, + }; } export default useParticipants;