-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3cdec01
commit 4e27d9d
Showing
3 changed files
with
349 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { getEventById } from "db/functions"; | ||
import { redirect } from "next/navigation"; | ||
import EditEventForm from "@/components/events/admin/EditEventForm"; | ||
|
||
export default async function EditEventPage({ | ||
params, | ||
}: { | ||
params: { slug: string }; | ||
}) { | ||
const eventId = parseInt(params.slug); | ||
|
||
if (!eventId) { | ||
return <h1>Error: invalid event id</h1>; | ||
} | ||
|
||
const event = await getEventById(eventId); | ||
|
||
if (!event) { | ||
return redirect("/admin/events"); | ||
} | ||
|
||
return ( | ||
<div className="mx-auto max-w-3xl pt-32"> | ||
<div className="grid grid-cols-2"> | ||
<h1 className="text-3xl font-bold tracking-tight"> | ||
Edit Event | ||
</h1> | ||
</div> | ||
<div className="mt-2 rounded-xl border border-muted p-5"> | ||
<EditEventForm {...event} /> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,306 @@ | ||
"use client"; | ||
|
||
import { useForm } from "react-hook-form"; | ||
import { | ||
Form, | ||
FormField, | ||
FormItem, | ||
FormLabel, | ||
FormControl, | ||
FormDescription, | ||
FormMessage, | ||
} from "@/components/shadcn/ui/form"; | ||
import { | ||
Select, | ||
SelectTrigger, | ||
SelectContent, | ||
SelectGroup, | ||
SelectItem, | ||
SelectValue, | ||
} from "@/components/shadcn/ui/select"; | ||
import { Input } from "@/components/shadcn/ui/input"; | ||
import { Button } from "@/components/shadcn/ui/button"; | ||
import { z } from "zod"; | ||
import { zodResolver } from "@hookform/resolvers/zod"; | ||
import { Textarea } from "@/components/shadcn/ui/textarea"; | ||
import c from "config"; | ||
import { DateTimePicker } from "@/components/shadcn/ui/date-time-picker/date-time-picker"; | ||
import { parseAbsolute, getLocalTimeZone } from "@internationalized/date"; | ||
import { useCallback, useState } from "react"; | ||
import { useRouter } from "next/navigation"; | ||
import { ONE_HOUR_IN_MILLISECONDS } from "@/lib/constants"; | ||
import { type eventEditType } from "@/lib/types/events"; | ||
import { newEventFormSchema } from "@/validators/event"; | ||
import { ThreeCircles } from "react-loader-spinner"; | ||
import { useAction } from "next-safe-action/hooks"; | ||
import { editEvent } from "@/actions/admin/event-actions"; | ||
import { toast } from "sonner"; | ||
|
||
export default function EditEventForm({ | ||
id, | ||
title, | ||
description, | ||
type, | ||
host, | ||
endTime, | ||
points, | ||
startTime, | ||
location, | ||
}: eventEditType & { id: number }) { | ||
const [loading, setLoading] = useState(false); | ||
const router = useRouter(); | ||
const userLocalTimeZone = getLocalTimeZone(); | ||
const { execute } = useAction(editEvent, { | ||
onExecute: () => setLoading(true), | ||
onSettled: () => setLoading(false), | ||
onSuccess: () => { | ||
alert("Event Edited Successfully! Redirecting to event page..."); | ||
router.push("/admin/events"); | ||
}, | ||
onError: ({ error }) => { | ||
const description = | ||
error.serverError || | ||
"An error occurred while validating the form data"; | ||
|
||
toast.error("Unable to edit event", { description }); | ||
}, | ||
}); | ||
|
||
const form = useForm<z.infer<typeof newEventFormSchema>>({ | ||
resolver: zodResolver(newEventFormSchema), | ||
defaultValues: { | ||
id, | ||
title, | ||
description, | ||
type: type as any, | ||
host, | ||
startTime, | ||
points, | ||
location, | ||
endTime, | ||
}, | ||
}); | ||
|
||
const onSubmit = useCallback( | ||
(values: z.infer<typeof newEventFormSchema>) => { | ||
if (form.formState.isDirty) { | ||
execute(values); | ||
} else { | ||
toast.error("Failed to edit event", { | ||
description: | ||
"Please make changes to the form before submitting it!", | ||
}); | ||
} | ||
}, | ||
[form.formState.isDirty], | ||
); | ||
|
||
return ( | ||
<Form {...form}> | ||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4"> | ||
<FormField | ||
control={form.control} | ||
name="title" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Title</FormLabel> | ||
<FormControl> | ||
<Input {...field} /> | ||
</FormControl> | ||
<FormDescription> | ||
Keep title short and concise | ||
</FormDescription> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
|
||
<FormField | ||
control={form.control} | ||
name="description" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Description</FormLabel> | ||
<FormControl> | ||
<Textarea {...field} /> | ||
</FormControl> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<FormField | ||
control={form.control} | ||
name="location" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Location</FormLabel> | ||
<FormControl> | ||
<Input | ||
{...field} | ||
value={field.value ?? "TBD"} | ||
/> | ||
</FormControl> | ||
{/* <FormDescription> | ||
Keep title short and concise | ||
</FormDescription> */} | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<div className="grid grid-cols-2 gap-x-2"> | ||
<FormField | ||
control={form.control} | ||
name="type" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Event Type</FormLabel> | ||
<Select | ||
onValueChange={field.onChange} | ||
defaultValue={field.value} | ||
> | ||
<FormControl> | ||
<SelectTrigger className="w-full"> | ||
<SelectValue placeholder="Select a Event Type" /> | ||
</SelectTrigger> | ||
</FormControl> | ||
<SelectContent> | ||
<SelectGroup> | ||
{Object.keys(c.eventTypes).map( | ||
(type) => ( | ||
<SelectItem | ||
key={type} | ||
value={type} | ||
> | ||
{type} | ||
</SelectItem> | ||
), | ||
)} | ||
</SelectGroup> | ||
</SelectContent> | ||
</Select> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<FormField | ||
control={form.control} | ||
name="host" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Host (Optional)</FormLabel> | ||
<FormControl> | ||
<Input | ||
placeholder={c.hackathonName} | ||
{...(field as any)} | ||
/> | ||
</FormControl> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
</div> | ||
<div className="grid grid-cols-2 gap-x-2"> | ||
<FormField | ||
control={form.control} | ||
name="startTime" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Event Start</FormLabel> | ||
<DateTimePicker | ||
value={ | ||
field.value | ||
? parseAbsolute( | ||
field.value.toISOString(), | ||
userLocalTimeZone, | ||
) | ||
: null | ||
} | ||
onChange={(date) => { | ||
const newDate = date | ||
? date.toDate(userLocalTimeZone) | ||
: null; | ||
field.onChange(newDate); | ||
const isEventStartBeforeEnd = | ||
newDate && | ||
newDate > form.getValues("endTime"); | ||
if (isEventStartBeforeEnd) { | ||
form.setValue( | ||
"endTime", | ||
new Date( | ||
newDate.getTime() + | ||
ONE_HOUR_IN_MILLISECONDS, | ||
), | ||
); | ||
} | ||
}} | ||
shouldCloseOnSelect={false} | ||
granularity={"minute"} | ||
label="Event Start" | ||
/> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<FormField | ||
control={form.control} | ||
name="endTime" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Event End</FormLabel> | ||
<DateTimePicker | ||
value={ | ||
!!field.value | ||
? parseAbsolute( | ||
field.value.toISOString(), | ||
userLocalTimeZone, | ||
) | ||
: null | ||
} | ||
onChange={(date) => { | ||
const newDate = !!date | ||
? date.toDate(userLocalTimeZone) | ||
: null; | ||
field.onChange(newDate); | ||
const isEventEndBeforeStart = | ||
newDate && | ||
newDate < | ||
form.getValues("startTime"); | ||
if (isEventEndBeforeStart) { | ||
form.setValue( | ||
"startTime", | ||
new Date( | ||
newDate.getTime() - | ||
ONE_HOUR_IN_MILLISECONDS, | ||
), | ||
); | ||
} | ||
}} | ||
shouldCloseOnSelect={false} | ||
granularity={"minute"} | ||
label="Event End" | ||
/> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
</div> | ||
{loading ? ( | ||
<p className="flex justify-center gap-x-1"> | ||
Updating Event{" "} | ||
<ThreeCircles | ||
visible={true} | ||
height="20" | ||
width="20" | ||
color="#4fa94d" | ||
ariaLabel="three-circles-loading" | ||
wrapperStyle={{}} | ||
wrapperClass="" | ||
/> | ||
</p> | ||
) : ( | ||
<Button type="submit">Edit Event</Button> | ||
)} | ||
</form> | ||
</Form> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters