Skip to content

Commit

Permalink
816 frontend implement editing functionality for events (#818)
Browse files Browse the repository at this point in the history
* working implementation of presentation

* working network implementation

* fix route

* add loader

* fix freezes

* fix timezone issues

* add placeholder for deleting event

* fix sign up logic

* fix tests

* fix tests
  • Loading branch information
choden-dev authored Nov 3, 2024
1 parent def9819 commit 0ded76a
Show file tree
Hide file tree
Showing 15 changed files with 279 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import EventsCardPreview, {
IEventsCardPreview
} from "@/components/generic/Event/EventPreview/EventPreview"
import EventsCardPreview from "@/components/generic/Event/EventPreview/EventPreview"
import { DateUtils } from "@/components/utils/DateUtils"
import { Event } from "@/models/Events"
import { useCallback, useMemo, useState } from "react"
Expand Down Expand Up @@ -127,7 +125,7 @@ const AdminAllEvents = ({
/**
* Detailed view of the event
*/
const previewCurrentEvents: IEventsCardPreview[] =
const previewCurrentEvents =
eventList.upcomingAndCurrentEvents?.map((event) => {
return EventRenderingUtils.previewTransformer(
event,
Expand All @@ -137,7 +135,7 @@ const AdminAllEvents = ({
)
}) || []

const previewPastEvents: IEventsCardPreview[] =
const previewPastEvents =
eventList.pastEvents?.map((event) => {
return EventRenderingUtils.previewTransformer(
event,
Expand All @@ -164,11 +162,11 @@ const AdminAllEvents = ({
</h5>
)}
{previewCurrentEvents.map((event) => (
<EventsCardPreview key={event.title} {...event} />
<EventsCardPreview {...event} key={event.key} />
))}

{previewPastEvents.map((event) => (
<EventsCardPreview key={event.title} {...event} />
<EventsCardPreview {...event} key={event.key} />
))}
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,18 @@ interface IAdminEventForm {

/**
* To be called after user submits the new data for the event
*
* (the big call to action button)
*/
handlePostEvent: (data: CreateEventBody) => void

/**
* Handler which is passed in if {@link isEditMode} is `true`,
*
* if the user confirms they want the event deleted this handler will be called
*/
handleDeleteEvent?: () => void

/**
* If the panel should suggest that the event is being edited, instead of created
*
Expand All @@ -42,6 +52,18 @@ interface IAdminEventForm {
defaultData?: CreateEventBody["data"]
}

const USER_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone

const TimezoneIndicator = () => (
<>
{USER_TIMEZONE && (
<h5 className="text-center font-bold uppercase">
The timezone is: {USER_TIMEZONE}
</h5>
)}
</>
)

export const AdminEventFormKeys = {
TITLE: "title",
DESCRIPTION: "description",
Expand All @@ -61,7 +83,8 @@ const AdminEventForm = ({
handlePostEvent,
generateImageLink,
isEditMode = false,
defaultData
defaultData,
handleDeleteEvent
}: IAdminEventForm) => {
const [isSubmitting, setIsSubmitting] = useState<boolean>(false)

Expand Down Expand Up @@ -102,7 +125,11 @@ const AdminEventForm = ({
Number.parseInt(data.get(AdminEventFormKeys.MAX_OCCUPANCY) as string) ||
undefined,
physical_end_date: physical_end_date
? Timestamp.fromDate(new Date(physical_end_date as string))
? Timestamp.fromDate(
new Date(
(physical_end_date as string).replace(/-/g, "/").replace("T", " ")
)
)
: undefined
}

Expand All @@ -128,6 +155,20 @@ const AdminEventForm = ({
return (
<div className="relative my-4 flex w-full flex-col items-center rounded-md bg-white p-2">
<h2 className="text-dark-blue-100">{formTitle}</h2>
{isEditMode && (
<button
className="mt-2"
onClick={() => {
confirm(
EventMessages.adminDeleteEventConfirmation(
defaultData?.title || ""
)
) && handleDeleteEvent?.()
}}
>
<h5 className="text-red font-bold uppercase">Delete Event</h5>
</button>
)}
<form
onSubmit={handleSubmit}
className="flex w-full max-w-[800px] flex-col gap-2"
Expand Down Expand Up @@ -184,7 +225,7 @@ const AdminEventForm = ({
name={AdminEventFormKeys.SIGN_UP_START_DATE}
data-testid={AdminEventFormKeys.SIGN_UP_START_DATE}
type="datetime-local"
value={
defaultValue={
defaultData &&
EventRenderingUtils.dateTimeLocalPlaceHolder(
new Date(
Expand All @@ -200,7 +241,7 @@ const AdminEventForm = ({
<TextInput
name={AdminEventFormKeys.SIGN_UP_END_DATE}
data-testid={AdminEventFormKeys.SIGN_UP_END_DATE}
value={
defaultValue={
defaultData?.sign_up_end_date &&
EventRenderingUtils.dateTimeLocalPlaceHolder(
new Date(
Expand All @@ -212,14 +253,14 @@ const AdminEventForm = ({
label="Sign Up End Date (If exists)"
/>
</span>

<TimezoneIndicator />
<Divider />
<h3 className="text-dark-blue-100 mt-2 text-center">Event Dates</h3>
<span className="flex w-full flex-col gap-2 sm:flex-row">
<TextInput
name={AdminEventFormKeys.PHYSICAL_START_DATE}
data-testid={AdminEventFormKeys.PHYSICAL_START_DATE}
value={
defaultValue={
defaultData?.physical_start_date &&
EventRenderingUtils.dateTimeLocalPlaceHolder(
new Date(
Expand All @@ -236,7 +277,7 @@ const AdminEventForm = ({
<TextInput
name={AdminEventFormKeys.PHYSICAL_END_DATE}
data-testid={AdminEventFormKeys.PHYSICAL_END_DATE}
value={
defaultValue={
defaultData?.physical_end_date &&
EventRenderingUtils.dateTimeLocalPlaceHolder(
new Date(
Expand All @@ -250,6 +291,7 @@ const AdminEventForm = ({
label="End Date of Event"
/>
</span>
<TimezoneIndicator />
<Divider />
<TextInput
name={AdminEventFormKeys.GOOGLE_FORMS_LINK}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ export const AdminEventViewWithEvents: Story = {
generateImageLink: async () => {
return undefined
},
eventPreviousData: {
id: "1",
title: "CACK",
location: "STRAIGHT ZHAO",
physical_start_date: earlierStartDate,
physical_end_date: earlierStartDate,
sign_up_start_date: earlierStartDate,
sign_up_end_date: earlierStartDate,
google_forms_link: "https://google.com",
description: "default data"
},
rawEvents: [
{
id: "1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import Button from "@/components/generic/FigmaButtons/FigmaButton"
import { CreateEventBody, Event } from "@/models/Events"
import { CreateEventBody, EditEventBody, Event } from "@/models/Events"
import { useState } from "react"
import AdminEventForm from "./AdminEventForm/AdminEventForm"
import AdminAllEvents from "./AdminAllEvents/AdminAllEvents"
import Loader from "@/components/generic/SuspenseComponent/Loader"

type EventViewModes = "view-all-events" | "creating-new-event" | "editing-event"

Expand Down Expand Up @@ -44,6 +45,22 @@ interface IAdminEventView {
* Function to fetch more events.
*/
fetchMoreEvents?: () => void

/**
* If passed in, will open the edit panel for the event with given data
*/
eventPreviousData?: Event

/**
* Will be called when the admin is _editing_ a selected event
*/
handleEditEvent?: (eventId: string, newData: EditEventBody) => void

/**
* Obtains the latest data for an event to edit, if `undefined` is passed
* in then it means that no event should be edited
*/
fetchEventToEdit?: (eventId?: string) => void
}

const AdminEventViewContent = ({
Expand All @@ -54,16 +71,28 @@ const AdminEventViewContent = ({
rawEvents,
hasMoreEvents,
isLoading,
fetchMoreEvents
// TODO: extend with the event id to allow showing an edit view
fetchMoreEvents,
handleEditEvent,
eventPreviousData,
fetchEventToEdit
}: {
mode: EventViewModes
setMode: (mode: EventViewModes) => void
} & IAdminEventView) => {
/**
* Used to make the `PATCH` request for the event (need to specify path with `id`)
*/
const [editedEventId, setEditedEventId] = useState<string | undefined>()

switch (mode) {
case "view-all-events":
return (
<AdminAllEvents
onSelectedEventIdChange={(id) => {
setEditedEventId(id)
fetchEventToEdit?.(id)
setMode("editing-event")
}}
rawEvents={rawEvents}
hasMoreEvents={hasMoreEvents}
isLoading={isLoading}
Expand All @@ -83,7 +112,28 @@ const AdminEventViewContent = ({
/>
)
case "editing-event":
return null
if (!editedEventId) {
setMode("view-all-events")
return <Loader />
}

if (!eventPreviousData) {
return <Loader />
}

return (
<AdminEventForm
generateImageLink={async (image) => {
return await generateImageLink(image)
}}
defaultData={eventPreviousData}
handlePostEvent={async (data) => {
await handleEditEvent?.(editedEventId, data.data)
setMode("view-all-events")
}}
isEditMode
/>
)
}
}

Expand All @@ -95,6 +145,7 @@ const buttonMessage = (mode: EventViewModes) => {
case "view-all-events":
return "Create Event"
case "creating-new-event":
return "Back to Events"
case "editing-event":
return "Back to Events"
}
Expand All @@ -110,7 +161,10 @@ const AdminEventView = ({
rawEvents = [],
hasMoreEvents,
isLoading,
fetchMoreEvents
fetchMoreEvents,
eventPreviousData,
handleEditEvent,
fetchEventToEdit
}: IAdminEventView) => {
const [mode, setMode] = useState<EventViewModes>("view-all-events")

Expand All @@ -127,7 +181,10 @@ const AdminEventView = ({
setMode("creating-new-event")
break
case "creating-new-event":
setMode("view-all-events")
break
case "editing-event":
fetchEventToEdit?.()
setMode("view-all-events")
}
}}
Expand All @@ -136,14 +193,18 @@ const AdminEventView = ({
</Button>
</div>
</span>
{/** TODO: pass in delete handler */}
<AdminEventViewContent
setMode={setMode}
mode={mode}
fetchEventToEdit={fetchEventToEdit}
handleEditEvent={handleEditEvent}
handlePostEvent={handlePostEvent}
generateImageLink={generateImageLink}
rawEvents={rawEvents}
hasMoreEvents={hasMoreEvents}
isLoading={isLoading}
eventPreviousData={eventPreviousData}
fetchMoreEvents={fetchMoreEvents}
/>
</div>
Expand Down
Loading

0 comments on commit 0ded76a

Please sign in to comment.