# useDocyrusDataImportWizard URL: /docs/web/hooks/use-docyrus-data-import-wizard One-call wiring of a Docyrus data source to the DataImportWizard — handles upload, analyse, mapping, preview, and import in a single guided flow. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/hooks-use-docyrus-data-import-wizard ``` **Dependencies:** - [@docyrus/app-utils](https://www.npmjs.com/package/@docyrus/app-utils) - [@docyrus/api-client](https://www.npmjs.com/package/@docyrus/api-client) - [@tanstack/react-query](https://tanstack.com/query/latest) This hook is distributed as source. It needs an authenticated `RestApiClient` from `@docyrus/api-client` and a `QueryClientProvider` from `@tanstack/react-query` somewhere above your component tree. ## Overview `useDocyrusDataImportWizard` is the one-call entry point that wires a Docyrus data source to [` {wizard} ); } ``` ### Side-by-side with `useDocyrusDataGrid` Both hooks accept the same `client` + `appSlug` + `dataSourceSlug`. Wire `onImported` to the grid's `reload()` so a successful import refreshes the visible rows: ```tsx const grid = useDocyrusDataGrid({ client, appSlug, dataSourceSlug }); const importer = useDocyrusDataImportWizard({ client, appSlug, dataSourceSlug, fields: grid.fields, // skip duplicate metadata fetch onImported: () => grid.reload() }); return ( <> {grid.toolbar} {importer.wizard} ); ``` ### Imperative usage (skip the dialog UI) ```tsx const importer = useDocyrusDataImportWizard({ client, appSlug, dataSourceSlug }); async function silentImport(file: File) { const uploaded = await importer.uploadAsync(file); await importer.analyseAsync(uploaded.fileName); // …mutate `importer.mapping` via your own UI… return importer.importAsync(); } ``` ## API Reference ### Parameters | Option | Type | Default | Description | |--------|------|---------|-------------| | `client` | `RestApiClient` | — | Authenticated client from `@docyrus/api-client`. **Required.** | | `appSlug` | `string` | — | Target app slug. **Required.** | | `dataSourceSlug` | `string` | — | Target data source slug. **Required.** | | `appId` | `string` | — | Optional multi-tenant isolation key. | | `fields` | `Array` | — | Pre-resolved target fields. When provided, the hook skips its internal data source fetch. | | `requiredFieldSlugs` | `Array` | `[]` | Slugs that must be mapped before the user can leave the mapping step. | | `uniqueFieldSlugs` | `Array` | from analyse response | Slugs covered by unique indexes — controls upsert availability. | | `endpoints` | `{ upload?: string; analyse?: string; import?: string }` | derived | Override the three endpoints (e.g. for tenant-specific routing). | | `uploadFile` | `(file: File) => Promise` | — | Replace the upload phase. Called instead of POSTing to the upload endpoint; the `fileName` it returns is what analyse and import receive. | | `previewRowCount` | `number` | `10` | Number of rows shown in the preview step. | | `maxFileSizeBytes` | `number` | `20 * 1024 * 1024` | Client-side size limit. | | `acceptedExtensions` | `Array` | `['xlsx','xls','csv']` | Allowed file extensions. | | `initialMapping` | `WizardMappingMap` | — | Seed mapping that overrides auto-mapping after the first analyse. | | `enabled` | `boolean` | `true` | When `false` the hook returns `wizard: null` (use as a feature flag). | | `open` | `boolean` | — | Controlled open state. When omitted the hook owns the open flag. | | `onOpenChange` | `(open: boolean) => void` | — | Fires whenever the open state changes (controlled or not). | | `onImported` | `(result: ImportResultPayload) => void` | — | Called after a successful import. | | `onError` | `(error: Error, phase: 'upload' \| 'analyse' \| 'import') => void` | — | Called whenever any of the three phases throws. | | `title` / `description` | `ReactNode` | translated default | Override the wizard header copy. | ### Return Value | Property | Type | Description | |----------|------|-------------| | `wizard` | `ReactElement \| null` | Render this once next to the trigger button — already wired. `null` when `enabled === false`. | | `open` | `boolean` | Current open state of the dialog. | | `openWizard` / `closeWizard` / `resetWizard` | `() => void` | Imperative dialog controls. | | `step` | `ImportWizardStep` | Current step. | | `setStep` | `(step) => void` | Move to a step programmatically. | | `uploadAsync` | `(file: File) => Promise` | POST to the upload endpoint. | | `analyseAsync` | `(fileName: string) => Promise` | GET the analyse endpoint. | | `importAsync` | `() => Promise` | POST the import endpoint using the current mapping + options. | | `uploadedFile` / `analysedFile` / `importResult` | `* \| null` | Latest payloads from each phase. | | `fields` | `Array` | Resolved target fields (from `fields` prop or `useDocyrusDataViewSelect`). | | `isUploading` / `isAnalysing` / `isImporting` | `boolean` | TanStack Query mutation pending flags. | | `error` | `Error \| null` | First error from any of the three phases. | ## Field-Type Awareness The wizard uses field metadata in three places: | Surface | Mechanism | |---------|-----------| | **Mapping step** — per-type config (relation `reference_key`, phone country code, date format, money/phone companion column) | Direct switch on `field.type` in the dumb `` component. | | **Preview step** — every cell rendered with the right Docyrus widget | [`useDocyrusFieldComponent(field.type, 'value-renderer')`](/docs/web/hooks/use-docyrus-field-component). | | **uniqueData pre-resolution** — slugs values to `matchedId` for enum-like fields | Reads `field.enums` / `field.options` client-side. Relation lookups are deferred to the server. | ## Error Handling - File-too-large, wrong-extension, and empty-file errors fire client-side and surface as inline banners on the upload step (no network round-trip). - Network errors are stored on each mutation's `error` and shown in the same banner; `onError(err, phase)` fires for telemetry. - Partial server-side errors come back in `importResult.error[]` — rendered as a per-row error list on the result step. - The wizard locks the close button (`preventOutsideClose`) while any phase is in flight so the user can't accidentally drop the upload mid-stream.