refactor(inventar,times): use shared createViewStore factory

Replace ~230 LOC of duplicated view store boilerplate with 20-line
factory calls. Both modules had identical copy-pasted implementations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-02 14:25:48 +02:00
parent 934f3337e3
commit b1a5c95f1d
2 changed files with 33 additions and 217 deletions

View file

@ -2,116 +2,20 @@
* View Store Client-side view preferences persisted to localStorage.
*/
import { browser } from '$app/environment';
import type { ViewMode, SortOption, FilterCriteria } from '../queries';
import { createViewStore } from '@manacore/shared-stores';
import type { ViewMode, FilterCriteria } from '../queries';
const VIEW_KEY = 'inventar_view_mode';
const SORT_KEY = 'inventar_sort';
interface SavedFilter {
id: string;
name: string;
criteria: FilterCriteria;
createdAt: string;
}
const FILTERS_KEY = 'inventar_saved_filters';
function load<T>(key: string, fallback: T): T {
if (!browser) return fallback;
try {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : fallback;
} catch {
return fallback;
}
}
function save(key: string, value: unknown) {
if (!browser) return;
localStorage.setItem(key, JSON.stringify(value));
}
let viewMode = $state<ViewMode>('list');
let sort = $state<SortOption>({ field: 'name', direction: 'asc' });
let activeFilters = $state<FilterCriteria>({});
let savedFilters = $state<SavedFilter[]>([]);
let initialized = $state(false);
export const viewStore = {
get viewMode() {
return viewMode;
},
get sort() {
return sort;
},
get activeFilters() {
return activeFilters;
},
get savedFilters() {
return savedFilters;
},
get hasActiveFilters() {
return !!(
activeFilters.search ||
activeFilters.status?.length ||
activeFilters.locationId ||
activeFilters.categoryId ||
activeFilters.tagIds?.length ||
activeFilters.collectionId
);
},
initialize() {
if (initialized) return;
viewMode = load<ViewMode>(VIEW_KEY, 'list');
sort = load<SortOption>(SORT_KEY, { field: 'name', direction: 'asc' });
savedFilters = load<SavedFilter[]>(FILTERS_KEY, []);
initialized = true;
},
setViewMode(mode: ViewMode) {
viewMode = mode;
save(VIEW_KEY, mode);
},
setSort(newSort: SortOption) {
sort = newSort;
save(SORT_KEY, newSort);
},
setFilters(filters: FilterCriteria) {
activeFilters = filters;
},
updateFilter<K extends keyof FilterCriteria>(key: K, value: FilterCriteria[K]) {
activeFilters = { ...activeFilters, [key]: value };
},
clearFilters() {
activeFilters = {};
},
saveFilter(name: string) {
const filter: SavedFilter = {
id: crypto.randomUUID(),
name,
criteria: { ...activeFilters },
createdAt: new Date().toISOString(),
};
savedFilters = [...savedFilters, filter];
save(FILTERS_KEY, savedFilters);
},
loadFilter(id: string) {
const filter = savedFilters.find((f) => f.id === id);
if (filter) {
activeFilters = { ...filter.criteria };
}
},
deleteSavedFilter(id: string) {
savedFilters = savedFilters.filter((f) => f.id !== id);
save(FILTERS_KEY, savedFilters);
},
};
export const viewStore = createViewStore<ViewMode, FilterCriteria>({
storagePrefix: 'inventar',
defaultViewMode: 'list',
defaultSort: { field: 'name', direction: 'asc' },
hasActiveFilters: (f) =>
!!(
f.search ||
f.status?.length ||
f.locationId ||
f.categoryId ||
f.tagIds?.length ||
f.collectionId
),
});

View file

@ -2,109 +2,21 @@
* View Store manages view mode, sort, and filter state for Times.
*/
import { browser } from '$app/environment';
import type { ViewMode, SortOption, FilterCriteria, SavedFilter } from '$lib/modules/times/types';
import { createViewStore } from '@manacore/shared-stores';
import type { ViewMode, FilterCriteria } from '$lib/modules/times/types';
const VIEW_KEY = 'times_view_mode';
const SORT_KEY = 'times_sort';
const FILTERS_KEY = 'times_saved_filters';
function load<T>(key: string, fallback: T): T {
if (!browser) return fallback;
try {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : fallback;
} catch {
return fallback;
}
}
function save(key: string, value: unknown) {
if (!browser) return;
localStorage.setItem(key, JSON.stringify(value));
}
let viewMode = $state<ViewMode>('week');
let sort = $state<SortOption>({ field: 'date', direction: 'desc' });
let activeFilters = $state<FilterCriteria>({});
let savedFilters = $state<SavedFilter[]>([]);
let initialized = $state(false);
export const viewStore = {
get viewMode() {
return viewMode;
},
get sort() {
return sort;
},
get activeFilters() {
return activeFilters;
},
get savedFilters() {
return savedFilters;
},
get hasActiveFilters() {
return !!(
activeFilters.search ||
activeFilters.projectId ||
activeFilters.clientId ||
activeFilters.tagIds?.length ||
activeFilters.isBillable !== undefined ||
activeFilters.dateFrom ||
activeFilters.dateTo
);
},
initialize() {
if (initialized) return;
viewMode = load<ViewMode>(VIEW_KEY, 'week');
sort = load<SortOption>(SORT_KEY, { field: 'date', direction: 'desc' });
savedFilters = load<SavedFilter[]>(FILTERS_KEY, []);
initialized = true;
},
setViewMode(mode: ViewMode) {
viewMode = mode;
save(VIEW_KEY, mode);
},
setSort(newSort: SortOption) {
sort = newSort;
save(SORT_KEY, newSort);
},
setFilters(filters: FilterCriteria) {
activeFilters = filters;
},
updateFilter<K extends keyof FilterCriteria>(key: K, value: FilterCriteria[K]) {
activeFilters = { ...activeFilters, [key]: value };
},
clearFilters() {
activeFilters = {};
},
saveFilter(name: string) {
const filter: SavedFilter = {
id: crypto.randomUUID(),
name,
criteria: { ...activeFilters },
createdAt: new Date().toISOString(),
};
savedFilters = [...savedFilters, filter];
save(FILTERS_KEY, savedFilters);
},
loadFilter(id: string) {
const filter = savedFilters.find((f) => f.id === id);
if (filter) {
activeFilters = { ...filter.criteria };
}
},
deleteSavedFilter(id: string) {
savedFilters = savedFilters.filter((f) => f.id !== id);
save(FILTERS_KEY, savedFilters);
},
};
export const viewStore = createViewStore<ViewMode, FilterCriteria>({
storagePrefix: 'times',
defaultViewMode: 'week',
defaultSort: { field: 'date', direction: 'desc' },
hasActiveFilters: (f) =>
!!(
f.search ||
f.projectId ||
f.clientId ||
f.tagIds?.length ||
f.isBillable !== undefined ||
f.dateFrom ||
f.dateTo
),
});