diff --git a/app/components/EventAgenda.tsx b/app/components/EventAgenda.tsx new file mode 100644 index 0000000..175c4fa --- /dev/null +++ b/app/components/EventAgenda.tsx @@ -0,0 +1,62 @@ +import { useUser } from '~/utils/user.ts' +import { volunteerTypes, type EventWithVolunteers } from '~/data.ts' + +type VolunteerTypes = typeof volunteerTypes +type VolunteerType = VolunteerTypes[number] + +interface PositionStatusProps { + event: EventWithVolunteers + volunteerType: VolunteerType +} + +function PositionStatus({ volunteerType, event }: PositionStatusProps) { + const user = useUser() + + const positionFilled = + event[volunteerType.field].length >= event[volunteerType.reqField] + const containerClass = `grid grid-cols-2 gap-4 ${ + positionFilled ? 'text-muted-foreground' : '' + }` + + const userIsRegistered = event[volunteerType.field] + .map(user => user.id) + .includes(user.id) + const volunteerTypeClass = `capitalize ${ + userIsRegistered ? 'before:content-["✅"] before:pr-1' : '' + }` + + const spotsLeft = + event[volunteerType.reqField] - event[volunteerType.field].length + + if (event[volunteerType.reqField] > 0) + return ( +
+
{volunteerType.displayName}
+
+ {spotsLeft} spot{spotsLeft === 1 ? '' : 's'} left +
+
+ ) +} + +export function EventAgenda({ event }: { event: EventWithVolunteers }) { + const eventIsUpcoming = event.end.valueOf() > new Date().valueOf() + + return ( +
+
{event.title}
+
+
+ {eventIsUpcoming && + volunteerTypes.map(volunteerType => ( + + ))} +
+
+
+ ) +} diff --git a/app/routes/_marketing+/index.tsx b/app/routes/_marketing+/index.tsx index 30355e9..9872605 100644 --- a/app/routes/_marketing+/index.tsx +++ b/app/routes/_marketing+/index.tsx @@ -23,8 +23,8 @@ export default function Index() { />
-
-

+
+

- -
Trot Track is built by: - - opportunity hack logo - +
+ + Trot Track + {' '} + is built by: + + opportunity hack logo +
diff --git a/app/routes/calendar+/index.tsx b/app/routes/calendar+/index.tsx index e63a7d5..b6b57e0 100644 --- a/app/routes/calendar+/index.tsx +++ b/app/routes/calendar+/index.tsx @@ -15,7 +15,7 @@ import { type HorseData, type EventWithVolunteers, } from '~/data.ts' -import { useState } from 'react' +import { useMemo, useState } from 'react' import { prisma } from '~/utils/db.server.ts' import { requireUserId } from '~/utils/auth.server.ts' @@ -71,6 +71,7 @@ import { horseDateConflicts, renderHorseConflictMessage, } from '~/utils/cooldown-functions.ts' +import { EventAgenda } from '~/components/EventAgenda.tsx' const locales = { 'en-US': enUS, @@ -139,8 +140,12 @@ const instructorSchema = z const createEventSchema = z.object({ title: z.string().min(1, 'Title is required'), - dates: z.string().regex(new RegExp(/^(\d{4}-\d{2}-\d{2},?\s?)+$/g), 'Invalid dates'), - startTime: z.string().regex(new RegExp(/^\d{2}:\d{2}$/g), 'Invalid start time'), + dates: z + .string() + .regex(new RegExp(/^(\d{4}-\d{2}-\d{2},?\s?)+$/g), 'Invalid dates'), + startTime: z + .string() + .regex(new RegExp(/^\d{2}:\d{2}$/g), 'Invalid start time'), duration: z.coerce.number().gt(0), horses: z.array(horseSchema).optional(), instructor: instructorSchema, @@ -269,13 +274,13 @@ export default function Schedule() { const [filterFlag, setFilterFlag] = useState(false) - const eventsThatNeedHelp = events.filter((event: (typeof events)[number]) => { return ( - event.cleaningCrewReq > event.cleaningCrew.length || - event.lessonAssistantsReq > event.lessonAssistants.length || - event.horseLeadersReq > event.horseLeaders.length || - event.sideWalkersReq > event.sideWalkers.length + event.start.valueOf() > new Date().valueOf() && + (event.cleaningCrewReq > event.cleaningCrew.length || + event.lessonAssistantsReq > event.lessonAssistants.length || + event.horseLeadersReq > event.horseLeaders.length || + event.sideWalkersReq > event.sideWalkers.length) ) }) @@ -284,10 +289,19 @@ export default function Schedule() { setRegisterOpen(!registerOpen) } + const components = useMemo( + () => ({ + agenda: { + event: EventAgenda, + }, + }), + [], + ) + return (
-

Calendar

-
+

Calendar

+
setFilterFlag(!filterFlag)} @@ -297,20 +311,22 @@ export default function Schedule() { Show only events that need more volunteers
- + {userIsAdmin ? ( - - ) : null} + + ) : null} -
+
`Cleaning Crew: ${event.cleaningCrew.length} / ${event.cleaningCrewReq}\nSidewalkers: ${event.sideWalkers.length} / ${event.sideWalkersReq}\nLesson Assistants: ${event.lessonAssistants.length} / ${event.lessonAssistantsReq}\nHorse Leaders: ${event.horseLeaders.length} / ${event.horseLeadersReq}`} + tooltipAccessor={event => + `Cleaning Crew: ${event.cleaningCrew.length} / ${event.cleaningCrewReq}\nSidewalkers: ${event.sideWalkers.length} / ${event.sideWalkersReq}\nLesson Assistants: ${event.lessonAssistants.length} / ${event.lessonAssistantsReq}\nHorse Leaders: ${event.horseLeaders.length} / ${event.horseLeadersReq}` + } startAccessor="start" endAccessor="end" onSelectEvent={handleSelectEvent} - style={{ + style={{ height: '95%', width: '95%', backgroundColor: 'white', @@ -318,17 +334,17 @@ export default function Schedule() { padding: 20, borderRadius: '1.5rem', }} + components={components} + defaultView="agenda" />
- + - -
) } @@ -557,7 +573,7 @@ function CreateEventDialog({ horses, instructors }: CreateEventDialogProps) { return ( -