"use client" import { zodResolver } from "@hookform/resolvers/zod" import { useCallback, useEffect, useMemo, useState } from "react" import { Controller, type Resolver, useForm } from "react-hook-form" import { toast } from "sonner" import { z } from "zod/v4" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Field, FieldContent, FieldError, FieldLabel, } from "@/components/ui/field" import { Input } from "@/components/ui/input" import { OptionCombobox } from "@/components/option-combobox" import { Textarea } from "@/components/ui/textarea" import { type AdminAgentTeam, type AdminAgentTeamSchedule, type CreateAdminAgentTeamSchedulePayload, fetchAgentTeamSchedule, fetchAgentTeamsAll, } from "@/lib/api/admin" import { useI18n } from "@/i18n/provider" type TFunction = (key: string, values?: Record) => string type ScheduleEditDialogProps = { open: boolean saving: boolean itemId: number | null defaultValues?: Partial | null onOpenChange: (open: boolean) => void onSubmit: (payload: CreateAdminAgentTeamSchedulePayload) => Promise onDelete?: (id: number) => Promise } const emptyForm: EditForm = { teamId: "", startAt: "", endAt: "", remark: "", } type EditForm = { teamId: string startAt: string endAt: string remark: string } function createEditFormSchema(t: TFunction) { return z.object({ teamId: z.string().trim().regex(/^\d+$/, t("agentTeamSchedule.teamRequired")), startAt: z.string().trim().min(1, t("agentTeamSchedule.startRequired")), endAt: z.string().trim().min(1, t("agentTeamSchedule.endRequired")), remark: z.string().trim(), }).superRefine((value, ctx) => { const startAt = parseDateTimeLocal(value.startAt) const endAt = parseDateTimeLocal(value.endAt) if (!startAt || !endAt) { return } if (!endAt || endAt <= startAt) { ctx.addIssue({ code: "custom", path: ["endAt"], message: t("agentTeamSchedule.endAfterStart"), }) return } if (!isSameLocalDay(startAt, endAt)) { ctx.addIssue({ code: "custom", path: ["endAt"], message: t("agentTeamSchedule.singleDayOnly"), }) } if (startAt < startOfLocalDay(new Date())) { ctx.addIssue({ code: "custom", path: ["startAt"], message: t("agentTeamSchedule.historyReadonly"), }) } }) } function toDateTimeLocal(value?: string) { if (!value) { return "" } return value.replace(" ", "T").slice(0, 16) } function parseDateTimeLocal(value: string) { const ret = new Date(value) return Number.isNaN(ret.getTime()) ? null : ret } function startOfLocalDay(value: Date) { const ret = new Date(value) ret.setHours(0, 0, 0, 0) return ret } function isSameLocalDay(a: Date, b: Date) { return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate() } function todayDateTimeLocalMin() { const today = startOfLocalDay(new Date()) const month = String(today.getMonth() + 1).padStart(2, "0") const day = String(today.getDate()).padStart(2, "0") return `${today.getFullYear()}-${month}-${day}T00:00` } function buildForm(item: AdminAgentTeamSchedule | null, defaultValues?: Partial | null): EditForm { if (!item) { return { teamId: defaultValues?.teamId ? String(defaultValues.teamId) : emptyForm.teamId, startAt: toDateTimeLocal(defaultValues?.startAt), endAt: toDateTimeLocal(defaultValues?.endAt), remark: defaultValues?.remark ?? emptyForm.remark, } } return { teamId: String(item.teamId), startAt: toDateTimeLocal(item.startAt), endAt: toDateTimeLocal(item.endAt), remark: item.remark || "", } } function buildPayload(form: EditForm): CreateAdminAgentTeamSchedulePayload { return { teamId: Number(form.teamId), startAt: form.startAt.trim(), endAt: form.endAt.trim(), remark: form.remark.trim(), } } export function EditDialog({ open, saving, itemId, defaultValues, onOpenChange, onSubmit, onDelete, }: ScheduleEditDialogProps) { return ( {open ? ( ) : null} ) } type ScheduleEditDialogBodyProps = Omit function ScheduleEditDialogBody({ saving, itemId, defaultValues, onOpenChange, onSubmit, onDelete, }: ScheduleEditDialogBodyProps) { const t = useI18n() const [teams, setTeams] = useState([]) const [loading, setLoading] = useState(false) const loadOptions = useCallback(async () => { try { const teamsData = await fetchAgentTeamsAll() setTeams(teamsData) } catch (error) { toast.error(error instanceof Error ? error.message : t("agentTeamSchedule.loadOptionsFailed")) } }, [t]) const editFormSchema = useMemo(() => createEditFormSchema(t), [t]) const editFormResolver = useMemo( () => zodResolver(editFormSchema) as Resolver, [editFormSchema], ) const form = useForm({ resolver: editFormResolver, defaultValues: emptyForm, }) const { control, handleSubmit, reset, register, formState: { errors }, } = form const minDateTime = todayDateTimeLocalMin() useEffect(() => { async function loadDetail() { if (!itemId) { reset(buildForm(null, defaultValues)) return } setLoading(true) try { const data = await fetchAgentTeamSchedule(itemId) reset(buildForm(data)) } catch (error) { toast.error(error instanceof Error ? error.message : t("agentTeamSchedule.loadDetailFailed")) } finally { setLoading(false) } } void loadDetail() }, [defaultValues, itemId, reset, t]) useEffect(() => { void loadOptions() }, [loadOptions]) async function onFormSubmit(values: EditForm) { await onSubmit(buildPayload(values)) } return ( {itemId ? t("agentTeamSchedule.editTitle") : t("agentTeamSchedule.createTitle")} {loading ? (
{t("agentTeamSchedule.loading")}
) : (
{t("agentTeamSchedule.team")} ( ({ value: String(team.id), label: team.name, }))} placeholder={t("agentTeamSchedule.teamRequired")} searchPlaceholder={t("agentTeamSchedule.searchTeam")} emptyText={t("agentTeamSchedule.emptyTeam")} onChange={field.onChange} /> )} />
{t("agentTeamSchedule.startTime")} {t("agentTeamSchedule.endTime")}
{t("agentTeamSchedule.remark")}