Composables

useBoardService

Facade composable per board generiche. Coordina useBoardMetadata + useBoardElementi e delega tutta l'I/O a un KanbanDataSource pluggable.

Panoramica

useBoardService è la facade principale per la gestione di board generiche. Coordina internamente useBoardMetadata (board, stati, priorità, tag, campi) e useBoardElementi (CRUD schede + spostamento ottimistico), delegando tutta l'I/O al KanbanDataSource iniettato.

Senza opzioni, istanzia un PostgRESTDataSource di default — preserva il comportamento storico v1 senza configurazione aggiuntiva.

Firma

function useBoardService(
  boardIdOrCode: number | string,
  options?: UseBoardServiceOptions
): {
  // State reattivo
  board: Ref<Board | null>
  stati: Ref<BoardStato[]>
  priorita: Ref<BoardPriorita[]>
  elementi: Ref<BoardElement[]>
  tags: Ref<BoardTag[]>
  origini: Ref<BoardOrigine[]>
  visibilita: Ref<BoardVisibilita[]>
  campi: Ref<BoardCampoRow[]>
  loading: Ref<boolean>
  error: Ref<string | null>

  // Caricamento
  initializeBoard: () => Promise<void>
  loadBoardData: () => Promise<void>
  loadStati: () => Promise<void>
  loadElementi: () => Promise<void>
  loadElement: (id: number) => Promise<BoardElement | undefined>
  loadTags: () => Promise<void>
  loadPriorita: () => Promise<void>
  loadCampi: () => Promise<void>

  // CRUD schede
  createElement: (data: Partial<BoardElement>) => Promise<BoardElement | null>
  updateElement: (id: number, data: Partial<BoardElement>) => Promise<BoardElement | null>
  deleteElement: (id: number) => Promise<boolean | null>
  moveElement: (elementId: number, targetFaseId: number, newOrder: number) => Promise<void>
  updateElementTags: (elementId: number, newTagIds: number[]) => Promise<boolean>
}

interface UseBoardServiceOptions {
  /**
   * Sorgente dati iniettabile. Se omessa, viene istanziato `PostgRESTDataSource`
   * con il client singleton di libreria — preserva il comportamento storico.
   */
  dataSource?: KanbanDataSource<Board, BoardStato, BoardElement, BoardPriorita, BoardTag, BoardCampoRow>
}
boardIdOrCode è passato al composable, non a loadBoardData(). Il composable cattura l'ID al setup e tutte le chiamate successive lo riusano. Per cambiare board, monta un nuovo componente con un nuovo boardIdOrCode.

Esempio: backend PostgREST default

import { useBoardService } from '@pzeta/vue-kanban'
import { onMounted } from 'vue'

const boardService = useBoardService('TICKET') // o ID numerico, es. 42

onMounted(() => boardService.loadBoardData())

async function onMoveItem(itemId: number, sourceId: number, targetId: number, newIndex: number) {
  await boardService.moveElement(itemId, targetId, newIndex)
}

loadBoardData() carica in parallelo: initializeBoard + stati + priorità + elementi + campi + tag.

Esempio: backend custom

import { useBoardService, type KanbanDataSource } from '@pzeta/vue-kanban'

const myDataSource: KanbanDataSource = { /* ... vedi KanbanDataSource ... */ }

const service = useBoardService('PROJECT-X', { dataSource: myDataSource })

Tutti i composable interni useranno myDataSource invece del default PostgREST.

Stato reattivo

ProprietàTipoDescrizione
boardRef<Board | null>Metadati board (caricata da initializeBoard)
statiRef<BoardStato[]>Stati / colonne
elementiRef<BoardElement[]>Schede attive
prioritaRef<BoardPriorita[]>Priorità disponibili
tagsRef<BoardTag[]>Tag disponibili
campiRef<BoardCampoRow[]>Campi dinamici (per dati_extra)
origini / visibilitarefsLookup ausiliari (vuoti se non popolati)
loadingRef<boolean>True durante operazioni async
errorRef<string | null>Ultimo messaggio di errore

CRUD schede

// Crea
const nuovo = await boardService.createElement({
  titolo: 'Nuova scheda',
  idfase: 1,
  idpriorita: 2,
})

// Aggiorna
await boardService.updateElement(idscheda, {
  titolo: 'Aggiornato',
  idpriorita: 3,
})

// Elimina (soft o hard, dipende dal datasource)
await boardService.deleteElement(idscheda)

// Sostituzione completa dei tag (M2M)
await boardService.updateElementTags(idscheda, [1, 3, 5])

Spostamento ottimistico

await boardService.moveElement(elementId, targetFaseId, newOrder)

Il composable applica il move localmente prima di chiamare il datasource; in caso di errore esegue rollback automatico ripristinando lo stato precedente della scheda. La promise rilancia comunque l'errore così il consumer può gestirlo (toast, log, ecc.).

Quando usare cosa

ScenarioSoluzione
Board generica con stati/colonneuseBoardService
Pipeline con WIP, stallo, metriche, teamuseFasiService
Solo metadata (board + stati + priorità + tag + campi)useBoardMetadata direttamente
Solo schede (CRUD + ottimismo)useBoardElementi direttamente