Composables

useAttachments

Composable principale che orchestra un AttachmentAdapter + state reattivo per CRUD completo degli allegati.

Panoramica

useAttachments è il composable di alto livello: combina un AttachmentAdapter (axios, GraphQL, mock, ...) con uno state reattivo per esporre lista, loading, errori, progress di upload e i metodi di rete.

L'adapter è risolto con priorità:

  1. options.adapter esplicito
  2. inject(ATTACHMENT_ADAPTER_KEY) fornito da VueAttachmentPlugin
  3. Errore al primo metodo invocato
Risoluzione lazy: inject viene chiamato a setup-time, ma la verifica dell'adapter avviene solo al primo metodo di rete. Se passi attachments via prop a un componente presentational, il composable può girare senza alcun adapter (utile per demo/SSR).

Firma

function useAttachments(options: UseAttachmentsOptions): {
  attachments: Ref<Attachment[]>
  isLoading: Ref<boolean>
  error: Ref<string | null>
  uploadProgress: Ref<number>
  totalSize: ComputedRef<number>
  hasAttachments: ComputedRef<boolean>
  fetchAttachments: () => Promise<void>
  uploadFile: (file: File) => Promise<Attachment>
  downloadFile: (id: string, disposition?: 'inline' | 'attachment') => Promise<Blob>
  deleteFile: (id: string) => Promise<void>
  deleteMultiple: (ids: string[]) => Promise<void>
}

Opzioni

interface UseAttachmentsOptions {
  /** Adapter esplicito; se omesso, viene risolto via inject del plugin. */
  adapter?: AttachmentAdapter

  /** Reference key dell'entità owner (es. 'tickets'/'idticket'/123). */
  referenceTable?: string
  referenceColumn?: string
  referenceValue?: string | number

  /** Path storage su MinIO/S3 (override del default del plugin). */
  storagePath?: string
}
Auto-fetch al setup: se referenceTable e referenceValue sono valorizzati, il composable lancia fetchAttachments() automaticamente. Altrimenti devi chiamarlo esplicitamente. Niente watch reattivo: cambiare referenceValue dopo il setup non re-fetcha — usa il pattern key sul componente padre o chiama fetchAttachments() manualmente.

Esempio base (con plugin)

<script setup lang="ts">
import { useAttachments } from '@pzeta/vue-attachment'

const {
  attachments,
  isLoading,
  fetchAttachments,
  uploadFile,
  deleteFile
} = useAttachments({
  // adapter omesso → inject dal VueAttachmentPlugin
  referenceTable: 'tickets',
  referenceColumn: 'idticket',
  referenceValue: 42
})

async function handleUpload(file: File) {
  await uploadFile(file)
  // attachments si aggiorna automaticamente
}
</script>

<template>
  <div v-if="isLoading">Caricamento...</div>
  <ul v-else>
    <li v-for="a in attachments" :key="a.id">
      {{ a.fileName }}
      <button @click="deleteFile(a.id)">Elimina</button>
    </li>
  </ul>
</template>

Esempio: adapter esplicito (no plugin)

Per scenari di test, multi-tenant o componenti isolati che non passano dal plugin globale:

import axios from 'axios'
import { createAxiosAdapter, useAttachments } from '@pzeta/vue-attachment'

const adapter = createAxiosAdapter(axios.create({ baseURL: '/api/storage' }))

const { attachments, fetchAttachments } = useAttachments({
  adapter,
  referenceTable: 'tickets',
  referenceColumn: 'idticket',
  referenceValue: 42
})

Download

downloadFile ritorna il Blob ma triggera automaticamente il save dialog del browser usando attachment.fileName:

const { downloadFile } = useAttachments({ /* ... */ })

await downloadFile('abc123')                  // disposition: 'inline' (default)
await downloadFile('abc123', 'attachment')    // forza Content-Disposition: attachment

Upload progress

uploadProgress è un Ref<number> (0–100) aggiornato durante l'upload. L'adapter axios di default non popola attualmente questo valore via onUploadProgress — è disponibile come hook per adapter custom.

Gestione errori

const { error } = useAttachments({ /* ... */ })

watch(error, (msg) => {
  if (msg) toast.error(msg)
})

Tutti i metodi catturano gli errori e popolano error.value. La Promise rilancia comunque l'eccezione, quindi puoi anche gestirla con try/catch lato consumer.

Stato reattivo

Ref/ComputedTipoDescrizione
attachmentsRef<Attachment[]>Lista corrente
isLoadingRef<boolean>True durante fetch/upload/download/delete
errorRef<string | null>Ultimo errore (messaggio dell'eccezione)
uploadProgressRef<number>0–100 (popolato solo se l'adapter lo supporta)
totalSizeComputedRef<number>Somma size di tutti gli allegati (bytes)
hasAttachmentsComputedRef<boolean>attachments.length > 0