# Export/Import Frontend Implementation ## File Structure ``` src/pages/ ExportImport.jsx # Main page src/components/export/ DataExporter.jsx # Export functionality DataImporter.jsx # Import functionality ConflictDialog.jsx # Conflict resolution UI ImportSummary.jsx # Post-import report ``` ## Component Specifications ### ExportImport.jsx ```jsx import { useState } from 'react'; import DataExporter from '../components/export/DataExporter'; import DataImporter from '../components/export/DataImporter'; export default function ExportImportPage() { const [activeTab, setActiveTab] = useState('export'); return (
{activeTab === 'export' ? : }
); } ``` ### DataExporter.jsx ```jsx import { useState } from 'react'; const EXPORT_TYPES = [ { id: 'routes', label: 'Routes' }, { id: 'rules', label: 'Training Rules' }, { id: 'plans', label: 'Training Plans' } ]; const EXPORT_FORMATS = [ { id: 'json', label: 'JSON' }, { id: 'zip', label: 'ZIP Archive' }, { id: 'gpx', label: 'GPX Files' } ]; export default function DataExporter() { const [selectedTypes, setSelectedTypes] = useState([]); const [selectedFormat, setSelectedFormat] = useState('json'); const [isExporting, setIsExporting] = useState(false); const [progress, setProgress] = useState(0); const handleExport = async () => { setIsExporting(true); // API call to /api/export?types=...&format=... // Track progress and trigger download }; return (

Export Data

Select Data to Export

{EXPORT_TYPES.map(type => ( ))}

Export Format

); } ``` ### DataImporter.jsx ```jsx import { useState } from 'react'; import ConflictDialog from './ConflictDialog'; export default function DataImporter() { const [file, setFile] = useState(null); const [validation, setValidation] = useState(null); const [isImporting, setIsImporting] = useState(false); const [showConflictDialog, setShowConflictDialog] = useState(false); const handleFileUpload = (e) => { const file = e.target.files[0]; setFile(file); // Call /api/import/validate // Set validation results }; const handleImport = () => { if (validation?.conflicts?.length > 0) { setShowConflictDialog(true); } else { startImport(); } }; const startImport = (resolutions = []) => { setIsImporting(true); // Call /api/import with conflict resolutions }; return (

Import Data

{validation && (

Validation Results

Found: {validation.summary.routes} routes, {validation.summary.rules} rules, {validation.summary.plans} plans

{validation.conflicts.length > 0 && (

⚠️ {validation.conflicts.length} conflicts detected

)}
)} {showConflictDialog && ( setShowConflictDialog(false)} /> )}
); } ``` ### ConflictDialog.jsx ```jsx export default function ConflictDialog({ conflicts, onResolve, onCancel }) { const [resolutions, setResolutions] = useState({}); const handleResolution = (id, action) => { setResolutions(prev => ({ ...prev, [id]: action })); }; const applyResolutions = () => { const resolutionList = Object.entries(resolutions).map(([id, action]) => ({ id, action })); onResolve(resolutionList); }; return (

Resolve Conflicts

{conflicts.map(conflict => (

{conflict.name} ({conflict.type})

Existing version: {conflict.existing_version}

Import version: {conflict.import_version}

))}
); } ``` ## Dependencies to Install ```bash npm install react-dropzone react-json-view file-saver