From fe1f9c8989d2e1d9ec984c212d9f9823e25bf807 Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Mon, 6 Jul 2026 09:10:10 +0700 Subject: [PATCH] [tasks] persist filter state across navigation via shared store When navigating from a task list to a task detail and back, filter state (statuses, priorities, project, sort, pagination) is now preserved via a module-level Svelte 5 store keyed by route. New: stores/task-filters.svelte.ts --- .../src/lib/pages/dashboard-personal.svelte | 21 +++++++++++++++ frontend/src/lib/pages/dashboard-tasks.svelte | 22 +++++++++++++++ frontend/src/lib/pages/project-tasks.svelte | 27 +++++++++++++++++++ .../src/lib/stores/task-filters.svelte.ts | 21 +++++++++++++++ 4 files changed, 91 insertions(+) create mode 100644 frontend/src/lib/stores/task-filters.svelte.ts diff --git a/frontend/src/lib/pages/dashboard-personal.svelte b/frontend/src/lib/pages/dashboard-personal.svelte index c269505..fa3db84 100644 --- a/frontend/src/lib/pages/dashboard-personal.svelte +++ b/frontend/src/lib/pages/dashboard-personal.svelte @@ -9,9 +9,12 @@ import * as Card from "$lib/components/ui/card"; import type { Task, Project } from "$lib/types/api"; import { ACTIVE_STATUSES } from "$lib/types/api"; + import { getSnapshot, saveSnapshot } from "$lib/stores/task-filters.svelte"; let { params = {} }: { params?: Record } = $props(); + const ROUTE_KEY = "/dashboard"; + let createdTasks = $state([]); let assignedTasks = $state([]); let projects = $state([]); @@ -25,6 +28,14 @@ let searchQuery = $state(""); let sort = $state("-created_at"); + let saved = getSnapshot(ROUTE_KEY); + if (saved) { + filterProject = saved.filterProject ?? ""; + filterStatuses = saved.filterStatuses; + filterPriorities = saved.filterPriorities; + sort = saved.sort; + } + function toggleStatus(s: string) { if (filterStatuses.includes(s)) { filterStatuses = filterStatuses.filter((x) => x !== s); @@ -60,6 +71,16 @@ navigate(`/tasks/${task.id}`); } + $effect(() => { + saveSnapshot(ROUTE_KEY, { + filterProject: filterProject || undefined, + filterStatuses: [...filterStatuses], + filterPriorities: [...filterPriorities], + sort, + offset: 0, + }); + }); + $effect(() => { loading = true; error = ""; diff --git a/frontend/src/lib/pages/dashboard-tasks.svelte b/frontend/src/lib/pages/dashboard-tasks.svelte index 742dc73..b7f4e42 100644 --- a/frontend/src/lib/pages/dashboard-tasks.svelte +++ b/frontend/src/lib/pages/dashboard-tasks.svelte @@ -11,9 +11,12 @@ import { Button } from "$lib/components/ui/button"; import type { Project, Task } from "$lib/types/api"; import { ACTIVE_STATUSES } from "$lib/types/api"; + import { getSnapshot, saveSnapshot } from "$lib/stores/task-filters.svelte"; let { params = {} }: { params?: Record } = $props(); + const ROUTE_KEY = "/dashboard/all"; + let tasks = $state([]); let total = $state(0); let projects = $state([]); @@ -28,6 +31,15 @@ let offset = $state(0); const limit = 50; + let saved = getSnapshot(ROUTE_KEY); + if (saved) { + filterProject = saved.filterProject ?? ""; + filterStatuses = saved.filterStatuses; + filterPriorities = saved.filterPriorities; + sort = saved.sort; + offset = saved.offset; + } + function toggleStatus(s: string) { if (filterStatuses.includes(s)) { filterStatuses = filterStatuses.filter((x) => x !== s); @@ -98,6 +110,16 @@ $effect(load); + $effect(() => { + saveSnapshot(ROUTE_KEY, { + filterProject: filterProject || undefined, + filterStatuses: [...filterStatuses], + filterPriorities: [...filterPriorities], + sort, + offset, + }); + }); + function handleTaskClick(task: Task) { navigate(`/tasks/${task.id}`); } diff --git a/frontend/src/lib/pages/project-tasks.svelte b/frontend/src/lib/pages/project-tasks.svelte index 7bef64d..907ed0e 100644 --- a/frontend/src/lib/pages/project-tasks.svelte +++ b/frontend/src/lib/pages/project-tasks.svelte @@ -12,17 +12,20 @@ import { Button } from "$lib/components/ui/button"; import type { Project, Task } from "$lib/types/api"; import { ACTIVE_STATUSES } from "$lib/types/api"; + import { getSnapshot, saveSnapshot } from "$lib/stores/task-filters.svelte"; // svelte-ignore a11y_click_events_have_key_events let { params = {} }: { params?: Record } = $props(); let slug = $derived(params.slug || ""); + let routeKey = $derived(`/projects/${slug}`); let project = $state(null); let tasks = $state([]); let total = $state(0); let loading = $state(true); let error = $state(""); + let currentRouteKey = $state(""); let sort = $state("-created_at"); let offset = $state(0); const limit = 50; @@ -31,6 +34,19 @@ let filterPriorities = $state([]); let searchQuery = $state(""); + $effect(() => { + const saved = getSnapshot(routeKey); + if (saved) { + filterStatuses = saved.filterStatuses; + filterPriorities = saved.filterPriorities; + sort = saved.sort; + offset = saved.offset; + } else { + searchQuery = ""; + sort = "-created_at"; + } + }); + function toggleStatus(s: string) { if (filterStatuses.includes(s)) { filterStatuses = filterStatuses.filter((x) => x !== s); @@ -78,12 +94,23 @@ error = e.message || "Failed to load data"; }) .finally(() => { + currentRouteKey = routeKey; loading = false; }); } $effect(load); + $effect(() => { + if (routeKey !== currentRouteKey) return; + saveSnapshot(routeKey, { + filterStatuses: [...filterStatuses], + filterPriorities: [...filterPriorities], + sort, + offset, + }); + }); + function handleSort(field: string) { sort = sort === field ? `-${field}` : field; offset = 0; diff --git a/frontend/src/lib/stores/task-filters.svelte.ts b/frontend/src/lib/stores/task-filters.svelte.ts new file mode 100644 index 0000000..40beebe --- /dev/null +++ b/frontend/src/lib/stores/task-filters.svelte.ts @@ -0,0 +1,21 @@ +export type FilterSnapshot = { + filterProject?: string + filterStatuses: string[] + filterPriorities: string[] + sort: string + offset: number +} + +let store = $state>({}) + +export function getSnapshot(path: string): FilterSnapshot | null { + return store[path] ?? null +} + +export function saveSnapshot(path: string, snapshot: FilterSnapshot): void { + store[path] = snapshot +} + +export function clearSnapshot(path: string): void { + delete store[path] +}