From d2e098d41274ab4f5ad8e5396b54bc50ff65b86e Mon Sep 17 00:00:00 2001 From: Sam Chau Date: Sun, 28 Dec 2025 20:08:35 +1030 Subject: [PATCH] feat(data-page): implement data page store for search and view management --- src/lib/client/stores/dataPage.ts | 98 +++++++++++++++++++ src/lib/client/ui/actions/ViewToggle.svelte | 27 +++++ .../[databaseId]/+page.svelte | 67 +++---------- 3 files changed, 140 insertions(+), 52 deletions(-) create mode 100644 src/lib/client/stores/dataPage.ts create mode 100644 src/lib/client/ui/actions/ViewToggle.svelte diff --git a/src/lib/client/stores/dataPage.ts b/src/lib/client/stores/dataPage.ts new file mode 100644 index 0000000..b10db78 --- /dev/null +++ b/src/lib/client/stores/dataPage.ts @@ -0,0 +1,98 @@ +/** + * Data page store for managing search, view toggle, and filtering + * Combines search functionality with view state persistence + */ + +import { writable, derived } from 'svelte/store'; +import { browser } from '$app/environment'; +import { createSearchStore, type SearchStore } from './search'; + +export type ViewMode = 'table' | 'cards'; + +export interface DataPageConfig { + /** Key for localStorage persistence */ + storageKey: string; + /** Fields to search within items */ + searchKeys: (keyof T)[]; + /** Default view mode */ + defaultView?: ViewMode; + /** Debounce time for search in ms */ + debounceMs?: number; +} + +export interface DataPageStore { + /** Search store for SearchAction component */ + search: SearchStore; + /** Current view mode ('table' | 'cards') */ + view: { + subscribe: (fn: (value: ViewMode) => void) => () => void; + set: (value: ViewMode) => void; + }; + /** Filtered items based on search query */ + filtered: { + subscribe: (fn: (value: T[]) => void) => () => void; + }; + /** Update the items (e.g., when data changes) */ + setItems: (items: T[]) => void; +} + +/** + * Create a data page store for managing list pages + * + * @example + * const { search, view, filtered } = createDataPageStore(data.profiles, { + * storageKey: 'qualityProfilesView', + * searchKeys: ['name', 'description'] + * }); + */ +export function createDataPageStore( + initialItems: T[], + config: DataPageConfig +): DataPageStore { + const { storageKey, searchKeys, defaultView = 'table', debounceMs = 300 } = config; + + // Items store + const items = writable(initialItems); + + // Search store + const search = createSearchStore({ debounceMs }); + + // View store with localStorage persistence + const storedView = browser ? (localStorage.getItem(storageKey) as ViewMode | null) : null; + const view = writable(storedView ?? defaultView); + + // Persist view changes to localStorage + if (browser) { + view.subscribe((value) => { + localStorage.setItem(storageKey, value); + }); + } + + // Filtered items derived from search + const filtered = derived([items, search.debouncedQuery], ([$items, $query]) => { + if (!$query) return $items; + + const queryLower = $query.toLowerCase(); + return $items.filter((item) => + searchKeys.some((key) => { + const value = item[key]; + if (value == null) return false; + return String(value).toLowerCase().includes(queryLower); + }) + ); + }); + + return { + search, + view: { + subscribe: view.subscribe, + set: view.set + }, + filtered: { + subscribe: filtered.subscribe + }, + setItems: (newItems: T[]) => items.set(newItems) + }; +} + +export type { SearchStore }; diff --git a/src/lib/client/ui/actions/ViewToggle.svelte b/src/lib/client/ui/actions/ViewToggle.svelte new file mode 100644 index 0000000..55a66ca --- /dev/null +++ b/src/lib/client/ui/actions/ViewToggle.svelte @@ -0,0 +1,27 @@ + + + + + (value = 'cards')} + /> + (value = 'table')} + /> + + diff --git a/src/routes/quality-profiles/[databaseId]/+page.svelte b/src/routes/quality-profiles/[databaseId]/+page.svelte index edb48ef..8063367 100644 --- a/src/routes/quality-profiles/[databaseId]/+page.svelte +++ b/src/routes/quality-profiles/[databaseId]/+page.svelte @@ -1,36 +1,23 @@ @@ -59,23 +37,8 @@ - - - - (currentView = 'cards')} - /> - (currentView = 'table')} - /> - - + + @@ -88,7 +51,7 @@ No quality profiles found for {data.currentDatabase.name}

- {:else if filteredProfiles.length === 0} + {:else if $filtered.length === 0}
@@ -96,10 +59,10 @@ No quality profiles match your search

- {:else if currentView === 'table'} - + {:else if $view === 'table'} + {:else} - + {/if}