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 { createFm } from '../../lib/api/fms.js'; import { listHosts, listHostDeployedParts } from '../../lib/api/hosts.js'; import { ApiRequestError } from '../../lib/api/client.js'; import { queryKeys } from '../../lib/queryKeys.js'; import type { Fm } 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), }); type CreateValues = z.infer; interface FmFormDialogProps { open: boolean; onOpenChange: (open: boolean) => void; defaultHostId?: string; defaultProblemPartIds?: string[]; onCreated?: (fm: Fm) => void; } export function FmFormDialog({ open, onOpenChange, defaultHostId, defaultProblemPartIds, onCreated, }: FmFormDialogProps) { 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: [] }, }); useEffect(() => { if (!open) return; form.reset({ hostId: defaultHostId ?? '', problem: '', problemPartIds: defaultProblemPartIds ?? [], }); }, [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) => createFm({ hostId: values.hostId, problem: values.problem, problemPartIds: values.problemPartIds.length > 0 ? values.problemPartIds : undefined, }), onSuccess: (fm) => { toast.success('FM opened'); queryClient.invalidateQueries({ queryKey: queryKeys.fms.all }); queryClient.invalidateQueries({ queryKey: queryKeys.parts.all }); onOpenChange(false); onCreated?.(fm); }, 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 FM Open a Future Maintenance item against a host. Select deployed parts involved (optional).
createMutation.mutate(v))} className="space-y-3" > ( Host )} /> ( Problem