diff --git a/frontend/src/components/sites/SiteFormDialog.tsx b/frontend/src/components/sites/SiteFormDialog.tsx new file mode 100644 index 0000000..3257c60 --- /dev/null +++ b/frontend/src/components/sites/SiteFormDialog.tsx @@ -0,0 +1,182 @@ +import { useState, useEffect } from 'react' +import { useMutation, useQueryClient } from '@tanstack/react-query' +import { sitesApi, type SiteResponse, type SiteCreate, type SiteUpdate } from '@/lib/api' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from '@/components/ui/dialog' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Button } from '@/components/ui/button' + +interface SiteFormDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + tenantId: string + site?: SiteResponse | null +} + +export function SiteFormDialog({ open, onOpenChange, tenantId, site }: SiteFormDialogProps) { + const queryClient = useQueryClient() + const isEdit = !!site + + const [name, setName] = useState('') + const [address, setAddress] = useState('') + const [latitude, setLatitude] = useState('') + const [longitude, setLongitude] = useState('') + const [elevation, setElevation] = useState('') + const [notes, setNotes] = useState('') + + // Populate form when editing or reset when dialog opens/closes + useEffect(() => { + if (site) { + setName(site.name) + setAddress(site.address ?? '') + setLatitude(site.latitude != null ? String(site.latitude) : '') + setLongitude(site.longitude != null ? String(site.longitude) : '') + setElevation(site.elevation != null ? String(site.elevation) : '') + setNotes(site.notes ?? '') + } else { + setName('') + setAddress('') + setLatitude('') + setLongitude('') + setElevation('') + setNotes('') + } + }, [site, open]) + + const createMutation = useMutation({ + mutationFn: (data: SiteCreate) => sitesApi.create(tenantId, data), + onSuccess: () => { + void queryClient.invalidateQueries({ queryKey: ['sites', tenantId] }) + onOpenChange(false) + }, + }) + + const updateMutation = useMutation({ + mutationFn: (data: SiteUpdate) => sitesApi.update(tenantId, site!.id, data), + onSuccess: () => { + void queryClient.invalidateQueries({ queryKey: ['sites', tenantId] }) + onOpenChange(false) + }, + }) + + const isPending = createMutation.isPending || updateMutation.isPending + + function handleSubmit(e: React.FormEvent) { + e.preventDefault() + const data = { + name: name.trim(), + address: address.trim() || null, + latitude: latitude ? parseFloat(latitude) : null, + longitude: longitude ? parseFloat(longitude) : null, + elevation: elevation ? parseFloat(elevation) : null, + notes: notes.trim() || null, + } + + if (isEdit) { + updateMutation.mutate(data) + } else { + createMutation.mutate(data) + } + } + + return ( + + + + {isEdit ? 'Edit Site' : 'Create Site'} + + {isEdit ? 'Update site details.' : 'Add a new site to organize devices by physical location.'} + + + +
+
+ + setName(e.target.value)} + placeholder="Main Office" + required + /> +
+ +
+ + setAddress(e.target.value)} + placeholder="123 Main St, City, State" + /> +
+ +
+
+ + setLatitude(e.target.value)} + placeholder="-33.8688" + /> +
+
+ + setLongitude(e.target.value)} + placeholder="151.2093" + /> +
+
+ +
+ + setElevation(e.target.value)} + placeholder="58" + /> +
+ +
+ +