# useDocyrusDataImportWizard URL: /docs/native/hooks/use-docyrus-data-import-wizard One-call wiring of a Docyrus data source to the native DataImportWizard. Picks a spreadsheet, uploads and analyses it on the server, auto-maps columns, imports the rows and returns a ready-to-render wizard element plus imperative helpers. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-hooks-use-docyrus-data-import-wizard ``` **Dependencies:** - [@docyrus/api-client](https://www.npmjs.com/package/@docyrus/api-client) - [@docyrus/app-utils](https://www.npmjs.com/package/@docyrus/app-utils) - [@tanstack/react-query](https://tanstack.com/query/latest) - [expo-document-picker (optional)](https://www.npmjs.com/package/expo-document-picker) This is a port of the web hook with the same options and result. It needs an authenticated `RestApiClient` and a `QueryClientProvider` above it. The file is picked with the optional `expo-document-picker` peer and **parsed on the server**, so no spreadsheet library runs on the device. The hook renders [rn-data-import-wizard](/docs/native/docyrus/data-import-wizard). ## How it works 1. **Fields.** Target fields come from [`useDocyrusDataSourceFields`](/docs/native/hooks/use-docyrus-data-source-fields) while the wizard is open (cache-first through the shared inventory), unless you pass `fields`. 2. **Upload.** Picking a file POSTs multipart `FormData` with a React Native file part (`{ uri, name, type }`) to `…/import/upload`. `RestApiClient` detects `FormData` and leaves the `Content-Type` to `fetch`. Pass `uploadFile` to replace this phase. 3. **Analyse.** `GET …/import/details?fileName=…` returns the columns, rows (`data` is normalized to `rows`) and unique-field hints. The mapping is seeded from `initialMapping` or `buildAutoMapping(columns, fields)`. If the fields load after the analysis, auto-mapping runs again. 4. **Import.** Enum-like columns are resolved to option ids on the device and sent as `uniqueData`. Relation lookups are left to the server. The hook POSTs `{ fileName, options }` to `…/import`, where `options` comes from `buildImportOptions(mapping, fields, upsertUniqueFields, uniqueData)`. All three endpoints default to `/v1/apps/{appSlug}/data-sources/{dataSourceSlug}/import/…` and can be overridden with `endpoints`. Closing the wizard resets the run, so the next open starts fresh. ### Deployments without an upload route When the API has no `POST …/import/upload`, the raw file must already be in tenant storage under `tenant-{tenantNo}/tmp/import/{fileName}`. Pass `uploadFile` to put it there yourself and return the `UploadedFileInfo`. The returned `fileName` is what the analyse and import phases use. ## 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({ onDone }: { onDone: () => void }) { const client = useDocyrusClient(); const { wizard, openWizard, isImporting } = useDocyrusDataImportWizard({ client: client!, appSlug: 'base', dataSourceSlug: 'contact', requiredFieldSlugs: ['name'], onImported: () => onDone(), onError: (error, phase) => console.warn(phase, error.message) }); return ( <> {wizard} ); } ``` ### Imperative usage `uploadAsync` and `analyseAsync` call the endpoints directly and resolve their responses without changing the wizard state. They are useful for custom flows and tests. `importAsync` imports the file the wizard has already analysed, using the current mapping. It throws when no file has been analysed yet, and it skips the on-device `uniqueData` pre-resolution. ```tsx const importer = useDocyrusDataImportWizard({ client, appSlug, dataSourceSlug }); const uploaded = await importer.uploadAsync(file); // DataImportFile const analysed = await importer.analyseAsync(uploaded.fileName); // …after the user went through the mapping step: const result = await importer.importAsync(); ``` ## API Reference ### Options (`UseDocyrusDataImportWizardOptions`) | Option | Type | Default | Description | |--------|------|---------|-------------| | `client` | `RestApiClient` | — | Authenticated REST client (required). | | `appSlug` | `string` | — | App slug (required). | | `dataSourceSlug` | `string` | — | Data source slug (required). | | `appId` | `string` | — | Accepted for web parity (web scopes its saved-view lookup by app id). | | `fields` | `ReadonlyArray` | — | Pre-resolved target fields. Skips the schema fetch. | | `requiredFieldSlugs` | `ReadonlyArray` | `[]` | Slugs that must be mapped before leaving the mapping step. | | `uniqueFieldSlugs` | `ReadonlyArray` | from the analyse response | Slugs covered by unique indexes. | | `endpoints` | `{ upload?: string; analyse?: string; import?: string }` | `…/import/upload`, `…/import/details`, `…/import` | Endpoint overrides. | | `uploadFile` | `(file: DataImportFile) => Promise` | — | Replace the upload phase (see above). | | `previewRowCount` | `number` | `10` | Rows shown in the preview step. | | `maxFileSizeBytes` | `number` | `20 * 1024 * 1024` | Maximum file size (20 MB). | | `acceptedExtensions` | `ReadonlyArray` | `['xlsx', 'xls', 'csv']` | Accepted extensions. | | `initialMapping` | `WizardMappingMap` | — | Mapping seed that overrides auto-mapping. | | `enabled` | `boolean` | `true` | `false` returns `wizard: null` (feature flag). | | `open` | `boolean` | — | Controlled open state. When omitted, the hook owns it. | | `onOpenChange` | `(open: boolean) => void` | — | Called whenever the open state changes. | | `onImported` | `(result: ImportResultPayload) => void` | — | Called after a successful import. | | `onError` | `(error: Error, phase: 'upload' \| 'analyse' \| 'import') => void` | — | Called when any phase throws. | | `title` | `ReactNode` | — | Wizard header title. | | `description` | `ReactNode` | — | Wizard header description. | ### Result (`UseDocyrusDataImportWizardResult`) | Field | Type | Description | |-------|------|-------------| | `open` | `boolean` | Current open state. | | `openWizard` / `closeWizard` | `() => void` | Open or close (closing resets the run). | | `resetWizard` | `() => void` | Back to the upload step with a clean state. | | `step` | `ImportWizardStep` | Current step. | | `setStep` | `(next: ImportWizardStep) => void` | Change the step. | | `wizard` | `ReactElement \| null` | The wired `DataImportWizard`. Render it once next to your trigger. | | `uploadAsync` | `(file: DataImportFile) => Promise` | Imperative upload. | | `analyseAsync` | `(fileName: string) => Promise` | Imperative analyse. | | `importAsync` | `() => Promise` | Imperative import with the current mapping. | | `uploadedFile` | `UploadedFileInfo \| null` | Upload response. | | `analysedFile` | `AnalysedFile \| null` | Analyse response. | | `importResult` | `ImportResultPayload \| null` | Import response. | | `fields` | `ReadonlyArray` | Resolved target fields. | | `isUploading` / `isAnalysing` / `isImporting` | `boolean` | Phase flags. | | `error` | `Error \| null` | First error of the upload, analyse or import phase. | ## Differences from web - `File` becomes `DataImportFile` (`NativeFile`: `{ uri, name, type, size? }`), and the upload sends a React Native `FormData` file part. - Target fields come from the lightweight `useDocyrusDataSourceFields` schema hook instead of `useDocyrusDataViewSelect`. - The wizard is a full-height bottom sheet, and the file comes from the system document picker instead of drag-and-drop. ## Type Exports | Type | Description | |------|-------------| | `UseDocyrusDataImportWizardOptions` | Hook options. | | `UseDocyrusDataImportWizardResult` | Hook result. | The wizard types (`DataImportFile`, `ImportWizardStep`, `WizardMappingMap`, `UploadedFileInfo`, `AnalysedFile`, `ImportResultPayload`, …) are exported from [`data-import-wizard`](/docs/native/docyrus/data-import-wizard#type-exports).