Funzionalita Avanzate

Ricerche Salvate

Persistenza dello stato di ricerca completo tramite adapter custom (v2).

Panoramica

Le ricerche salvate permettono agli utenti di salvare, caricare e gestire configurazioni di ricerca complete (filtri + preset + regole + group-by). La libreria fornisce solo l'interfaccia SavedSearchAdapter: l'implementazione dello storage è a carico del consumer (REST, GraphQL, IndexedDB, in-memory).

Il pannello viene renderizzato solo se la prop savedSearchAdapter è presente nella SearchToolbar. La ricerca con isDefault: true viene applicata automaticamente al mount.

Anteprima live (con adapter in-memory)

Interfaccia adapter

interface SavedSearchAdapter {
  /** Carica tutte le ricerche accessibili all'utente */
  list(): Promise<SavedSearch[]>

  /** Salva con nome e stato corrente */
  save(
    name: string,
    state: SavedSearchState,
    options?: { isDefault?: boolean; isShared?: boolean }
  ): Promise<SavedSearch>

  /** Aggiorna metadati (opzionale — se assente la UI nasconde "Modifica") */
  update?(
    id: string,
    patch: Partial<Pick<SavedSearch, 'name' | 'isDefault' | 'isShared' | 'sharedWith'>>
  ): Promise<SavedSearch>

  /** Elimina per ID */
  delete(id: string): Promise<void>

  /** Soft delete (opzionale — se assente la UI mostra solo "Elimina") */
  archive?(id: string): Promise<void>

  /** Imposta/rimuove il flag default (id=null rimuove) */
  setDefault(id: string | null): Promise<void>
}

Struttura SavedSearch

interface SavedSearchState {
  filters: Record<string, unknown>
  activePresets: string[]
  rules: RuleGroup | null
  groupByFields: GroupByEntry[]
}

interface SavedSearch {
  id: string
  name: string
  state: SavedSearchState
  isDefault: boolean  // applicata automaticamente al mount se true
  isShared: boolean
  sharedWith?: string[]
  createdAt: string   // ISO 8601
  updatedAt: string   // ISO 8601
}

Implementazione adapter (esempio con API REST)

import type { SavedSearchAdapter, SavedSearch, SavedSearchState } from '@pzeta/vue-ricerca'

const searchAdapter: SavedSearchAdapter = {
  async list() {
    const res = await fetch('/api/ricerche-salvate')
    return res.json()
  },

  async save(name, state, options) {
    const res = await fetch('/api/ricerche-salvate', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, state, ...options }),
    })
    return res.json()
  },

  async delete(id) {
    await fetch(`/api/ricerche-salvate/${id}`, { method: 'DELETE' })
  },

  async setDefault(id) {
    await fetch(`/api/ricerche-salvate/default`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ id }),
    })
  },
}

Utilizzo in SearchToolbar

<SearchToolbar
  v-model="filtri"
  :fields="fields"
  :saved-search-adapter="searchAdapter"
  @apply="onApply"
/>

Via useSearchToolbar

const {
  savedSearches,
  savedSearchesLoading,
  canUpdateSearch,
  canArchiveSearch,
  saveSearch,
  updateSearch,
  archiveSearch,
  loadSearch,
  deleteSearch,
  setDefaultSearch,
} = useSearchToolbar({
  fields,
  savedSearchAdapter: searchAdapter,
  onApply: (state) => { /* ... */ },
})

// Salva ricerca corrente (snapshot di searchState.value)
await saveSearch('Ricerca clienti attivi', {
  isDefault: false,
  isShared: false,
  sharedWith: ['user-1', 'user-2'],
})

// Ripristina una ricerca salvata (aggiorna filtri, preset, rules, groupBy)
loadSearch('uuid-ricerca')

// Aggiorna metadati (richiede adapter.update)
if (canUpdateSearch) {
  await updateSearch('uuid-ricerca', { name: 'Nuovo nome', isShared: true })
}

// Soft delete (richiede adapter.archive)
if (canArchiveSearch) {
  await archiveSearch('uuid-ricerca')
}

// Hard delete
await deleteSearch('uuid-ricerca')

// Imposta come default (al prossimo mount verrà applicata automaticamente)
await setDefaultSearch('uuid-ricerca')
// Rimuove il default
await setDefaultSearch(null)

Note

  • La ricerca con isDefault: true viene applicata automaticamente al mount del componente.
  • canUpdateSearch è true se l'adapter implementa il metodo update.
  • canArchiveSearch è true se l'adapter implementa il metodo archive.
  • La libreria non interpreta sharedWith: è un array opaco passato all'adapter senza elaborazione.