Skip to content

Commit ef5bd72

Browse files
committed
feat: add stale boards indication
1 parent ee6441c commit ef5bd72

5 files changed

Lines changed: 81 additions & 19 deletions

File tree

frontend/testing-view/src/features/filtering/components/FilterController.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { useShallow } from "zustand/shallow";
2+
import { detectExtraBoards } from "../../../lib/utils";
13
import { useStore } from "../../../store/store";
24
import { FilterCategoryItem } from "./FilterCategoryItem";
35
import { FilterDialog } from "./FilterDialog";
@@ -7,12 +9,15 @@ export const FilterController = () => {
79
const close = useStore((s) => s.closeFilterDialog);
810

911
const boards = useStore((s) => s.boards);
12+
const activeFilters = useStore(useShallow((s) => s.getActiveFilters(scope)));
1013

1114
const clearFilters = useStore((s) => s.clearFilters);
1215
const selectAllFilters = useStore((s) => s.selectAllFilters);
1316

1417
if (!scope) return null;
1518

19+
const extraBoards = detectExtraBoards(activeFilters, boards);
20+
1621
return (
1722
<FilterDialog
1823
title={`Filter ${scope === "commands" ? "commands" : "telemetry packets"}`}
@@ -22,6 +27,7 @@ export const FilterController = () => {
2227
onClearAll={() => clearFilters(scope)}
2328
onSelectAll={() => selectAllFilters(scope)}
2429
categories={boards}
30+
extraCategories={extraBoards}
2531
FilterCategoryComponent={FilterCategoryItem}
2632
/>
2733
);

frontend/testing-view/src/features/filtering/components/FilterDialog.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
DialogHeader,
77
DialogTitle,
88
} from "@workspace/ui";
9+
import { AlertTriangle } from "@workspace/ui/icons";
910
import { type ComponentType } from "react";
1011
import type { BoardName } from "../../../types/data/board";
1112

@@ -17,6 +18,7 @@ interface FilterDialogProps {
1718
onClearAll: () => void;
1819
onSelectAll: () => void;
1920
categories: readonly BoardName[];
21+
extraCategories: readonly BoardName[];
2022
FilterCategoryComponent: ComponentType<{ category: BoardName }>;
2123
}
2224

@@ -28,11 +30,13 @@ export const FilterDialog = ({
2830
onClearAll,
2931
onSelectAll,
3032
categories,
33+
extraCategories,
3134
FilterCategoryComponent,
3235
}: FilterDialogProps) => {
36+
console.log(extraCategories);
3337
return (
3438
<Dialog open={isOpen} onOpenChange={onClose}>
35-
<DialogContent className="bg-background text-foreground max-h-[85vh] w-full min-w-[600px] max-w-2xl overflow-y-auto px-10 py-8">
39+
<DialogContent className="bg-background text-foreground max-h-[85vh] w-full max-w-2xl min-w-[600px] overflow-y-auto px-10 py-8">
3640
<DialogHeader>
3741
<DialogTitle className="text-2xl font-semibold">{title}</DialogTitle>
3842
{description && <DialogDescription>{description}</DialogDescription>}
@@ -47,6 +51,30 @@ export const FilterDialog = ({
4751
</Button>
4852
</div>
4953

54+
{extraCategories.length > 0 && (
55+
<div className="mt-2 rounded-md border border-amber-500/30 bg-amber-500/15 p-4 text-sm text-amber-600 dark:text-amber-400">
56+
<div className="flex items-center gap-2 font-semibold">
57+
<AlertTriangle className="h-4 w-4" />
58+
Stale filters detected
59+
</div>
60+
<p className="mt-1 opacity-90">
61+
The following boards are in your saved filters but not in the
62+
current configuration:{" "}
63+
<span className="font-mono font-bold">
64+
{extraCategories.join(", ")}
65+
</span>
66+
</p>
67+
<Button
68+
variant="outline"
69+
size="sm"
70+
className="mt-3 border-amber-500/30 hover:bg-amber-500/10"
71+
onClick={onClearAll}
72+
>
73+
Clear Stale Filters
74+
</Button>
75+
</div>
76+
)}
77+
5078
<div className="space-y-1">
5179
{categories.map((category) => (
5280
<FilterCategoryComponent key={category} category={category} />

frontend/testing-view/src/features/filtering/store/filteringSlice.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export interface FilteringSlice {
3838
workspaceFilters: Record<string, WorkspaceFilters>;
3939
initializeWorkspaceFilters: () => void;
4040
updateFilters: (scope: FilterScope, filters: TabFilter) => void;
41-
getActiveFilters: (scope: FilterScope) => TabFilter | undefined;
41+
getActiveFilters: (scope: FilterScope | null) => TabFilter | undefined;
4242

4343
/** Filter Actions */
4444
selectAllFilters: (scope: FilterScope) => void;
@@ -229,6 +229,7 @@ export const createFilteringSlice: StateCreator<
229229
// Helper getters
230230
getActiveFilters: (scope) => {
231231
const id = get().getActiveWorkspaceId();
232+
if (!scope) return {};
232233
return id ? get().workspaceFilters[id]?.[scope] : undefined;
233234
},
234235

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Button } from "@workspace/ui";
2-
import { ListFilterPlus } from "@workspace/ui/icons";
2+
import { AlertTriangle, ListFilterPlus } from "@workspace/ui/icons";
3+
import { useShallow } from "zustand/shallow";
4+
import { detectExtraBoards } from "../../../../../lib/utils";
35
import { useStore } from "../../../../../store/store";
46
import type { SidebarTab } from "../../../types/sidebar";
57

@@ -13,23 +15,40 @@ export const TabHeader = ({ title, scope }: TabHeaderProps) => {
1315
const totalCount = useStore((state) => state.getTotalCount(scope));
1416
const filteredCount = useStore((state) => state.getFilteredCount(scope));
1517

18+
const boards = useStore((s) => s.boards);
19+
const activeFilters = useStore(useShallow((s) => s.getActiveFilters(scope)));
20+
const extraBoards = detectExtraBoards(activeFilters, boards);
21+
1622
return (
17-
<div className="flex items-center justify-between gap-2 py-4">
18-
<h3 className="text-foreground text-lg font-semibold">
19-
{title}
20-
<span className="text-muted-foreground ml-2 text-sm font-normal">
21-
{filteredCount} / {totalCount}
22-
</span>
23-
</h3>
24-
<Button
25-
onClick={() => openFilterDialog(scope)}
26-
size="sm"
27-
variant="secondary"
28-
className="ring-border/50 hover:ring-primary/30 gap-2 shadow-sm ring-1 transition-all"
29-
>
30-
<ListFilterPlus className="h-4 w-4" />
31-
Filter
32-
</Button>
23+
<div className="flex flex-col gap-1 py-4">
24+
<div className="flex items-center justify-between gap-2">
25+
<h3 className="text-foreground text-lg font-semibold">
26+
{title}
27+
<span className="text-muted-foreground ml-2 text-sm font-normal">
28+
{filteredCount} / {totalCount}
29+
</span>
30+
</h3>
31+
<Button
32+
onClick={() => openFilterDialog(scope)}
33+
size="sm"
34+
variant="secondary"
35+
className="ring-border/50 hover:ring-primary/30 gap-2 shadow-sm ring-1 transition-all"
36+
>
37+
<ListFilterPlus className="h-4 w-4" />
38+
Filter
39+
</Button>
40+
</div>
41+
42+
{/* Warning for stale boards */}
43+
{extraBoards.length > 0 && (
44+
<div
45+
className="flex cursor-help items-center gap-1.5 text-[11px] font-medium text-amber-500"
46+
title={`Stale boards in filters: ${extraBoards.join(", ")}`}
47+
>
48+
<AlertTriangle className="h-3.5 w-3.5" />
49+
<span>{extraBoards.length} stale board(s) affecting counts</span>
50+
</div>
51+
)}
3352
</div>
3453
);
3554
};

frontend/testing-view/src/lib/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,11 @@ export const formatTimestamp = (ts: MessageTimestamp) => {
119119
if (!ts) return "00:00:00";
120120
return `${ts.hour.toString().padStart(2, "0")}:${ts.minute.toString().padStart(2, "0")}:${ts.second.toString().padStart(2, "0")}`;
121121
};
122+
123+
export const detectExtraBoards = (
124+
activeFilters: TabFilter | undefined,
125+
boards: BoardName[],
126+
) =>
127+
Object.keys(activeFilters || {}).filter(
128+
(key) => !boards.includes(key),
129+
) as BoardName[];

0 commit comments

Comments
 (0)