import { useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { z } from 'zod'; import { Loader2 } from 'lucide-react'; import { toast } from 'sonner'; import { Button, Checkbox, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Skeleton, Textarea, } from '@vector/ui'; import { createRepair } from '../../lib/api/repairs.js'; import { listHosts, listHostDeployedParts } from '../../lib/api/hosts.js'; import { ApiRequestError } from '../../lib/api/client.js'; import { queryKeys } from '../../lib/queryKeys.js'; import type { RepairJob } from '../../lib/api/types.js'; const CreateSchema = z.object({ hostId: z.string().uuid('Pick a host'), problem: z.string().trim().min(1, 'Describe the problem').max(2000), problemPartIds: z.array(z.string().uuid()).max(100), notes: z.string().max(4096).optional(), }); type CreateValues = z.infer; interface RepairFormDialogProps { open: boolean; onOpenChange: (open: boolean) => void; defaultHostId?: string; defaultProblemPartIds?: string[]; onCreated?: (repair: RepairJob) => void; } export function RepairFormDialog({ open, onOpenChange, defaultHostId, defaultProblemPartIds, onCreated, }: RepairFormDialogProps) { const queryClient = useQueryClient(); const hostsQuery = useQuery({ queryKey: queryKeys.hosts.list({ pageSize: 100 }), queryFn: () => listHosts({ pageSize: 100 }), enabled: open, }); const form = useForm({ resolver: zodResolver(CreateSchema), defaultValues: { hostId: '', problem: '', problemPartIds: [], notes: '', }, }); useEffect(() => { if (!open) return; form.reset({ hostId: defaultHostId ?? '', problem: '', problemPartIds: defaultProblemPartIds ?? [], notes: '', }); }, [open, defaultHostId, defaultProblemPartIds, form]); const hostId = form.watch('hostId'); const selectedPartIds = form.watch('problemPartIds'); const deployedQuery = useQuery({ queryKey: queryKeys.hosts.deployedParts(hostId), queryFn: () => listHostDeployedParts(hostId), enabled: open && Boolean(hostId), }); const createMutation = useMutation({ mutationFn: async (values: CreateValues) => createRepair({ hostId: values.hostId, problem: values.problem, problemPartIds: values.problemPartIds.length > 0 ? values.problemPartIds : undefined, notes: values.notes ? values.notes : null, }), onSuccess: (repair) => { toast.success('Repair opened'); queryClient.invalidateQueries({ queryKey: queryKeys.repairs.all }); queryClient.invalidateQueries({ queryKey: queryKeys.parts.all }); onOpenChange(false); onCreated?.(repair); }, onError: (err) => toast.error(err instanceof ApiRequestError ? err.body.message : 'Save failed'), }); const pending = createMutation.isPending; function togglePart(partId: string, checked: boolean) { const next = checked ? [...new Set([...selectedPartIds, partId])] : selectedPartIds.filter((id) => id !== partId); form.setValue('problemPartIds', next, { shouldValidate: true, shouldDirty: true }); } return ( Open repair Create a repair against a host. Select the deployed parts involved (optional).
createMutation.mutate(v))} className="space-y-3" > ( Host )} /> ( Problem