Skip to content

[tasks] persist filter state on back navigation#37

Open
capcom6 wants to merge 1 commit into
masterfrom
fix/persist-task-filters-on-back-nav
Open

[tasks] persist filter state on back navigation#37
capcom6 wants to merge 1 commit into
masterfrom
fix/persist-task-filters-on-back-nav

Conversation

@capcom6

@capcom6 capcom6 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Problem

Task list filters (statuses, priorities, project, sort, pagination) are lost when navigating from a task list to a task detail and back. The filter state was held in local `` variables, which reset on component mount.

Solution

Created a shared Svelte 5 store (stores/task-filters.svelte.ts) that holds filter state in module-level ``, keyed by route path. Each task list page now:

  1. Restores filter state from the store on mount (before the load effect runs)
  2. Persists filter state to the store via a `` whenever filters change

Files

  • New: frontend/src/lib/stores/task-filters.svelte.ts — module-level store keyed by route
  • Modified: dashboard-tasks.svelte — key /dashboard/all
  • Modified: dashboard-personal.svelte — key /dashboard
  • Modified: project-tasks.svelte — per-project key /projects/{slug}

Summary by CodeRabbit

  • New Features
    • Dashboard task filters, sorting, and pagination now persist when navigating away and return automatically when revisiting.
    • Personal and project task views restore their previous filter and sorting selections.
    • Filter state is maintained separately for each dashboard or project view.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Task filter snapshots are stored in memory and keyed by route. Dashboard and project task pages restore saved filter, sorting, and pagination values, then save updated state through reactive effects.

Changes

Task filter persistence

Layer / File(s) Summary
Snapshot store contract
frontend/src/lib/stores/task-filters.svelte.ts
Defines FilterSnapshot and adds path-keyed get, save, and clear operations.
Dashboard state restoration and saving
frontend/src/lib/pages/dashboard-personal.svelte, frontend/src/lib/pages/dashboard-tasks.svelte
Dashboard pages restore saved filter and sort values and persist current state under route-specific keys.
Project task state restoration and saving
frontend/src/lib/pages/project-tasks.svelte
Project task pages derive a route key, restore saved filter state, and persist changes to filters, sorting, and offset.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: dudina-ma

Sequence Diagram(s)

sequenceDiagram
  participant TaskPage
  participant task-filters
  participant SnapshotStore
  TaskPage->>task-filters: getSnapshot(route key)
  task-filters->>SnapshotStore: read FilterSnapshot
  SnapshotStore-->>task-filters: saved state or null
  task-filters-->>TaskPage: restore filter, sort, and offset
  TaskPage->>task-filters: saveSnapshot(route key, current state)
  task-filters->>SnapshotStore: write FilterSnapshot
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: persisting task filter state so it restores on back navigation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

🤖 Pull request artifacts

Platform File
🐳 Docker GitHub Container Registry
🍎 Darwin arm64 backend_Darwin_arm64.tar.gz
🍎 Darwin x86_64 backend_Darwin_x86_64.tar.gz
🐧 Linux arm64 backend_Linux_arm64.tar.gz
🐧 Linux i386 backend_Linux_i386.tar.gz
🐧 Linux x86_64 backend_Linux_x86_64.tar.gz
🪟 Windows arm64 backend_Windows_arm64.zip
🪟 Windows i386 backend_Windows_i386.zip
🪟 Windows x86_64 backend_Windows_x86_64.zip

@capcom6 capcom6 changed the title [tasks] Persist filter state on back navigation [tasks] persist filter state on back navigation Jul 6, 2026
@capcom6

capcom6 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@capcom6

capcom6 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.


Your plan includes PR reviews subject to rate limits. More reviews will be available in 35 minutes.

@capcom6

capcom6 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.


Your plan includes PR reviews subject to rate limits. More reviews will be available in 58 minutes.

