mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:01:08 +02:00
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:
parent
934f3337e3
commit
b1a5c95f1d
2 changed files with 33 additions and 217 deletions
|
|
@ -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
|
||||
),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
),
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue