From 2110a592fa959cc3e40435bd79830546e8ef3729 Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Mon, 6 Jul 2026 09:10:10 +0700 Subject: [PATCH 1/2] [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 | 19 ++++++++++++++++ .../src/lib/stores/task-filters.svelte.ts | 21 ++++++++++++++++++ 4 files changed, 83 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..4dda22e 100644 --- a/frontend/src/lib/pages/project-tasks.svelte +++ b/frontend/src/lib/pages/project-tasks.svelte @@ -12,11 +12,13 @@ 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([]); @@ -31,6 +33,14 @@ let filterPriorities = $state([]); let searchQuery = $state(""); + let saved = getSnapshot(routeKey); + if (saved) { + 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); @@ -84,6 +94,15 @@ $effect(load); + $effect(() => { + 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] +} From 00dfc34f2823068cf333fb78202b01f5f4cb7a7b Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Thu, 16 Jul 2026 13:28:35 +0700 Subject: [PATCH 2/2] [frontend] fix review issues --- frontend/src/lib/pages/project-tasks.svelte | 22 ++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/frontend/src/lib/pages/project-tasks.svelte b/frontend/src/lib/pages/project-tasks.svelte index 4dda22e..907ed0e 100644 --- a/frontend/src/lib/pages/project-tasks.svelte +++ b/frontend/src/lib/pages/project-tasks.svelte @@ -25,6 +25,7 @@ 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; @@ -33,13 +34,18 @@ let filterPriorities = $state([]); let searchQuery = $state(""); - let saved = getSnapshot(routeKey); - if (saved) { - filterStatuses = saved.filterStatuses; - filterPriorities = saved.filterPriorities; - sort = saved.sort; - offset = saved.offset; - } + $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)) { @@ -88,6 +94,7 @@ error = e.message || "Failed to load data"; }) .finally(() => { + currentRouteKey = routeKey; loading = false; }); } @@ -95,6 +102,7 @@ $effect(load); $effect(() => { + if (routeKey !== currentRouteKey) return; saveSnapshot(routeKey, { filterStatuses: [...filterStatuses], filterPriorities: [...filterPriorities],