Skip to content

Commit 7b43cc6

Browse files
committed
Add URL-based filter management for drilldown functionality
Signed-off-by: Rahim <[email protected]>
1 parent de4b0d2 commit 7b43cc6

File tree

2 files changed

+78
-18
lines changed

2 files changed

+78
-18
lines changed

src/pages/Allocations.js

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import AllocationService from "../services/allocation";
1818
import {
1919
checkCustomWindow,
2020
cumulativeToTotals,
21+
parseFilters,
22+
parseFiltersFromUrl,
2123
rangeToCumulative,
2224
toVerboseTimeRange,
2325
} from "../util";
@@ -118,25 +120,50 @@ const ReportsPage = () => {
118120
searchParams.get("title") ||
119121
generateTitle({ window: win, aggregateBy, accumulate });
120122

121-
// Reset filters when aggregateBy changes manually (not through drilldown)
123+
// Load filters from URL on initial load or when URL changes
124+
// Also validate that filters match the current aggregateBy level
122125
useEffect(() => {
123-
// Only reset if aggregateBy changed and we're going back to a higher level
126+
const currentSearchParams = new URLSearchParams(routerLocation.search);
127+
const filterParam = currentSearchParams.get("filter");
124128
const hierarchy = ["namespace", "controllerKind", "controller", "pod", "container"];
125129
const currentIndex = hierarchy.indexOf(aggregateBy);
126130

127-
// If we're at namespace level, always reset filters
128-
// Otherwise, reset if we're going back to a higher level
129-
setFilters((prevFilters) => {
130-
if (currentIndex === 0) {
131-
return [];
131+
if (filterParam) {
132+
const urlFilters = parseFiltersFromUrl(filterParam);
133+
134+
// Validate filters match current aggregateBy level
135+
// If we're at namespace level, there should be no filters
136+
if (currentIndex === 0 && urlFilters.length > 0) {
137+
const newSearchParams = new URLSearchParams(routerLocation.search);
138+
newSearchParams.delete("filter");
139+
navigate({
140+
search: `?${newSearchParams.toString()}`,
141+
}, { replace: true });
142+
setFilters([]);
143+
return;
132144
}
133-
// If current level index is less than number of filters, we went back
134-
if (currentIndex < prevFilters.length) {
135-
return [];
145+
146+
// If current level index is less than number of filters, trim them
147+
if (currentIndex >= 0 && currentIndex < urlFilters.length) {
148+
const trimmedFilters = urlFilters.slice(0, currentIndex);
149+
const newSearchParams = new URLSearchParams(routerLocation.search);
150+
if (trimmedFilters.length > 0) {
151+
newSearchParams.set("filter", parseFilters(trimmedFilters));
152+
} else {
153+
newSearchParams.delete("filter");
154+
}
155+
navigate({
156+
search: `?${newSearchParams.toString()}`,
157+
}, { replace: true });
158+
setFilters(trimmedFilters);
159+
return;
136160
}
137-
return prevFilters;
138-
});
139-
}, [aggregateBy]);
161+
162+
setFilters(urlFilters);
163+
} else {
164+
setFilters([]);
165+
}
166+
}, [routerLocation.search, aggregateBy]);
140167

141168
// When parameters which effect query results change, refetch the data.
142169
useEffect(() => {
@@ -246,10 +273,16 @@ const ReportsPage = () => {
246273
const newFilters = [...filters, newFilter];
247274
setFilters(newFilters);
248275

249-
// Update URL parameters
250-
searchParams.set("agg", nextAgg);
276+
// Update URL parameters with new aggregateBy and filters
277+
const newSearchParams = new URLSearchParams(routerLocation.search);
278+
newSearchParams.set("agg", nextAgg);
279+
if (newFilters.length > 0) {
280+
newSearchParams.set("filter", parseFilters(newFilters));
281+
} else {
282+
newSearchParams.delete("filter");
283+
}
251284
navigate({
252-
search: `?${searchParams.toString()}`,
285+
search: `?${newSearchParams.toString()}`,
253286
});
254287
}
255288

@@ -289,9 +322,12 @@ const ReportsPage = () => {
289322
aggregationOptions={aggregationOptions}
290323
aggregateBy={aggregateBy}
291324
setAggregateBy={(agg) => {
292-
searchParams.set("agg", agg);
325+
const newSearchParams = new URLSearchParams(routerLocation.search);
326+
newSearchParams.set("agg", agg);
327+
// Reset filters when aggregateBy is changed manually
328+
newSearchParams.delete("filter");
293329
navigate({
294-
search: `?${searchParams.toString()}`,
330+
search: `?${newSearchParams.toString()}`,
295331
});
296332
}}
297333
accumulateOptions={accumulateOptions}

src/util.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,30 @@ export function parseFilters(filters) {
446446
);
447447
}
448448

449+
// Parse filters from URL string format: namespace:"my-namespace"+controllerKind:"deployment"
450+
export function parseFiltersFromUrl(filterString) {
451+
if (!filterString || typeof filterString !== "string") {
452+
return [];
453+
}
454+
455+
const filters = [];
456+
// Split by "+" but be careful with escaped quotes
457+
const parts = filterString.split("+");
458+
459+
for (const part of parts) {
460+
// Match pattern: property:"value"
461+
const match = part.match(/^([^:]+):"((?:[^"\\]|\\.)*)"$/);
462+
if (match) {
463+
const property = match[1].trim();
464+
// Unescape the value: replace \" with "
465+
const value = match[2].replace(/\\"/g, '"');
466+
filters.push({ property, value });
467+
}
468+
}
469+
470+
return filters;
471+
}
472+
449473
export default {
450474
rangeToCumulative,
451475
cumulativeToTotals,

0 commit comments

Comments
 (0)