import { useRef } from 'react'; import { useSitesStore } from '@/store/sites.ts'; import { useToastStore } from '@/components/ui/Toast.tsx'; import Button from '@/components/ui/Button.tsx'; import { logger } from '@/utils/logger.ts'; /** * Import/Export site configurations as JSON. * Export downloads the current site list; Import merges (appends) sites. */ export default function SiteImportExport() { const sites = useSitesStore((s) => s.sites); const importSites = useSitesStore((s) => s.importSites); const addToast = useToastStore((s) => s.addToast); const fileInputRef = useRef(null); const handleExport = () => { if (sites.length === 0) { addToast('No sites to export', 'warning'); return; } // Strip internal fields (id, createdAt, updatedAt) so import can reassign them const exportData = sites.map((s) => ({ name: s.name, lat: s.lat, lon: s.lon, height: s.height, power: s.power, gain: s.gain, frequency: s.frequency, antennaType: s.antennaType, azimuth: s.azimuth, beamwidth: s.beamwidth, color: s.color, visible: s.visible, notes: s.notes, equipment: s.equipment, })); const json = JSON.stringify(exportData, null, 2); const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `rfcp-sites-${new Date().toISOString().slice(0, 10)}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); addToast(`Exported ${sites.length} site(s) as JSON`, 'success'); }; const handleImport = async (file: File) => { try { const text = await file.text(); const parsed = JSON.parse(text); if (!Array.isArray(parsed)) { addToast('Invalid file format: expected an array of sites', 'error'); return; } // Basic validation const valid = parsed.filter( (s: Record) => typeof s.name === 'string' && typeof s.lat === 'number' && typeof s.lon === 'number' ); if (valid.length === 0) { addToast('No valid sites found in file', 'error'); return; } // Map to SiteFormData shape const sitesData = valid.map((s: Record) => ({ name: s.name as string, lat: s.lat as number, lon: s.lon as number, height: (s.height as number) ?? 30, power: (s.power as number) ?? 43, gain: (s.gain as number) ?? 8, frequency: (s.frequency as number) ?? 1800, antennaType: ((s.antennaType as string) === 'sector' ? 'sector' : 'omni') as 'omni' | 'sector', azimuth: s.azimuth as number | undefined, beamwidth: s.beamwidth as number | undefined, color: (s.color as string) ?? '', visible: (s.visible as boolean) ?? true, notes: s.notes as string | undefined, equipment: s.equipment as string | undefined, })); const count = await importSites(sitesData); addToast(`Imported ${count} site(s)`, 'success'); } catch (error) { logger.error('Import failed:', error); addToast('Invalid JSON file', 'error'); } // Reset file input so same file can be re-imported if (fileInputRef.current) { fileInputRef.current.value = ''; } }; return (

Site Import / Export

{sites.length > 0 && (

{sites.length} site(s) configured

)} {/* Hidden file input */} { const file = e.target.files?.[0]; if (file) handleImport(file); }} />
); }