Componenti

SpreadsheetEditor

Componente orchestratore dell'editor foglio. Carica metadati + snapshot + lock, monta Fortune Sheet, gestisce panel ERP/versioni/ACL, autosave e refresh interval.

Quick start

<template>
  <SpreadsheetEditor
    :foglio-id="42"
    mode="edit"
    height="700px"
    show-erp-panel
    @save="onSave"
    @change="onChange"
    @error="onError"
  />
</template>

Props

PropTipoDefaultDescrizione
foglioIdnumber | nullObbligatorio. ID del foglio da caricare. Se null, mostra stato "seleziona un foglio"
mode'edit' | 'view''edit'Forza modalità sola-lettura ('view') anche con permessi di scrittura
showToolbarbooleantrueMostra/nasconde la toolbar superiore
showErpPanelbooleantrueMostra/nasconde il pannello laterale ERP
showGlobalFiltersbooleanfalseMostra controlli filtri globali (periodo, cliente)
initialFiltersGlobalFilters | nullnullFiltri globali iniziali da applicare prima del load
heightstring'100%'Altezza CSS del componente (es. '600px', '80vh')
widthstring'100%'Larghezza CSS
autosaveIntervalnumber | nullnullIntervallo autosave in secondi (es. 60). null = disabilitato
refreshIntervalnumber | nullnullIntervallo refresh sorgenti ERP in secondi. null = disabilitato
postgrestUrlOverridestring | nullnullOverride puntuale dell'URL PostgREST (raramente necessario)

Eventi

EventoPayloadQuando
saveSpreadsheetSaveEventSalvataggio completato — { idfolio, nome, revisione, timestamp }
changeSpreadsheetChangeEventModifica cella (debounced 500ms) — { sheetIndex, row, col, value, isDirty }
exportSpreadsheetExportEventExport xlsx completato — { idfolio, filename }
errorSpreadsheetErrorEventErrore in load/save/ERP/export/import/lock/ACL
erp-refreshErpRefreshEventRefresh sorgenti ERP completato — { sources, timestamp, errors }
readyFoglio caricato e adapter inizializzato
filter-changeGlobalFiltersModifica filtri globali del foglio

SpreadsheetErrorEvent.code

CodeOrigine tipica
'LOAD_ERROR'Foglio inesistente o errore PostgREST in load
'SAVE_ERROR'Errore POST /foglidicalcoloversioni o RPC
'ERP_FETCH_ERROR'Errore fetch sorgente ERP
'EXPORT_ERROR'Errore microservizio xlsx
'IMPORT_ERROR'Errore parsing/import xlsx
'LOCK_ERROR'RPC lock fallita
'ACL_ERROR'Errore CRUD ACL

API esposta via template ref

SpreadsheetEditor espone metodi imperativi via defineExpose:

interface ExposedAPI {
  save(): Promise<SpreadsheetSaveEvent>
  exportXlsx(filename?: string): Promise<void>
  refreshErpData(): Promise<void>
  setGlobalFilters(filters: GlobalFilters): void
  getSnapshot(): FortuneSheetSnapshot
  loadSnapshot(snapshot: FortuneSheetSnapshot): void
  readonly state: Ref<FoglioState>
}

Uso

<script setup lang="ts">
import { ref } from 'vue'

const editor = ref<InstanceType<typeof SpreadsheetEditor> | null>(null)

async function salvaManuale() {
  if (!editor.value) return
  const event = await editor.value.save()
  console.log(`Salvata rev. ${event.revisione}`)
}

function leggiStato() {
  console.log(editor.value?.state.value.isDirty)
}

async function esporta() {
  await editor.value?.exportXlsx('rendiconto-2026.xlsx')
}
</script>

<template>
  <SpreadsheetEditor ref="editor" :foglio-id="42" />
  <button @click="salvaManuale">Salva</button>
  <button @click="esporta">Esporta xlsx</button>
</template>

Lifecycle interno

onMounted
  ├─ Registra adapter.onChange → emit('change') + markDirty
  └─ Se foglioId !== null → loadFoglio()
       ├─ adapter.initialize(fortuneContainer)
       ├─ spreadsheet.load(idfolio)
       │   ├─ FogliPostgrestService.caricaFoglio → metadati + sorgenti + filtri
       │   ├─ FogliPostgrestService.caricaUltimaVersione → snapshot
       │   ├─ adapter.loadSnapshot(snapshot)
       │   └─ se mode='edit' && permesso !== 'lettura':
       │      ├─ LockService.acquisisciLock
       │      └─ avvia heartbeat (5 min)
       ├─ Aggiorna filtri globali da foglio
       ├─ erp.caricaCatalogo
       └─ emit('ready')

onUnmounted
  ├─ adapter.offChange
  ├─ LockService.rilasciaLock (se owner)
  └─ stop heartbeat

Stati visuali

StatoUI
isLoading.value === trueOverlay con ProgressBar "Caricamento foglio in corso..."
error.value !== nullEmpty state con icona warning e dettaglio errore
foglioId === null"Seleziona o crea un foglio per iniziare"
NormaleToolbar + Splitter (workbook | pannello ERP)
mode === 'view' o permesso_effettivo === 'lettura'UI read-only, pulsanti save/share disabilitati

Modalità 'view' vs permesso_effettivo

const isReadOnly = computed(() =>
  mode === 'view' || state.permessoEffettivo === 'lettura'
)

mode='view' forza la sola lettura anche se l'utente ha permessi di scrittura — utile per anteprime, dashboard read-only, link condivisi.

Pattern: editor in dialog modale

<script setup lang="ts">
import { ref } from 'vue'
import { Dialog } from '@pzeta/vue-components'

const dialogVisible = ref(false)
const foglioApertoId = ref<number | null>(null)

function apriFoglio(id: number) {
  foglioApertoId.value = id
  dialogVisible.value = true
}
</script>

<template>
  <Dialog v-model:visible="dialogVisible" :style="{ width: '90vw', height: '85vh' }">
    <SpreadsheetEditor :foglio-id="foglioApertoId" height="100%" />
  </Dialog>
</template>

Pattern: dashboard read-only con auto-refresh

<template>
  <SpreadsheetEditor
    :foglio-id="dashboardFoglioId"
    mode="view"
    :refresh-interval="30"
    :show-toolbar="false"
    :show-erp-panel="false"
    height="600px"
  />
</template>

Tipi correlati