# 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 (
);
}
```
### 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