Skip to content

Commit

Permalink
allow form to be used for both editing and creating events (#815)
Browse files Browse the repository at this point in the history
* allow form to be used for both editing and creating

* add tests

* fix paddingg on cards
  • Loading branch information
choden-dev authored Nov 2, 2024
1 parent 13817ab commit 84b1c9c
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react"
import AdminEventForm from "./AdminEventForm"
import { Timestamp } from "firebase/firestore"

const meta: Meta<typeof AdminEventForm> = {
component: AdminEventForm
Expand All @@ -9,3 +10,21 @@ export default meta
type Story = StoryObj<typeof meta>

export const DefaultAdminEventForm: Story = {}

export const DefaultAdminEventFormEditView: Story = {
args: {
isEditMode: true,
defaultData: {
title: "Goon Cave",
description:
"Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eaque officiis similique repellat accusantium eos dignissimos suscipit voluptatibus? Quia nobis repellendus quam, aliquid, veritatis eos fuga eligendi harum dolorum vero facere!",
location: "straight zhao's basement",
sign_up_start_date: Timestamp.fromDate(new Date(2024, 10, 4)),
sign_up_end_date: Timestamp.fromDate(new Date(2024, 11, 5)),
physical_start_date: Timestamp.fromDate(new Date(2024, 10, 3)),
physical_end_date: Timestamp.fromDate(new Date(2024, 11, 3)),
google_forms_link:
"https://www.reddit.com/r/VrGoonCave/comments/18gnrk8/welcome_to_vrgooncave/"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import {
EventMessages,
EventRenderingUtils
} from "@/components/generic/Event/EventUtils"
import Button from "@/components/generic/FigmaButtons/FigmaButton"
import TextInput from "@/components/generic/TextInputComponent/TextInput"
import { DateUtils } from "@/components/utils/DateUtils"
import { CreateEventBody } from "@/models/Events"
import { Timestamp } from "firebase/firestore"
import Image from "next/image"
import { FormEvent, useState } from "react"
import { FormEvent, useMemo, useState } from "react"

interface IAdminEventForm {
/**
Expand All @@ -23,6 +28,18 @@ interface IAdminEventForm {
* To be called after user submits the new data for the event
*/
handlePostEvent: (data: CreateEventBody) => void
/**
* If the panel should suggest that the event is being edited, instead of created
*
* Does the create mode if set to `false`
*/
isEditMode?: boolean
/**
* What to pre-fill the form with - _generally_ only to be used
* if {@link isEditMode} is set to true (which would have the previous
* values of the event.)
*/
defaultData?: CreateEventBody["data"]
}

export const AdminEventFormKeys = {
Expand All @@ -42,12 +59,20 @@ const Divider = () => <span className="bg-gray-3 my-3 h-[1px] w-full" />

const AdminEventForm = ({
handlePostEvent,
generateImageLink
generateImageLink,
isEditMode = false,
defaultData
}: IAdminEventForm) => {
const [isSubmitting, setIsSubmitting] = useState<boolean>(false)

const [uploadedImage, setUploadedImage] = useState<File | undefined>()

const formTitle = useMemo(
() =>
isEditMode ? `Edit Event ${defaultData?.title || ""}` : "Create Event",
[isEditMode, defaultData]
)

const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
const data = new FormData(e.currentTarget)
Expand Down Expand Up @@ -86,7 +111,7 @@ const AdminEventForm = ({

if (
confirm(
`Are you sure you want to create the new event with title ${body.title}?`
EventMessages.adminEventPostConfirmation(!!isEditMode, body.title)
)
) {
handlePostEvent({
Expand All @@ -102,7 +127,7 @@ 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">Create Event</h2>
<h2 className="text-dark-blue-100">{formTitle}</h2>
<form
onSubmit={handleSubmit}
className="flex w-full max-w-[800px] flex-col gap-2"
Expand All @@ -111,11 +136,13 @@ const AdminEventForm = ({
name={AdminEventFormKeys.TITLE}
type="text"
label="Title"
defaultValue={defaultData?.title}
data-testid={AdminEventFormKeys.TITLE}
required
/>
<label htmlFor={AdminEventFormKeys.DESCRIPTION}>Description</label>
<textarea
defaultValue={defaultData?.description}
name={AdminEventFormKeys.DESCRIPTION}
data-testid={AdminEventFormKeys.DESCRIPTION}
/>
Expand Down Expand Up @@ -146,6 +173,7 @@ const AdminEventForm = ({
name={AdminEventFormKeys.LOCATION}
type="text"
label="Location"
defaultValue={defaultData?.location}
data-testid={AdminEventFormKeys.LOCATION}
required
/>
Expand All @@ -156,12 +184,30 @@ const AdminEventForm = ({
name={AdminEventFormKeys.SIGN_UP_START_DATE}
data-testid={AdminEventFormKeys.SIGN_UP_START_DATE}
type="datetime-local"
value={
defaultData &&
EventRenderingUtils.dateTimeLocalPlaceHolder(
new Date(
DateUtils.timestampMilliseconds(
defaultData?.sign_up_start_date
)
)
)
}
label="Sign Up Start Date"
required
/>
<TextInput
name={AdminEventFormKeys.SIGN_UP_END_DATE}
data-testid={AdminEventFormKeys.SIGN_UP_END_DATE}
value={
defaultData?.sign_up_end_date &&
EventRenderingUtils.dateTimeLocalPlaceHolder(
new Date(
DateUtils.timestampMilliseconds(defaultData?.sign_up_end_date)
)
)
}
type="datetime-local"
label="Sign Up End Date (If exists)"
/>
Expand All @@ -173,13 +219,33 @@ const AdminEventForm = ({
<TextInput
name={AdminEventFormKeys.PHYSICAL_START_DATE}
data-testid={AdminEventFormKeys.PHYSICAL_START_DATE}
value={
defaultData?.physical_start_date &&
EventRenderingUtils.dateTimeLocalPlaceHolder(
new Date(
DateUtils.timestampMilliseconds(
defaultData?.physical_start_date
)
)
)
}
type="datetime-local"
label="Start Date of Event"
required
/>
<TextInput
name={AdminEventFormKeys.PHYSICAL_END_DATE}
data-testid={AdminEventFormKeys.PHYSICAL_END_DATE}
value={
defaultData?.physical_end_date &&
EventRenderingUtils.dateTimeLocalPlaceHolder(
new Date(
DateUtils.timestampMilliseconds(
defaultData?.physical_end_date
)
)
)
}
type="datetime-local"
label="End Date of Event"
/>
Expand All @@ -188,6 +254,7 @@ const AdminEventForm = ({
<TextInput
name={AdminEventFormKeys.GOOGLE_FORMS_LINK}
data-testid={AdminEventFormKeys.GOOGLE_FORMS_LINK}
defaultValue={defaultData?.google_forms_link}
type="url"
label="Google Forms Link"
/>
Expand All @@ -196,7 +263,7 @@ const AdminEventForm = ({
type="submit"
data-testid="post-event-button"
>
Add Event
{isEditMode ? "Update Event" : "Add Event"}
</Button>
</form>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const EventsCardPreview = ({
<div
className={`border-gray-3 navbar-shadow flex w-full flex-col items-center
justify-center gap-2 rounded-md border bg-white p-4 sm:flex-row
sm:px-10 sm:py-${variant === "admin" ? 10 : 12} ${isPastEvent && "brightness-50"}`}
sm:px-10 ${variant === "admin" ? "sm:py-10" : "sm:py-12"} ${isPastEvent && "brightness-50"}`}
>
<div className="border-gray-3 h-fit max-h-[150px] w-[200px] overflow-hidden border">
<Image
Expand Down
33 changes: 33 additions & 0 deletions client/src/components/generic/Event/EventUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { EventRenderingUtils } from "./EventUtils"

describe("dateTimeLocalPlaceHolder", () => {
it("should return a formatted string in ISO 8601 format without milliseconds", () => {
const date = new Date("2024-11-01T23:34:15.123Z")
const result = EventRenderingUtils.dateTimeLocalPlaceHolder(date)
expect(result).toBe("2024-11-01T23:34:15")
})

it("should handle dates without milliseconds correctly", () => {
const date = new Date("2024-11-01T23:34:15Z")
const result = EventRenderingUtils.dateTimeLocalPlaceHolder(date)
expect(result).toBe("2024-11-01T23:34:15")
})

it("should handle different time zones correctly", () => {
const date = new Date("2024-11-01T23:34:15.123+09:00")
const result = EventRenderingUtils.dateTimeLocalPlaceHolder(date)
expect(result).toBe("2024-11-01T14:34:15")
})

it("should handle leap years correctly", () => {
const date = new Date("2024-02-29T23:34:15.123Z")
const result = EventRenderingUtils.dateTimeLocalPlaceHolder(date)
expect(result).toBe("2024-02-29T23:34:15")
})

it("should handle dates before 1970 correctly", () => {
const date = new Date("1969-12-31T23:34:15.123Z")
const result = EventRenderingUtils.dateTimeLocalPlaceHolder(date)
expect(result).toBe("1969-12-31T23:34:15")
})
})
25 changes: 25 additions & 0 deletions client/src/components/generic/Event/EventUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ export const IMAGE_PLACEHOLDER_SRC =
* Static methods to format strings related to events
*/
export const EventMessages = {
/**
* Message to be displayed for confirming event creation or editing
*
* @param isEditing boolean indicating if the event is being edited
* @param title the title of the event
* @returns a formatted, user-readable string asking for confirmation
*/
adminEventPostConfirmation: (isEditing: boolean, title: string) => {
if (isEditing) {
return `Are you sure you want to edit the event ${title}?`
} else {
return `Are you sure you want to create the new event with title ${title}?`
}
},
/**
* Message to be displayed if sign ups have opened in relation to the sign up date
*
Expand Down Expand Up @@ -72,6 +86,17 @@ export const EventDateComparisons = {
} as const

export const EventRenderingUtils = {
/**
* Generates a placeholder string for a local date and time input field
*
* @param date the date object to be formatted
* @returns a formatted string in ISO 8601 format without milliseconds
*/
dateTimeLocalPlaceHolder: (date: Date) => {
const isoString = date.toISOString()
const placeholderString = isoString.substring(0, isoString.lastIndexOf("."))
return placeholderString
},
/**
* Utility function to convert a raw {@link Event} into {@link IEventsCardPreview}
*
Expand Down

0 comments on commit 84b1c9c

Please sign in to comment.