Components

ImportWizard

Wizard multi-step per l'importazione massiva di dati CSV/Excel/JSON, con valori fissi dal contesto.

Panoramica

ImportWizard è il componente di alto livello che orchestra l'intero workflow di importazione. Espone un Dialog con uno Stepper lineare a 4 step:

  1. Carica File — drag & drop o file picker, validazione lato client, opzioni CSV se applicabili
  2. Mapping Campi — auto-suggest + tabella interattiva per associare colonne sorgente a campi destinazione
  3. Anteprima — dry-run lato server (POST /import/test) con tabella errori/warning
  4. Importa — esecuzione effettiva, sincrona o asincrona (con ImportStatusTracker e polling)

Il componente crea internamente l'istanza di ImportExportApi e la fornisce ai sotto-componenti tramite provide(IMPORT_EXPORT_API_KEY). Tutta la logica di stato è delegata a useImport.

Props

PropTipoDefaultDescrizione
visibleboolean—Visibilità del dialog (v-model)
baseUrlstring—URL base dei microservizi import/export
risorsastringundefinedCodice risorsa target (es. 'anagrafica'). Se omesso, va impostato dall'utente prima dell'upload
valoriFissiRecord<string, unknown>undefinedValori iniettati su ogni riga importata (es. { idlistino: 2 }). I campi corrispondenti spariscono dallo step Mapping. Dalla 0.2.5
apiImportExportClientundefinedClient custom (interfaccia, non classe) — utile per testing, mock o backend alternativi. Se omesso, viene istanziato new ImportExportApi(baseUrl). Vedi Client interfaces

Emits

EventoPayloadDescrizione
update:visiblebooleanRichiesta apertura/chiusura dialog (v-model)
importCompleted{ righeImportate: number; righeScartate: number }Import completato (stato === 'completato' || 'parziale')
errorErrorErrore propagato dall'ErrorBoundary interno

Comportamento

  • Reset alla chiusura: alla hide del dialog viene chiamato reset() di useImport e il ResetBoundary interno viene azzerato. Lo stato non sopravvive tra una sessione e l'altra.
  • Sincronizzazione risorsa: la prop viene riallineata sia al cambio prop sia all'apertura del dialog (dopo che reset() ha azzerato il valore interno).
  • Auto-upload: se al select-file la risorsa è già valorizzata, l'upload parte automaticamente (handleSelectFile). Lo step 1 → 2 avviene quando l'upload + caricamento metadati ha successo.
  • Soglia async: se il file ha ≥ 500 righe (ASYNC_THRESHOLD) l'esecuzione è asincrona — executeImport riceve un importId e useImportStatus avvia il polling. Il valore è override-abile passando useImport(api, { asyncThreshold: 1000 }) quando si usano i singoli step manualmente.
  • Valori fissi: valoriFissi va al servizio con l'upload, nel campo multipart fixedValues (JSON), e node-excel-import lo fonde su ogni riga. I campi che contiene non compaiono nel Mapping: l'utente non li deve mappare dal file. Come risorsa, viene riallineato a ogni apertura del dialog.
  • Fine dell'import: a import terminato il pulsante di sinistra passa da «Annulla» a «Chiudi». Se l'import è completato senza righe scartate, il wizard si chiude da solo dopo 1,5 secondi, così l'utente vede l'esito e la pagina può aggiornare la lista su importCompleted; con righe scartate resta aperto, per rivederle. Dalla 0.2.7.
  • Navigazione: lo Stepper è linear — è possibile tornare indietro tramite il pulsante "Indietro", ma goToStep rifiuta salti in avanti di più di 1 step.

Esempio completo

<script setup lang="ts">
import { ref } from 'vue'
import { ImportWizard } from '@pzeta/vue-importexport'
import '@pzeta/vue-importexport/style.css'

const showImport = ref(false)
const baseUrl = import.meta.env.VITE_IMPORTEXPORT_API_URL

function onCompleted(result: { righeImportate: number; righeScartate: number }) {
  console.log(`Importate ${result.righeImportate}, scartate ${result.righeScartate}`)
  // ricarica lista anagrafica, mostra toast, ecc.
}

function onError(err: Error) {
  console.error('Errore wizard import:', err)
}
</script>

<template>
  <button @click="showImport = true">Importa anagrafica</button>

  <ImportWizard
    v-model:visible="showImport"
    :base-url="baseUrl"
    risorsa="anagrafica"
    @import-completed="onCompleted"
    @error="onError"
  />
</template>

Valori fissi dal contesto

Quando l'import avviene dentro un contesto che fissa già un valore (il listino aperto, l'anagrafica corrente), passalo con valoriFissi invece di chiedere all'utente una colonna nel file. Così fa il gestore dei listini di vue-prezzi, dove le righe importate appartengono tutte al listino aperto:

<ImportWizard
  v-model:visible="showImportWizard"
  :base-url="importBaseUrl"
  risorsa="listinoprezzi"
  :valori-fissi="{ idlistino }"
  @import-completed="handleImportCompleted"
/>

Selezione dinamica risorsa

Se non passi risorsa come prop, il primo step deve includere un Select per farla scegliere all'utente. In tal caso bisogna ricomporre il wizard manualmente usando useImport e i singoli ImportStep* — ImportWizard da solo non espone uno slot per la selezione risorsa.

Iniezione contesto

Il wizard crea (o riceve) il client, lo passa a useImport e lo fornisce ai discendenti:

// All'interno di ImportWizard:
const apiInstance = props.api ?? new ImportExportApi(props.baseUrl)
provide(IMPORT_EXPORT_API_KEY, apiInstance)
const { /* … */ } = useImport(apiInstance)

Gli step (ImportStepUpload, ImportStepMapping, …) non usano l'iniezione: ricevono lo stato con le props. Per sostituire il backend (mock, adapter GraphQL, SDK custom) si passa un ImportExportClient alla prop api. IMPORT_EXPORT_API_KEY è interna e non esportata: per un wizard composto a mano il client va a useImport(client), vedi Wizard custom.

Note

  • Il dialog ha maxWidth: 56rem — adatto a desktop. Su mobile occupa tutta la larghezza disponibile.
  • L'ErrorBoundary interno cattura errori di render e invocazione e mostra una fallback UI con pulsante "Chiudi".
  • Il file picker accetta solo .csv,.xlsx,.xls,.json ed è limitato a 50 MB lato client.