@capcom6 capcom6 force-pushed the fix/persist-task-filters-on-back-nav branch from 7282a94 to 7d9797e Compare July 8, 2026 01:36
@capcom6 capcom6 requested a review from dudina-ma July 8, 2026 07:54
@capcom6 capcom6 added the ready PR is ready to merge label Jul 8, 2026
@capcom6 capcom6 force-pushed the fix/persist-task-filters-on-back-nav branch from 7d9797e to f4fc60d Compare July 9, 2026 00:59
@github-actions github-actions Bot removed the ready PR is ready to merge label Jul 9, 2026
@capcom6 capcom6 added the ready PR is ready to merge label Jul 9, 2026
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
@capcom6 capcom6 force-pushed the fix/persist-task-filters-on-back-nav branch from f4fc60d to 2110a59 Compare July 14, 2026 01:21
@github-actions github-actions Bot removed the ready PR is ready to merge label Jul 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/lib/pages/project-tasks.svelte`:
- Around line 97-105: Guard the snapshot-saving $effect around saveSnapshot so
it returns without writing until currentRouteKey is synchronized with routeKey
and the new project’s state has been loaded. Once both keys match, preserve the
existing filterStatuses, filterPriorities, sort, and offset payload and save it
under the stable route key.
- Around line 97-105: Guard the snapshot-saving $effect using currentRouteKey so
it only calls saveSnapshot when currentRouteKey matches the reactive routeKey.
Keep the existing filter, priority, sort, and offset snapshot payload unchanged,
preventing the previous project’s state from being written under the new project
key during navigation.
- Around line 36-43: Wrap the snapshot restoration logic around
getSnapshot(routeKey) in a Svelte 5 $effect so it reruns whenever the dynamic
routeKey changes. Restore all saved state for an existing snapshot, and
explicitly clear searchQuery and sort when no snapshot exists to prevent state
leaking between projects.
- Around line 36-43: Wrap the snapshot restoration logic around routeKey in an
$effect so it re-runs when dynamic route parameters change. When a snapshot
exists, restore its filterStatuses, filterPriorities, sort, and offset; when
none exists, clear searchQuery and sort to prevent state leaking between
projects.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fc4a00a0-9cc5-43f7-ab84-02da16042bf3

📥 Commits

Reviewing files that changed from the base of the PR and between 83faa4c and 2110a59.

📒 Files selected for processing (4)
  • frontend/src/lib/pages/dashboard-personal.svelte
  • frontend/src/lib/pages/dashboard-tasks.svelte
  • frontend/src/lib/pages/project-tasks.svelte
  • frontend/src/lib/stores/task-filters.svelte.ts

Comment on lines +36 to +43
let saved = getSnapshot(routeKey);
if (saved) {
filterStatuses = saved.filterStatuses;
filterPriorities = saved.filterPriorities;
sort = saved.sort;
offset = saved.offset;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make snapshot restoration reactive to route changes.

Svelte 5 component script blocks run only once upon initialization. Because routeKey is derived from dynamic route parameters (params.slug), navigating from one project directly to another reuses the same component instance and updates routeKey reactively, but this top-level initialization block will not re-execute. This causes the new project to incorrectly inherit the previous project's filter state.

To fix this, wrap the restoration in an $effect so it reacts to dynamic parameter changes. Ensure searchQuery and sort are cleared when no snapshot exists to prevent UI state from leaking across projects.

🐛 Proposed fix
-  let saved = getSnapshot(routeKey);
-  if (saved) {
-    filterStatuses = saved.filterStatuses;
-    filterPriorities = saved.filterPriorities;
-    sort = saved.sort;
-    offset = saved.offset;
-  }
+  let currentRouteKey = "";
+
+  $effect(() => {
+    if (routeKey !== currentRouteKey) {
+      currentRouteKey = routeKey;
+      let saved = getSnapshot(routeKey);
+      if (saved) {
+        filterStatuses = saved.filterStatuses;
+        filterPriorities = saved.filterPriorities;
+        sort = saved.sort;
+        offset = saved.offset;
+        searchQuery = ""; // Prevent previous search query from leaking
+      } else {
+        resetFilters();
+        sort = "-created_at"; // Prevent sort order from leaking
+      }
+    }
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let saved = getSnapshot(routeKey);
if (saved) {
filterStatuses = saved.filterStatuses;
filterPriorities = saved.filterPriorities;
sort = saved.sort;
offset = saved.offset;
}
let currentRouteKey = "";
$effect(() => {
if (routeKey !== currentRouteKey) {
currentRouteKey = routeKey;
let saved = getSnapshot(routeKey);
if (saved) {
filterStatuses = saved.filterStatuses;
filterPriorities = saved.filterPriorities;
sort = saved.sort;
offset = saved.offset;
searchQuery = ""; // Prevent previous search query from leaking
} else {
resetFilters();
sort = "-created_at"; // Prevent sort order from leaking
}
}
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/lib/pages/project-tasks.svelte` around lines 36 - 43, Wrap the
snapshot restoration logic around getSnapshot(routeKey) in a Svelte 5 $effect so
it reruns whenever the dynamic routeKey changes. Restore all saved state for an
existing snapshot, and explicitly clear searchQuery and sort when no snapshot
exists to prevent state leaking between projects.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Make snapshot restoration reactive to route changes.

Svelte 5 component script blocks only run once on initialization. Because routeKey is derived from dynamic route parameters (params.slug), navigating between projects reuses the component and updates routeKey, but this initialization block will not re-run. This causes the new project to inherit the previous project's state.

Wrap the restoration in an $effect to handle dynamic parameter changes, and ensure searchQuery and sort are cleared to prevent state leaking when no snapshot exists.

