/** * FilePanel Component * * Handles file import and export operations */ import { useRef, useCallback, useState } from 'react'; import { usePedigreeStore } from '@/store/pedigreeStore'; import { PedParser } from '@/core/parser/PedParser'; import { exportService } from '@/services/exportService'; import styles from './FilePanel.module.css'; export function FilePanel() { const { pedigree, loadPedigree, createNewPedigree, clearPedigree, } = usePedigreeStore(); const fileInputRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [importError, setImportError] = useState(null); const handleFileSelect = useCallback(async (file: File) => { setImportError(null); try { const content = await file.text(); const parser = new PedParser(); const { pedigree: newPedigree, result } = parser.parseToPedigree(content); if (result.errors.length > 0) { setImportError(`Parse errors: ${result.errors.map(e => e.message).join(', ')}`); return; } if (result.warnings.length > 0) { console.warn('Parse warnings:', result.warnings); } loadPedigree(newPedigree); } catch (error) { setImportError(`Failed to parse file: ${error instanceof Error ? error.message : 'Unknown error'}`); } }, [loadPedigree]); const handleFileInputChange = useCallback((e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { handleFileSelect(file); } // Reset input if (fileInputRef.current) { fileInputRef.current.value = ''; } }, [handleFileSelect]); const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); setIsDragging(true); }, []); const handleDragLeave = useCallback((e: React.DragEvent) => { e.preventDefault(); setIsDragging(false); }, []); const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault(); setIsDragging(false); const file = e.dataTransfer.files?.[0]; if (file) { handleFileSelect(file); } }, [handleFileSelect]); const handleExportSvg = useCallback(async () => { const svg = document.querySelector('.pedigree-main')?.closest('svg') as SVGSVGElement; if (!svg) { alert('No pedigree to export'); return; } try { await exportService.exportSvg(svg, { filename: pedigree?.familyId ?? 'pedigree' }); } catch (error) { alert('Failed to export SVG'); } }, [pedigree]); const handleExportPng = useCallback(async () => { const svg = document.querySelector('.pedigree-main')?.closest('svg') as SVGSVGElement; if (!svg) { alert('No pedigree to export'); return; } try { await exportService.exportPng(svg, { filename: pedigree?.familyId ?? 'pedigree' }); } catch (error) { alert('Failed to export PNG'); } }, [pedigree]); const handleExportPed = useCallback(() => { if (!pedigree) { alert('No pedigree to export'); return; } try { exportService.exportPed(pedigree, { filename: pedigree.familyId ?? 'pedigree' }); } catch (error) { alert('Failed to export PED'); } }, [pedigree]); const handleNewPedigree = useCallback(() => { const familyId = prompt('Enter Family ID:', 'FAM001'); if (familyId) { createNewPedigree(familyId); } }, [createNewPedigree]); return (
File Operations
Drag & Drop PED file here
{importError && (
{importError}
)}
Export
{pedigree && ( <>
Family ID: {pedigree.familyId}
Persons: {pedigree.persons.size}
Relationships: {pedigree.relationships.size}
)}
); }