mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 12:49:00 +00:00
289 lines
9.2 KiB
TypeScript
289 lines
9.2 KiB
TypeScript
import { useEffect } from "react";
|
|
import { Controller, useForm, useWatch } from "react-hook-form";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { useNavigate, useParams, useSearchParams } from "react-router";
|
|
import {
|
|
Alert,
|
|
Box,
|
|
Button,
|
|
Checkbox,
|
|
CircularProgress,
|
|
FormControl,
|
|
FormControlLabel,
|
|
InputLabel,
|
|
MenuItem,
|
|
Paper,
|
|
Select,
|
|
Stack,
|
|
TextField,
|
|
Typography,
|
|
} from "@mui/material";
|
|
import {
|
|
CALENDAR_REMINDER_OPTIONS,
|
|
CALENDAR_TIME_OPTIONS,
|
|
} from "@/components/common/calendar/calendar-utils";
|
|
import {
|
|
calendarEventSchema,
|
|
type CalendarEventFormValues,
|
|
} from "@/domain/calendar/schemas/calendar-event-schema";
|
|
import { useCalendarEventDetail } from "@/domain/calendar/use-cases/use-calendar-event-detail";
|
|
import { useCreateCalendarEvent } from "@/domain/calendar/use-cases/use-create-calendar-event";
|
|
import { useUpdateCalendarEvent } from "@/domain/calendar/use-cases/use-update-calendar-event";
|
|
import { toDateInputValue } from "@/lib/time-utils";
|
|
|
|
const COLORS = ["primary", "success", "info", "warning", "danger"] as const;
|
|
|
|
const defaultValues: CalendarEventFormValues = {
|
|
title: "",
|
|
description: "",
|
|
location: "",
|
|
recordId: null,
|
|
recordName: "",
|
|
recordTargetType: "",
|
|
reminderMinutes: 15,
|
|
eventColor: "primary",
|
|
startDate: new Date().toISOString().split("T")[0],
|
|
startTime: "08:00 AM",
|
|
endDate: new Date().toISOString().split("T")[0],
|
|
endTime: "08:30 AM",
|
|
allDay: false,
|
|
};
|
|
|
|
export default function EventFormPage() {
|
|
const { id } = useParams();
|
|
const [searchParams] = useSearchParams();
|
|
const navigate = useNavigate();
|
|
const isEdit = Boolean(id);
|
|
|
|
const { data: event, isLoading, error } = useCalendarEventDetail(id);
|
|
const createEvent = useCreateCalendarEvent();
|
|
const updateEvent = useUpdateCalendarEvent();
|
|
|
|
const {
|
|
register,
|
|
handleSubmit,
|
|
reset,
|
|
control,
|
|
formState: { errors },
|
|
} = useForm<CalendarEventFormValues>({
|
|
resolver: zodResolver(calendarEventSchema),
|
|
defaultValues: {
|
|
...defaultValues,
|
|
startDate: searchParams.get("date") ?? defaultValues.startDate,
|
|
endDate: searchParams.get("date") ?? defaultValues.endDate,
|
|
},
|
|
});
|
|
|
|
const allDay = useWatch({ control, name: "allDay" });
|
|
|
|
useEffect(() => {
|
|
if (event && isEdit) {
|
|
reset({
|
|
title: event.title,
|
|
description: event.description,
|
|
location: event.location,
|
|
recordId: event.recordId,
|
|
recordName: event.recordName,
|
|
recordTargetType: event.recordTargetType,
|
|
reminderMinutes: event.reminderMinutes,
|
|
eventColor: event.eventColor,
|
|
startDate: toDateInputValue(event.startDate),
|
|
startTime: event.startTime || "08:00 AM",
|
|
endDate: toDateInputValue(event.endDate),
|
|
endTime: event.endTime || "08:30 AM",
|
|
allDay: event.allDay,
|
|
});
|
|
}
|
|
}, [event, isEdit, reset]);
|
|
|
|
const isSaving = createEvent.isPending || updateEvent.isPending;
|
|
|
|
const onSubmit = (values: CalendarEventFormValues) => {
|
|
if (isEdit && id) {
|
|
updateEvent.mutate({ id, values });
|
|
return;
|
|
}
|
|
createEvent.mutate(values);
|
|
};
|
|
|
|
if (isEdit && isLoading) {
|
|
return (
|
|
<Box className="flex justify-center p-10">
|
|
<CircularProgress />
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Box className="mx-auto max-w-3xl p-4">
|
|
<Paper variant="outlined" className="p-6">
|
|
<Stack spacing={3}>
|
|
<Typography variant="h5">{isEdit ? "Edit Event" : "Create Event"}</Typography>
|
|
|
|
{Boolean(error) && <Alert severity="error">Failed to load event</Alert>}
|
|
|
|
<Box component="form" onSubmit={handleSubmit(onSubmit)}>
|
|
<Stack spacing={3}>
|
|
<TextField
|
|
label="Title"
|
|
required
|
|
fullWidth
|
|
error={Boolean(errors.title)}
|
|
helperText={errors.title?.message}
|
|
{...register("title")}
|
|
slotProps={{
|
|
htmlInput: { maxLength: 100 },
|
|
}}
|
|
/>
|
|
|
|
<TextField
|
|
label="Description"
|
|
fullWidth
|
|
multiline
|
|
rows={3}
|
|
{...register("description")}
|
|
/>
|
|
|
|
<TextField
|
|
label="Location"
|
|
fullWidth
|
|
{...register("location")}
|
|
slotProps={{
|
|
htmlInput: { maxLength: 150 },
|
|
}}
|
|
/>
|
|
|
|
<Controller
|
|
name="reminderMinutes"
|
|
control={control}
|
|
render={({ field }) => (
|
|
<FormControl fullWidth>
|
|
<InputLabel>Remind Attendees</InputLabel>
|
|
<Select
|
|
label="Remind Attendees"
|
|
value={field.value}
|
|
onChange={(event) => field.onChange(Number(event.target.value))}
|
|
>
|
|
{CALENDAR_REMINDER_OPTIONS.map((option) => (
|
|
<MenuItem key={option.value} value={option.value}>
|
|
{option.label}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
</FormControl>
|
|
)}
|
|
/>
|
|
|
|
<Controller
|
|
name="eventColor"
|
|
control={control}
|
|
render={({ field }) => (
|
|
<FormControl fullWidth>
|
|
<InputLabel>Calendar Color</InputLabel>
|
|
<Select label="Calendar Color" value={field.value} onChange={field.onChange}>
|
|
{COLORS.map((color) => (
|
|
<MenuItem key={color} value={color}>
|
|
{color}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
</FormControl>
|
|
)}
|
|
/>
|
|
|
|
{!allDay && (
|
|
<Stack direction={{ xs: "column", sm: "row" }} spacing={2}>
|
|
<TextField
|
|
label="Start Date"
|
|
type="date"
|
|
fullWidth
|
|
slotProps={{ inputLabel: { shrink: true } }}
|
|
error={Boolean(errors.startDate)}
|
|
helperText={errors.startDate?.message}
|
|
{...register("startDate")}
|
|
/>
|
|
<Controller
|
|
name="startTime"
|
|
control={control}
|
|
render={({ field }) => (
|
|
<FormControl fullWidth>
|
|
<InputLabel>Start Time</InputLabel>
|
|
<Select label="Start Time" value={field.value} onChange={field.onChange}>
|
|
{CALENDAR_TIME_OPTIONS.map((time) => (
|
|
<MenuItem key={time} value={time}>
|
|
{time}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
</FormControl>
|
|
)}
|
|
/>
|
|
</Stack>
|
|
)}
|
|
|
|
{!allDay && (
|
|
<Stack direction={{ xs: "column", sm: "row" }} spacing={2}>
|
|
<TextField
|
|
label="End Date"
|
|
type="date"
|
|
fullWidth
|
|
slotProps={{ inputLabel: { shrink: true } }}
|
|
error={Boolean(errors.endDate)}
|
|
helperText={errors.endDate?.message}
|
|
{...register("endDate")}
|
|
/>
|
|
<Controller
|
|
name="endTime"
|
|
control={control}
|
|
render={({ field }) => (
|
|
<FormControl fullWidth>
|
|
<InputLabel>End Time</InputLabel>
|
|
<Select label="End Time" value={field.value} onChange={field.onChange}>
|
|
{CALENDAR_TIME_OPTIONS.map((time) => (
|
|
<MenuItem key={time} value={time}>
|
|
{time}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
</FormControl>
|
|
)}
|
|
/>
|
|
</Stack>
|
|
)}
|
|
|
|
<Controller
|
|
name="allDay"
|
|
control={control}
|
|
render={({ field }) => (
|
|
<FormControlLabel
|
|
control={<Checkbox checked={field.value} onChange={field.onChange} />}
|
|
label="All day event"
|
|
/>
|
|
)}
|
|
/>
|
|
|
|
<Stack
|
|
direction="row"
|
|
spacing={2}
|
|
sx={{
|
|
justifyContent: "flex-end",
|
|
}}
|
|
>
|
|
<Button
|
|
variant="outlined"
|
|
onClick={() => navigate("/calendar")}
|
|
disabled={isSaving}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button type="submit" variant="contained" disabled={isSaving}>
|
|
{isSaving ? "Saving..." : isEdit ? "Update" : "Create"}
|
|
</Button>
|
|
</Stack>
|
|
</Stack>
|
|
</Box>
|
|
</Stack>
|
|
</Paper>
|
|
</Box>
|
|
);
|
|
}
|