-
Notifications
You must be signed in to change notification settings - Fork 0
Import Export Usage Guide
Masked-Kunsiquat edited this page Dec 25, 2025
·
2 revisions
This guide shows developers how to integrate the Import/Export module into CrewSplit screens and workflows.
Create a route file for the import/export screen:
// app/import-export.tsx
import { ImportExportScreen } from "@modules/import-export";
export default ImportExportScreen;
export const options = {
title: "Import & Export",
headerShown: true,
};import { useRouter } from 'expo-router';
import { Link } from 'expo-router';
// Option 1: Programmatic navigation
function SettingsMenu() {
const router = useRouter();
return (
<Button
title="Import/Export"
onPress={() => router.push('/import-export')}
/>
);
}
// Option 2: Link component
function SettingsMenu() {
return (
<Link href="/import-export" asChild>
<Button title="Import/Export" />
</Link>
);
}Full-featured screen for import/export operations.
Features:
- Export Current Trip (when tripId param provided)
- Export Full Database
- Import from File
- Configurable options (sample data, archived data)
- Loading states and error handling
Usage with Trip Context:
function TripDashboard({ tripId }: { tripId: string }) {
const router = useRouter();
return (
<Button
title="Export Trip"
onPress={() => router.push({
pathname: '/import-export',
params: { tripId },
})}
/>
);
}Compact component for Settings screens.
Features:
- Backup Database button
- Restore from Backup button
- Informational tip
- Optional callbacks
Basic Usage:
import { SettingsExportSection } from '@modules/import-export';
function SettingsScreen() {
return (
<ScrollView>
{/* Other settings sections */}
<SettingsExportSection />
</ScrollView>
);
}With Callbacks:
function SettingsScreen() {
const handleExportComplete = () => {
console.log('Backup created');
// Could refresh UI, update badge, etc.
};
const handleImportComplete = () => {
console.log('Data restored');
Alert.alert(
'Restart Required',
'Please restart the app for changes to take effect.'
);
};
return (
<ScrollView>
<SettingsExportSection
onExportComplete={handleExportComplete}
onImportComplete={handleImportComplete}
/>
</ScrollView>
);
}import { useExport } from '@modules/import-export';
function CustomExportButton({ tripId }: { tripId: string }) {
const {
exportTrip,
exportFullDatabase,
isExporting,
error,
} = useExport();
const handleExport = async () => {
await exportTrip(tripId, {
includeSampleData: false,
includeArchivedData: false,
});
};
return (
<Button
title={isExporting ? "Exporting..." : "Export Trip"}
onPress={handleExport}
disabled={isExporting}
/>
);
}API:
interface UseExportReturn {
exportTrip: (tripId: string, options?: ExportOptions) => Promise<void>;
exportFullDatabase: (options?: ExportOptions) => Promise<void>;
isExporting: boolean;
error: Error | null;
}
interface ExportOptions {
includeSampleData?: boolean;
includeArchivedData?: boolean;
}import { useImport } from '@modules/import-export';
function CustomImportButton() {
const {
importFromFile,
previewImport,
isImporting,
error,
result,
} = useImport();
const handleImport = async () => {
await importFromFile('skip'); // or 'replace'
};
return (
<>
<Button
title={isImporting ? "Importing..." : "Import Data"}
onPress={handleImport}
disabled={isImporting}
/>
{result && (
<Text>
Imported: {result.successCount}, Skipped: {result.skippedCount}
</Text>
)}
</>
);
}API:
interface UseImportReturn {
importFromFile: (
conflictResolution?: ConflictStrategy,
options?: ImportOptions,
) => Promise<void>;
previewImport: (file: any) => Promise<ImportPreview>;
isImporting: boolean;
error: Error | null;
result: ImportResult[] | null;
}
type ConflictStrategy = "skip" | "replace" | "generate_new_ids";
interface ImportOptions {
validateForeignKeys?: boolean;
dryRun?: boolean;
}For advanced use cases, use the services directly:
import { ExportService } from "@modules/import-export";
const exportService = new ExportService();
// Export single trip
const tripData = await exportService.exportTrip(tripId, {
includeSampleData: false,
includeArchivedData: false,
});
// Export full database
const fullData = await exportService.exportFullDatabase({
includeSampleData: true,
includeArchivedData: true,
});
// Export global data only (categories, FX rates)
const globalData = await exportService.exportGlobalData({
includeArchivedData: false,
});
// Write to file and share
await exportService.writeToFile(tripData, "trip-backup.json");import { ImportService } from "@modules/import-export";
const importService = new ImportService();
// Import from file (opens file picker)
const results = await importService.importFromFile("skip", {
validateForeignKeys: true,
dryRun: false,
});
// Preview import conflicts
const preview = await importService.previewImport(exportData);
console.log(
`${preview.totalRecords} records, ${preview.totalConflicts} conflicts`,
);
// Import from parsed data
const results = await importService.importFromData(exportData, "replace");
// Get import summary
const summary = importService.getImportSummary(results);
console.log(`Success: ${summary.successCount}, Errors: ${summary.errorCount}`);- User navigates to Settings
- Taps "Backup Database" in SettingsExportSection
- Button shows loading ("Creating Backup...")
- Native share dialog appears with backup file
- User saves/shares file
- Success alert confirms backup created
- Optional
onExportCompletecallback fires
- User viewing trip dashboard
- Taps "Export" or "Share Trip"
- ImportExportScreen opens with trip context
- User adjusts options (exclude sample data, etc.)
- Taps "Export Trip"
- Share dialog appears
- User sends via email/messaging
- Success alert confirms
- User navigates to Import/Export screen
- Reads warning about duplicate handling
- Taps "Choose File"
- File picker opens
- Selects backup JSON file
- Import processes with loading overlay
- Success alert shows summary (X imported, Y skipped)
- Optional
onImportCompletecallback fires
The hooks handle all errors automatically with user-facing alerts:
Export Errors:
- File system access denied
- Insufficient storage
- Invalid data format
Import Errors:
- Invalid JSON structure
- Unsupported file version
- Missing required fields
- Foreign key violations
- Database constraint errors
Example Error Display:
Import Failed
Invalid export file structure:
- Missing required field: version
- Invalid trip ID format in record 3
- Foreign key violation: participant 'abc-123' not found
Please check the file and try again.
import React from 'react';
import { View, Text, StyleSheet, ScrollView } from 'react-native';
import { theme } from '@ui/theme';
import { Card } from '@ui/components';
import { SettingsExportSection } from '@modules/import-export';
import { useRouter } from 'expo-router';
export default function SettingsScreen() {
const router = useRouter();
const handleExportComplete = () => {
console.log('Backup created successfully');
};
const handleImportComplete = () => {
console.log('Data restored successfully');
// Could show restart prompt, clear caches, etc.
};
return (
<View style={theme.commonStyles.container}>
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.content}
>
{/* App Info */}
<Card>
<Text style={styles.sectionTitle}>About</Text>
<View style={styles.row}>
<Text style={styles.label}>Version</Text>
<Text style={styles.value}>1.1.0</Text>
</View>
</Card>
{/* Backup & Restore */}
<SettingsExportSection
onExportComplete={handleExportComplete}
onImportComplete={handleImportComplete}
/>
{/* Advanced Options */}
<Card>
<Text style={styles.sectionTitle}>Advanced</Text>
<Button
title="Advanced Import/Export"
onPress={() => router.push('/import-export')}
/>
</Card>
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
scrollView: {
flex: 1,
},
content: {
padding: theme.spacing.lg,
gap: theme.spacing.lg,
},
sectionTitle: {
fontSize: theme.typography.lg,
fontWeight: theme.typography.bold,
color: theme.colors.text,
marginBottom: theme.spacing.md,
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: theme.spacing.sm,
},
label: {
fontSize: theme.typography.base,
color: theme.colors.textSecondary,
},
value: {
fontSize: theme.typography.base,
color: theme.colors.text,
},
});Key flows to test:
- Export trip: Verify file created and shareable
- Export full DB: Verify all data included
- Toggle options: Ensure options affect export content
- Import valid file: Verify data imported correctly
- Import duplicates: Verify skip strategy works
- Import invalid file: Verify error shown gracefully
- Cancel operations: Ensure no crashes on cancel
- Loading states: Verify buttons disable during operations
- Success feedback: Verify alerts appear
- Screen reader: Test with TalkBack/VoiceOver
Both screens follow accessibility best practices:
- All buttons have
accessibilityLabelandaccessibilityHint - Touch targets meet 44x44pt minimum
- Screen reader support with meaningful labels
- Loading states announced to assistive tech
- High contrast colors for visibility
- Import Export Overview - Architecture and design
- Offline and Backup - End-user guide
- Developer Guidelines - Code standards
- Architecture & Agents - System architecture