# rn-data-import-wizard URL: /docs/native/docyrus/data-import-wizard Five-step spreadsheet import wizard (Upload → Map fields → Options → Preview → Result) in a full-height bottom sheet. The file is picked with the system document picker and parsed on the server. API-aligned with the web DataImportWizard. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-data-import-wizard ``` **Dependencies:** - [expo-document-picker (optional)](https://www.npmjs.com/package/expo-document-picker) `expo-document-picker` is an **optional peer** that is loaded lazily. Without it, the **Choose file** button is disabled and the upload step shows an install notice. You can still call `onPickFile` from your own picker. `DataImportWizard` is the **presentational** shell: every piece of state comes in through props. To connect it to a Docyrus data source (upload → analyse → import endpoints, field schema, auto-mapping), use [`useDocyrusDataImportWizard`](/docs/native/hooks/use-docyrus-data-import-wizard), which wires every prop and returns a ready `wizard` element. Use the component directly only when you need your own state machine or back end. ## Usage ```tsx import { useDocyrusClient } from '@docyrus/signin/react-native'; import { Button } from '@/components/docyrus-native/button'; import { useDocyrusDataImportWizard } from '@/hooks/docyrus-native/use-docyrus-data-import-wizard'; export function ImportContacts() { const client = useDocyrusClient(); const { wizard, openWizard } = useDocyrusDataImportWizard({ client: client!, appSlug: 'base', dataSourceSlug: 'contact', requiredFieldSlugs: ['name'], onImported: result => console.log(result.totalSuccessfulRecords) }); return ( <> {wizard} ); } ``` ### Driving the component yourself ```tsx import { DataImportWizard } from '@/components/docyrus-native/data-import-wizard'; setStep('options')} onConfirmOptions={() => setStep('preview')} onStartImport={handleImport} onReset={handleReset} onClose={() => setOpen(false)} /> ``` ## Wizard steps | Step | Purpose | |------|---------| | **Upload** | Opens the system document picker, filtered to the accepted MIME types. Checks the extension and size (empty files are rejected) before calling `onPickFile`. **Continue** (`onAnalyse`) retries the analyse phase. | | **Map fields** | One card per source column with a target `Select` (data source fields, the reserved slugs marked **System**, or skip). Per type: relation `reference_key`, phone country code, date format, money / phone companion column. Required fields that are missing are listed. | | **Options** | "Update existing records on unique-key match" switch (`upsertUniqueFields`) and summary tiles for rows, columns and unique fields. | | **Preview** | The first `previewRowCount` rows in a horizontally scrolling table. Each cell renders through `DocyValueDynamic`, so it looks like the grid. | | **Result** | Tiles for successful records, warnings, duplicates and errors, plus a per-row error list. **Import another file** (`onReset`) and **Done** (`onClose`). | The footer shows **Back**, **Cancel** and the step's primary action. While uploading, analysing or importing, the sheet cannot be dismissed. ## API Reference ### DataImportWizardProps | Prop | Type | Default | Description | |------|------|---------|-------------| | `open` | `boolean` | — | Sheet visibility (required). | | `onOpenChange` | `(open: boolean) => void` | — | Visibility change handler (required). | | `step` | `ImportWizardStep` | — | Current step (required). | | `onStepChange` | `(next: ImportWizardStep) => void` | — | Step setter, used by **Back** (required). | | `targetFields` | `ReadonlyArray` | — | Fields the user can map columns onto (required). | | `requiredFieldSlugs` | `ReadonlyArray` | `[]` | Slugs that must be mapped before leaving the mapping step. | | `uniqueFieldSlugs` | `ReadonlyArray` | `[]` | Slugs covered by unique indexes (upsert toggle, `uniqueData`). | | `file` | `DataImportFile \| null` | — | The picked file (required). | | `uploadedFile` | `UploadedFileInfo \| null` | — | Upload response (required). | | `analysedFile` | `AnalysedFile \| null` | — | Analyse response (required). | | `importResult` | `ImportResultPayload \| null` | — | Import response (required). | | `isUploading` | `boolean` | — | Upload in flight (required). | | `isAnalysing` | `boolean` | — | Analyse in flight (required). | | `isImporting` | `boolean` | — | Import in flight (required). | | `uploadError` | `Error \| null` | — | Upload error, shown in the upload step (required). | | `analyseError` | `Error \| null` | — | Analyse error, shown in the upload step (required). | | `importError` | `Error \| null` | — | Import error, shown in the result step (required). | | `mapping` | `WizardMappingMap` | — | Per-source-column mapping (required). | | `onMappingChange` | `(next: WizardMappingMap) => void` | — | Mapping setter (required). | | `upsertUniqueFields` | `boolean` | — | Upsert toggle value (required). | | `onUpsertChange` | `(next: boolean) => void` | — | Upsert toggle setter (required). | | `onPickFile` | `(file: DataImportFile) => void` | — | A valid file was picked (required). | | `onAnalyse` | `() => void` | — | Upload step primary action (required). | | `onConfirmMapping` | `() => void` | — | Mapping step primary action (required). | | `onConfirmOptions` | `() => void` | — | Options step primary action (required). | | `onStartImport` | `() => void` | — | Preview step primary action (required). | | `onReset` | `() => void` | — | "Import another file" / clear the picked file (required). | | `onClose` | `() => void` | — | Cancel / Done (required). | | `title` | `ReactNode` | `t('ui.dataImportWizard.title', 'Import data')` | Header title. Native renders strings only. | | `description` | `ReactNode` | `t('ui.dataImportWizard.description', …)` | Header description. Native renders strings only. | | `icon` | `string` | `'fal file-import'` | `DocyrusIcon` next to the title. | | `maxFileSizeBytes` | `number` | `20 * 1024 * 1024` | Maximum file size (20 MB). | | `acceptedExtensions` | `ReadonlyArray` | `['xlsx', 'xls', 'csv']` | Accepted file extensions. | | `previewRowCount` | `number` | `10` | Rows shown in the preview step. | | `className` | `string` | — | Extra classes for the sheet body. | ### DataImportFile `DataImportFile` is the native replacement for the DOM `File`: the `NativeFile` shape `{ uri: string; name: string; type: string; size?: number }` returned by `expo-document-picker` and accepted by React Native `FormData`. ### ColumnMappingState | Field | Type | Description | |-------|------|-------------| | `sourceColumn` | `string` | Spreadsheet header. | | `targetSlug` | `string \| null` | Target field slug, a reserved slug, or `null` to skip the column. | | `companionSourceColumn` | `string \| null` | Money / phone companion column (currency, country). | | `fieldOptions` | `{ reference_key?: string; format?: string \| ReadonlyArray }` | Relation reference key, phone default country code or date format tokens. | ## Components | Component | Description | |-----------|-------------| | `DataImportWizard` | Sheet shell: `AwesomeDialog` + `Stepper` + step bodies + footer. | | `UploadStep` | Document picker, file card, upload / analyse spinners and errors. | | `MappingStep` | Per-column target select and per-type options. | | `OptionsStep` | Upsert switch and summary tiles. | | `PreviewStep` | Value-renderer preview table. | | `ProgressStep` | Import progress and result tiles. | Each step is exported on its own so you can rearrange or replace steps in a custom flow. ## Helpers | Helper | Description | |--------|-------------| | `buildAutoMapping(columns, fields)` | Greedy slug + name + token-Jaccard matcher. Returns a `WizardMappingMap`. | | `inferFieldOptions(field)` | Default `fieldOptions` for relation, phone and date fields. | | `validateMapping(mapping, requiredSlugs)` | `{ valid, missing, duplicateTargets }`. | | `buildImportOptions(mapping, fields, upsert, uniqueData?)` | UI state → import POST body. | | `collectUniqueDataLookups(mapping, fields, analysedFile)` | Columns that benefit from `uniqueData` pre-resolution. | | `buildUniqueDataPayload(specs, resolved)` | Builds the `uniqueData` payload entry. | | `getSecondarySlug(fieldType)` | `'__amount_currency'` / `'__phone_country'` for composite fields. | | `isEnumLikeType(fieldType)` / `isRelationLikeType(fieldType)` | Field-type predicates. | | `slugify(value)` / `normalizeKey(value)` | String helpers used by the auto-mapper. | | `IMPORT_WIZARD_STEPS` / `RESERVED_TARGET_SLUGS` | Step order and the reserved slugs (`name`, `description`, `created_on`, `autonumber_id`). | ## Native differences - The wizard is a full-height bottom-sheet `AwesomeDialog` with a compact, horizontally scrolling `Stepper`. - There is no drag-and-drop. The file comes from the system document picker (optional `expo-document-picker`). - `File` becomes `DataImportFile` (`NativeFile`). No spreadsheet library runs on the device: the file is parsed by the analyse endpoint. - `title` / `description` only render when they are strings. ## Translation keys `ui.dataImportWizard.*`: `title`, `description`, `steps.upload | mapping | options | preview | result` and `actions.continue | uploading | analysing | mappingNext | optionsNext | startImport | importing | importingNow | back | cancel | importAnother | done`, plus the per-step keys shared with web. ## Type Exports | Type | Description | |------|-------------| | `DataImportWizardProps` | Component props. | | `DataImportFile` | `NativeFile` (`{ uri, name, type, size? }`). | | `ImportWizardStep` | `'upload' \| 'mapping' \| 'options' \| 'preview' \| 'progress' \| 'result'`. | | `WizardMappingMap` | `Record`. | | `ColumnMappingState` | Per-column mapping state. | | `UploadedFileInfo` | Upload response: `{ fileName, originalName, size, mimeType, filePath }`. | | `AnalysedFile` | Analyse response: `{ fileName, filePath?, columns, rows, uniqueFieldIds, uniqueFieldSlugs, dataSourceRecord? }`. | | `ImportPayloadOptions` | Import POST body options. | | `ImportResultPayload` | Import response (success / warning counts, duplicates, errors). | | `MappingValidationResult` | `{ valid, missing, duplicateTargets }`. | | `ReservedTargetSlug` | `'name' \| 'description' \| 'created_on' \| 'autonumber_id'`. | | `UniqueDataLookupSpec` | Produced by `collectUniqueDataLookups`. |