"use client" import { useEffect, useMemo, useRef, useState } from "react" import { zodResolver } from "@hookform/resolvers/zod" import { Controller, type Resolver, useForm } from "react-hook-form" import { toast } from "sonner" import { z } from "zod/v4" import { OptionCombobox } from "@/components/option-combobox" 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 { Textarea } from "@/components/ui/textarea" import { fetchAgentProfilesAll, type AdminAgentProfile, } from "@/lib/api/admin" import { useI18n } from "@/i18n/provider" import { assignTicket } from "@/lib/api/ticket" type TFunction = (key: string, values?: Record) => string function createSchema(t: TFunction) { return z.object({ toUserId: z.string().trim().min(1, t("ticket.assigneeRequired")), reason: z.string().trim(), }) } type FormValues = { toUserId: string reason: string } const emptyForm: FormValues = { toUserId: "", reason: "", } type TicketAssignDialogProps = { open: boolean ticketId: number | null currentAssigneeId?: number onOpenChange: (open: boolean) => void onSuccess?: () => Promise | void } export function TicketAssignDialog({ open, ticketId, currentAssigneeId, onOpenChange, onSuccess, }: TicketAssignDialogProps) { return ( {open ? ( ) : null} ) } function TicketAssignDialogBody({ ticketId, currentAssigneeId, onOpenChange, onSuccess, }: Omit) { const t = useI18n() const [saving, setSaving] = useState(false) const [loading, setLoading] = useState(false) const [agents, setAgents] = useState([]) const activeRef = useRef(false) const schema = useMemo(() => createSchema(t), [t]) const resolver = useMemo( () => zodResolver(schema) as Resolver, [schema], ) const form = useForm({ resolver, defaultValues: emptyForm, }) const { control, handleSubmit, register, reset, formState: { errors }, } = form useEffect(() => { activeRef.current = true return () => { activeRef.current = false } }, []) useEffect(() => { reset({ toUserId: currentAssigneeId ? String(currentAssigneeId) : "", reason: "", }) }, [currentAssigneeId, reset, ticketId]) useEffect(() => { setLoading(true) fetchAgentProfilesAll() .then((agentData) => { if (!activeRef.current) { return } setAgents(Array.isArray(agentData) ? agentData : []) }) .catch((error) => { if (!activeRef.current) { return } toast.error(error instanceof Error ? error.message : t("ticket.loadAssigneesFailed")) }) .finally(() => { if (activeRef.current) { setLoading(false) } }) }, [t]) async function onFormSubmit(values: FormValues) { if (!ticketId) { toast.error(t("ticket.selectTicket")) return } setSaving(true) try { await assignTicket({ ticketId, toUserId: Number(values.toUserId), reason: values.reason.trim() || undefined, }) if (!activeRef.current) { return } toast.success(t("ticket.assigneeUpdated")) if (!activeRef.current) { return } onOpenChange(false) await onSuccess?.() } catch (error) { if (!activeRef.current) { return } toast.error(error instanceof Error ? error.message : t("ticket.assignFailed")) } finally { if (activeRef.current) { setSaving(false) } } } return ( {t("ticket.assignTitle")}
{t("ticket.assignee")} ( ({ value: String(agent.userId), label: agent.displayName || agent.nickname || agent.username || t("ticket.agentFallback", { id: agent.userId }), }))} /> )} /> {t("ticket.assignReason")}