Skip to content

Commit

Permalink
Show check-ins per day in Participants table (#363)
Browse files Browse the repository at this point in the history
- Provide checkins list in participants endpoint
- Add columns for each day of the event with an icon on check-in status
- Disable resizable columns
  • Loading branch information
taesungh authored Jan 26, 2024
1 parent fbbc82d commit 365683d
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 3 deletions.
11 changes: 9 additions & 2 deletions apps/api/src/admin/participant_manager.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from datetime import datetime
from logging import getLogger
from typing import Any, Optional, Union

from typing_extensions import TypeAlias

from auth.user_identity import User, utc_now
from models.ApplicationData import Decision
from services import mongodb_handler
Expand All @@ -9,6 +12,8 @@

log = getLogger(__name__)

Checkin: TypeAlias = tuple[datetime, str]

NON_HACKER_ROLES = (
Role.MENTOR,
Role.VOLUNTEER,
Expand All @@ -21,6 +26,7 @@
class Participant(UserRecord):
"""Participants attending the event."""

checkins: list[Checkin] = []
first_name: str
last_name: str
status: Union[Status, Decision] = Status.REVIEWED
Expand All @@ -47,6 +53,7 @@ async def get_hackers() -> list[Participant]:
"_id",
"status",
"role",
"checkins",
"application_data.first_name",
"application_data.last_name",
],
Expand All @@ -60,7 +67,7 @@ async def get_non_hackers() -> list[Participant]:
records: list[dict[str, Any]] = await mongodb_handler.retrieve(
Collection.USERS,
{"role": {"$in": NON_HACKER_ROLES}},
["_id", "status", "role", "first_name", "last_name"],
["_id", "status", "role", "checkins", "first_name", "last_name"],
)
return [Participant(**user) for user in records]

Expand All @@ -77,7 +84,7 @@ async def check_in_participant(uid: str, associate: User) -> None:
):
raise ValueError

new_checkin_entry = (utc_now(), associate.uid)
new_checkin_entry: Checkin = (utc_now(), associate.uid)

update_status = await mongodb_handler.raw_update_one(
Collection.USERS,
Expand Down
38 changes: 38 additions & 0 deletions apps/site/src/app/admin/participants/components/CheckinDayIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Icon from "@cloudscape-design/components/icon";
import dayjs from "dayjs";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";

import { Checkin } from "@/lib/admin/useParticipants";

dayjs.extend(utc);
dayjs.extend(timezone);
const EVENT_TIMEZONE = "America/Los_Angeles";

interface CheckinDayProps {
checkins: Checkin[];
date: Date;
}

const today = dayjs().tz(EVENT_TIMEZONE);

function CheckinDayIcon({ checkins, date }: CheckinDayProps) {
// Timezones are weird, but comparing the days of the check-ins
const day = dayjs(date).tz(EVENT_TIMEZONE);
const checkinTimes = checkins.map(([datetime]) =>
dayjs(datetime).tz(EVENT_TIMEZONE),
);

const checkedIn = checkinTimes.some((checkin) => day.isSame(checkin, "date"));
const past = day.isBefore(today, "date");

if (checkedIn) {
return <Icon name="status-positive" variant="success" alt="Checked in" />;
}
if (past) {
return <Icon name="status-negative" variant="error" alt="No show" />;
}
return <Icon name="status-pending" variant="subtle" alt="Pending" />;
}

export default CheckinDayIcon;
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ import TextFilter from "@cloudscape-design/components/text-filter";
import ApplicantStatus from "@/app/admin/applicants/components/ApplicantStatus";
import { Participant } from "@/lib/admin/useParticipants";

import CheckinDayIcon from "./CheckinDayIcon";
import ParticipantAction from "./ParticipantAction";
import RoleBadge from "./RoleBadge";

const FRIDAY = new Date("2024-01-26T12:00:00");
const SATURDAY = new Date("2024-01-27T12:00:00");
const SUNDAY = new Date("2024-01-28T12:00:00");

interface ParticipantsTableProps {
participants: Participant[];
loading: boolean;
Expand Down Expand Up @@ -83,6 +88,24 @@ function ParticipantsTable({
cell: ApplicantStatus,
sortingField: "status",
},
{
id: "friday",
header: "Fri",
cell: FridayCheckin,
sortingField: "friday",
},
{
id: "saturday",
header: "Sat",
cell: SaturdayCheckin,
sortingField: "saturday",
},
{
id: "sunday",
header: "Sun",
cell: SundayCheckin,
sortingField: "sunday",
},
{
id: "action",
header: "Action",
Expand All @@ -95,7 +118,6 @@ function ParticipantsTable({
items={participants}
loading={loading}
loadingText="Loading participants"
resizableColumns
variant="full-page"
stickyColumns={{ first: 1, last: 0 }}
trackBy="_id"
Expand All @@ -109,4 +131,16 @@ function ParticipantsTable({
);
}

const FridayCheckin = ({ checkins }: Participant) => (
<CheckinDayIcon checkins={checkins} date={FRIDAY} />
);

const SaturdayCheckin = ({ checkins }: Participant) => (
<CheckinDayIcon checkins={checkins} date={SATURDAY} />
);

const SundayCheckin = ({ checkins }: Participant) => (
<CheckinDayIcon checkins={checkins} date={SUNDAY} />
);

export default ParticipantsTable;
3 changes: 3 additions & 0 deletions apps/site/src/lib/admin/useParticipants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ const enum Role {
WorkshopLead = "workshop_lead",
}

export type Checkin = [string, Uid];

export interface Participant {
_id: Uid;
first_name: string;
last_name: string;
role: Role;
checkins: Checkin[];
status: Status;
}

Expand Down

0 comments on commit 365683d

Please sign in to comment.