🐛 Proposed fix
-  let saved = getSnapshot(routeKey);
-  if (saved) {
-    filterStatuses = saved.filterStatuses;
-    filterPriorities = saved.filterPriorities;
-    sort = saved.sort;
-    offset = saved.offset;
-  }
+  let currentRouteKey = "";
+
+  $effect(() => {
+    if (routeKey !== currentRouteKey) {
+      currentRouteKey = routeKey;
+      let saved = getSnapshot(routeKey);
+      if (saved) {
+        filterStatuses = saved.filterStatuses;
+        filterPriorities = saved.filterPriorities;
+        sort = saved.sort;
+        offset = saved.offset;
+        searchQuery = ""; // Prevent search query from leaking
+      } else {
+        resetFilters();
+        sort = "-created_at"; // Prevent sort order from leaking
+      }
+    }
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let saved = getSnapshot(routeKey);
if (saved) {
filterStatuses = saved.filterStatuses;
filterPriorities = saved.filterPriorities;
sort = saved.sort;
offset = saved.offset;
}
let currentRouteKey = "";
$effect(() => {
if (routeKey !== currentRouteKey) {
currentRouteKey = routeKey;
let saved = getSnapshot(routeKey);
if (saved) {
filterStatuses = saved.filterStatuses;
filterPriorities = saved.filterPriorities;
sort = saved.sort;
offset = saved.offset;
searchQuery = ""; // Prevent search query from leaking
} else {
resetFilters();
sort = "-created_at"; // Prevent sort order from leaking
}
}
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/lib/pages/project-tasks.svelte` around lines 36 - 43, Wrap the
snapshot restoration logic around routeKey in an $effect so it re-runs when
dynamic route parameters change. When a snapshot exists, restore its
filterStatuses, filterPriorities, sort, and offset; when none exists, clear
searchQuery and sort to prevent state leaking between projects.

Comment on lines +97 to +105
$effect(() => {
saveSnapshot(routeKey, {
filterStatuses: [...filterStatuses],
filterPriorities: [...filterPriorities],
sort,
offset,
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Prevent saving old state to the new project during navigation.

When navigating between projects, routeKey updates reactively. If this save $effect executes before the component has loaded the new project's snapshot, it will mistakenly overwrite the new project's storage key with the previous project's filter state.

Add a guard to ensure state is only saved when the route key is stable and fully synchronized with the component state. This implementation assumes currentRouteKey was added as suggested in the previous comment.

🐛 Proposed fix
   $effect(() => {
+    if (routeKey !== currentRouteKey) return;
+
     saveSnapshot(routeKey, {
       filterStatuses: [...filterStatuses],
       filterPriorities: [...filterPriorities],
       sort,
       offset,
     });
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$effect(() => {
saveSnapshot(routeKey, {
filterStatuses: [...filterStatuses],
filterPriorities: [...filterPriorities],
sort,
offset,
});
});
$effect(() => {
if (routeKey !== currentRouteKey) return;
saveSnapshot(routeKey, {
filterStatuses: [...filterStatuses],
filterPriorities: [...filterPriorities],
sort,
offset,
});
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/lib/pages/project-tasks.svelte` around lines 97 - 105, Guard the
snapshot-saving $effect around saveSnapshot so it returns without writing until
currentRouteKey is synchronized with routeKey and the new project’s state has
been loaded. Once both keys match, preserve the existing filterStatuses,
filterPriorities, sort, and offset payload and save it under the stable route
key.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Prevent saving old state to the new project during navigation.

When navigating between projects, routeKey updates reactively. If this $effect runs before the state has been synchronized with the new project's snapshot, it will save the previous project's filter state into the new project's snapshot key.

Add a guard to ensure state is only saved when the route key is stable. This assumes currentRouteKey was added as suggested in the previous comment.

🐛 Proposed fix
   $effect(() => {
+    if (routeKey !== currentRouteKey) return;
+
     saveSnapshot(routeKey, {
       filterStatuses: [...filterStatuses],
       filterPriorities: [...filterPriorities],
       sort,
       offset,
     });
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$effect(() => {
saveSnapshot(routeKey, {
filterStatuses: [...filterStatuses],
filterPriorities: [...filterPriorities],
sort,
offset,
});
});
$effect(() => {
if (routeKey !== currentRouteKey) return;
saveSnapshot(routeKey, {
filterStatuses: [...filterStatuses],
filterPriorities: [...filterPriorities],
sort,
offset,
});
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/lib/pages/project-tasks.svelte` around lines 97 - 105, Guard the
snapshot-saving $effect using currentRouteKey so it only calls saveSnapshot when
currentRouteKey matches the reactive routeKey. Keep the existing filter,
priority, sort, and offset snapshot payload unchanged, preventing the previous
project’s state from being written under the new project key during navigation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant