From 9ade29ea84e01fb447516616109e489fc0dc7956 Mon Sep 17 00:00:00 2001 From: cavidelizade Date: Fri, 17 Jul 2026 01:09:33 +0400 Subject: [PATCH] fix(ui): reset module and workspace-view filters when the scope changes The modules filter provider lives at the app-shell root and outlives the page, so a project's status/lead/member filters (project-specific ids) carried over into the next project, usually hiding everything. ModulesPage now resets the filter when the project id changes, tracked at module scope so it survives the page remounting between routes. Workspace views had the mirror problem: switching from a custom view (with saved filters) to a static view left the custom view's filters applied, because the apply effect early-returned for static views. Now it clears filters back to defaults when the target view is static. Closes #339 Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/web/src/contexts/ModulesFilterContext.tsx | 6 ++++++ apps/web/src/pages/ModulesPage.tsx | 13 +++++++++++++ apps/web/src/pages/WorkspaceViewsPage.tsx | 5 +++++ 3 files changed, 24 insertions(+) diff --git a/apps/web/src/contexts/ModulesFilterContext.tsx b/apps/web/src/contexts/ModulesFilterContext.tsx index e8ef26be..de3edc40 100644 --- a/apps/web/src/contexts/ModulesFilterContext.tsx +++ b/apps/web/src/contexts/ModulesFilterContext.tsx @@ -36,6 +36,8 @@ export interface ModulesFilterContextValue extends ModulesFilterState { setDueAfter: (v: string | null) => void; setDueBefore: (v: string | null) => void; updateFilter: (updater: (prev: ModulesFilterState) => Partial) => void; + /** Reset every filter back to defaults (e.g. when switching projects). */ + reset: () => void; } const defaultState: ModulesFilterState = { @@ -67,6 +69,8 @@ export function ModulesFilterProvider({ children }: { children: ReactNode }) { [], ); + const reset = useCallback(() => setState(defaultState), []); + const setSearch = useCallback((v: string) => { setState((prev) => ({ ...prev, search: v })); }, []); @@ -143,6 +147,7 @@ export function ModulesFilterProvider({ children }: { children: ReactNode }) { setDueAfter, setDueBefore, updateFilter, + reset, }), [ state, @@ -161,6 +166,7 @@ export function ModulesFilterProvider({ children }: { children: ReactNode }) { setDueAfter, setDueBefore, updateFilter, + reset, ], ); diff --git a/apps/web/src/pages/ModulesPage.tsx b/apps/web/src/pages/ModulesPage.tsx index 4bcfeb92..8dad5c9c 100644 --- a/apps/web/src/pages/ModulesPage.tsx +++ b/apps/web/src/pages/ModulesPage.tsx @@ -157,6 +157,12 @@ function ModuleProgressCircle({ progress }: { progress: number }) { ); } +// The modules filter provider lives at the app-shell root and outlives this +// page, so its status/lead/member ids (which are project-specific) would carry +// over into another project. Track the project the filter was last set for at +// module scope so it survives remounts, and reset when the project changes. +let lastFilteredProjectId: string | undefined; + export function ModulesPage() { const { t } = useTranslation(); const { workspaceSlug, projectId } = useParams<{ @@ -164,6 +170,13 @@ export function ModulesPage() { projectId: string; }>(); const filter = useModulesFilter(); + const resetFilter = filter.reset; + useEffect(() => { + if (projectId && projectId !== lastFilteredProjectId) { + lastFilteredProjectId = projectId; + resetFilter(); + } + }, [projectId, resetFilter]); const [workspace, setWorkspace] = useState(null); const [project, setProject] = useState(null); const [modules, setModules] = useState([]); diff --git a/apps/web/src/pages/WorkspaceViewsPage.tsx b/apps/web/src/pages/WorkspaceViewsPage.tsx index 78f4ddff..3e95a714 100644 --- a/apps/web/src/pages/WorkspaceViewsPage.tsx +++ b/apps/web/src/pages/WorkspaceViewsPage.tsx @@ -183,6 +183,11 @@ export function WorkspaceViewsPage() { if (prevViewIdRef.current !== viewId) { prevViewIdRef.current = viewId; viewAppliedRef.current = false; + // Static views (all-issues, assigned, etc.) carry no saved filters, so + // clear any left over from a custom view instead of keeping them applied. + if (!isCustomViewId(viewId)) { + setFilters(parseWorkspaceViewFiltersFromSearchParams(new URLSearchParams())); + } } if (!workspaceSlug || !viewId || !isCustomViewId(viewId) || viewAppliedRef.current) return; viewAppliedRef.current = true;