Composables

useCrudResource

Risorsa CRUD generica con stato items/loading/error e operazioni load/create/update/delete tipizzate.

Signature

function useCrudResource<
  T extends Identifiable,
  CreateRequest,
  UpdateRequest = Partial<CreateRequest>,
>(
  options: CrudResourceOptions<T, CreateRequest, UpdateRequest>,
): CrudResourceResult<T, CreateRequest, UpdateRequest>

Astrae il pattern ricorrente useRoles/usePermessi/useTag/ecc. in un composable generico, mantenendo i tre type parameter per evitare collassi a any.

Type parameters

ParameterVincoloDescrizione
Textends Identifiable (= { id: string })Entità della risorsa (deve avere id: string)
CreateRequestPayload per la create (es. Omit<T, 'id'>)
UpdateRequestdefault Partial<CreateRequest>Payload per l'update; di default è parziale della create

Opzioni CrudResourceOptions

interface CrudResourceOptions<T extends Identifiable, CreateRequest, UpdateRequest = Partial<CreateRequest>> {
  loadItems: (filters?: Record<string, unknown>) => Promise<T[]>
  createItem: (payload: CreateRequest) => Promise<T>
  updateItem?: (id: string, payload: UpdateRequest) => Promise<T>
  deleteItem?: (id: string) => Promise<void>
  resourceName: string
  autoReload?: boolean
}
OpzioneTipoDefaultDescrizione
loadItems(filters?) => Promise<T[]>Obbligatorio. Fetcher principale. Riceve filtri opzionali, ritorna lista
createItem(payload) => Promise<T>Obbligatorio. Crea un nuovo elemento
updateItem(id, payload) => Promise<T>Update (opzionale). Senza questo, result.update non è esposto
deleteItem(id) => Promise<void>Delete (opzionale). Senza questo, result.delete non è esposto
resourceNamestringObbligatorio. Usato nei messaggi di errore (Errore caricamento ${resourceName})
autoReloadbooleantrueRicarica automaticamente dopo create/update/delete

Risultato CrudResourceResult

interface CrudResourceResult<T, CreateRequest, UpdateRequest> {
  items: Ref<T[]>
  loading: Ref<boolean>
  error: Ref<string | null>
  load: (filters?: Record<string, unknown>) => Promise<void>
  create: (payload: CreateRequest) => Promise<T>
  update?: (id: string, payload: UpdateRequest) => Promise<T>
  delete?: (id: string) => Promise<void>
}
CampoDescrizione
itemsRef<T[]> reattiva con la lista corrente
loadingtrue durante load()
errorMessaggio errore corrente o null. Settato solo da load()
load(filters?)Carica gli elementi. Aggiorna items, loading, error
create(payload)Crea elemento, ricarica se autoReload e ritorna l'elemento creato
update(id, payload)Presente solo se updateItem fornita. Throw se chiamata e non disponibile
delete(id)Presente solo se deleteItem fornita
Errori CRUD diversi da load: create, update, deletethrow in caso di errore (con messaggio estratto via extractErrorMessage). error.value viene aggiornato solo da load(). Catcha tu negli handler UI o usa useCrudView che lo fa per te.

Esempio completo

import { useMfeContext } from '@pzeta/mfe-contracts/composables'
import { useCrudResource } from '@pzeta/mfe-contracts/composables'

interface Tag {
  id: string
  nome: string
  colore: string
}

interface CreateTagRequest {
  nome: string
  colore: string
}

const ctx = useMfeContext()

const tags = useCrudResource<Tag, CreateTagRequest>({
  resourceName: 'tag',
  autoReload: true,
  loadItems: async (filters) => {
    const { data } = await ctx.http.get<Tag[]>('/api/tags', { params: filters })
    return data
  },
  createItem: async (payload) => {
    const { data } = await ctx.http.post<Tag>('/api/tags', payload)
    return data
  },
  updateItem: async (id, payload) => {
    const { data } = await ctx.http.put<Tag>(`/api/tags/${id}`, payload)
    return data
  },
  deleteItem: async (id) => {
    await ctx.http.delete(`/api/tags/${id}`)
  },
})

// Uso
await tags.load({ active: true })
console.log(tags.items.value)

const nuovoTag = await tags.create({ nome: 'urgente', colore: '#dc2626' })
await tags.update!(nuovoTag.id, { colore: '#991b1b' })
await tags.delete!(nuovoTag.id)

Senza update/delete

Se la risorsa è read-create-only (audit log, eventi immutabili):

const events = useCrudResource<Event, CreateEventRequest>({
  resourceName: 'eventi',
  loadItems: async () => (await ctx.http.get<Event[]>('/api/events')).data,
  createItem: async (p) => (await ctx.http.post<Event>('/api/events', p)).data,
  // NO updateItem, NO deleteItem
})

events.update // undefined (non tipizzato sul risultato)
events.delete // undefined

Senza auto-reload

Per gestire manualmente quando ricaricare:

const resource = useCrudResource({
  /* ... */
  autoReload: false,
})

await resource.create(payload)
// resource.items non è ancora aggiornato
await resource.load() // ora sì

Utile quando vuoi ottimizzare ricaricando solo dopo bulk operations.

Bridge con useCrudView

Vedi useCrudViewFromResource per collegare automaticamente questo composable alla UI CRUD (dialog + handleEdit + handleDelete con conferma).

Tipi correlati