Skip to content

Commit ae0a2bf

Browse files
committed
Save search bar and filter state using sessionStorage (fixes #3257)
1 parent ea379af commit ae0a2bf

File tree

1 file changed

+130
-9
lines changed

1 file changed

+130
-9
lines changed

react_frontend/src/components/buttons/Search.tsx

Lines changed: 130 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ import {
1111
MenuItem,
1212
} from "@mui/material";
1313
import { useHomepage } from "Contexts/HomepageContext";
14-
import { useState } from "react";
14+
import { useState, useEffect } from "react";
1515
import { useAcademyTheme } from "Contexts/AcademyThemeContext";
1616

17+
// SessionStorage keys
18+
const FILTER_STORAGE_KEY = "ra_home_filters_v1";
19+
const SEARCH_STORAGE_KEY = "ra_home_search_v1";
20+
1721
// Estilos
1822
const Search = styled("div")(({ roundness }: { roundness: number }) => ({
1923
position: "relative",
@@ -85,13 +89,67 @@ const StyledMenu = styled(Menu)(
8589
})
8690
);
8791

92+
// Filter persistance structure
93+
type FilterState = {
94+
tags: boolean;
95+
description: boolean;
96+
status: boolean;
97+
};
98+
99+
const DEFAULT_FILTER_STATE: FilterState = {
100+
tags: true,
101+
description: false,
102+
status: false,
103+
};
104+
88105
// Componente FilterMenu
89106
const FilterMenu = () => {
90107
const { appendFilterItem } = useHomepage();
91108
const theme = useAcademyTheme();
92109
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
93110
const open: boolean = Boolean(anchorEl);
94111

112+
const [filterState, setFilterState] = useState<FilterState>(
113+
DEFAULT_FILTER_STATE
114+
);
115+
116+
// Restore session filter state
117+
useEffect(() => {
118+
if (typeof window === "undefined") return;
119+
120+
try {
121+
const raw = window.sessionStorage.getItem(FILTER_STORAGE_KEY);
122+
if (!raw) return;
123+
124+
const parsed = JSON.parse(raw) as Partial<FilterState>;
125+
const persisted: FilterState = {
126+
tags:
127+
typeof parsed.tags === "boolean"
128+
? parsed.tags
129+
: DEFAULT_FILTER_STATE.tags,
130+
description:
131+
typeof parsed.description === "boolean"
132+
? parsed.description
133+
: DEFAULT_FILTER_STATE.description,
134+
status:
135+
typeof parsed.status === "boolean"
136+
? parsed.status
137+
: DEFAULT_FILTER_STATE.status,
138+
};
139+
140+
setFilterState(persisted);
141+
142+
// Sync context with persisted values
143+
(["tags", "description", "status"] as const).forEach((key) => {
144+
if (persisted[key] !== DEFAULT_FILTER_STATE[key]) {
145+
appendFilterItem(key);
146+
}
147+
});
148+
} catch (err) {
149+
console.error("Failed to restore filter state", err);
150+
}
151+
}, [appendFilterItem]);
152+
95153
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
96154
setAnchorEl(event.currentTarget);
97155
};
@@ -100,9 +158,33 @@ const FilterMenu = () => {
100158
setAnchorEl(null);
101159
};
102160

103-
const handleFilterList = (e: any) => {
104-
const item = e.target.name;
105-
appendFilterItem(item);
161+
// Toggle and persist filter values
162+
const handleFilterCheckboxChange = (
163+
e: React.ChangeEvent<HTMLInputElement>
164+
) => {
165+
const name = e.target.name as keyof FilterState;
166+
167+
setFilterState((prev) => {
168+
const updated: FilterState = {
169+
...prev,
170+
[name]: !prev[name],
171+
};
172+
173+
if (typeof window !== "undefined") {
174+
try {
175+
window.sessionStorage.setItem(
176+
FILTER_STORAGE_KEY,
177+
JSON.stringify(updated)
178+
);
179+
} catch (err) {
180+
console.error("Failed to save filter state", err);
181+
}
182+
}
183+
184+
return updated;
185+
});
186+
187+
appendFilterItem(name);
106188
};
107189

108190
return (
@@ -127,29 +209,36 @@ const FilterMenu = () => {
127209
checkboxColor={theme.palette.text!}
128210
roundness={theme.roundness!}
129211
>
212+
{/* Name always active and not persisted */}
130213
<MenuItem>
131214
<Checkbox defaultChecked disabled size="small" name="name" />
132215
Name
133216
</MenuItem>
134217
<MenuItem>
135218
<Checkbox
136-
defaultChecked
137219
size="small"
138-
onClick={handleFilterList}
139220
name="tags"
221+
checked={filterState.tags}
222+
onChange={handleFilterCheckboxChange}
140223
/>
141224
Tags
142225
</MenuItem>
143226
<MenuItem>
144227
<Checkbox
145228
size="small"
146-
onClick={handleFilterList}
147229
name="description"
230+
checked={filterState.description}
231+
onChange={handleFilterCheckboxChange}
148232
/>
149233
Description
150234
</MenuItem>
151235
<MenuItem>
152-
<Checkbox size="small" onClick={handleFilterList} name="status" />
236+
<Checkbox
237+
size="small"
238+
name="status"
239+
checked={filterState.status}
240+
onChange={handleFilterCheckboxChange}
241+
/>
153242
Status
154243
</MenuItem>
155244
</StyledMenu>
@@ -162,9 +251,39 @@ const SearchBar = () => {
162251
const { setSearchBarText } = useHomepage();
163252
const theme = useAcademyTheme();
164253

254+
const [searchValue, setSearchValue] = useState<string>("");
255+
256+
// Restore session filter state
257+
useEffect(() => {
258+
if (typeof window === "undefined") return;
259+
260+
try {
261+
const saved = window.sessionStorage.getItem(SEARCH_STORAGE_KEY);
262+
if (saved) {
263+
setSearchValue(saved);
264+
265+
setSearchBarText(saved.toLowerCase());
266+
}
267+
} catch (err) {
268+
console.error("Failed to restore search text", err);
269+
}
270+
}, [setSearchBarText]);
271+
272+
// Update sessionStorage + context
165273
const inputHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
166-
const lowerCase = e.target.value.toLowerCase();
274+
const value = e.target.value;
275+
setSearchValue(value);
276+
277+
const lowerCase = value.toLowerCase();
167278
setSearchBarText(lowerCase);
279+
280+
if (typeof window !== "undefined") {
281+
try {
282+
window.sessionStorage.setItem(SEARCH_STORAGE_KEY, value);
283+
} catch (err) {
284+
console.error("Failed to save search text", err);
285+
}
286+
}
168287
};
169288

170289
return (
@@ -181,6 +300,7 @@ const SearchBar = () => {
181300
</SearchIconWrapper>
182301
<StyledInputBase
183302
placeholder="Search…"
303+
value={searchValue}
184304
onChange={inputHandler}
185305
textColor={theme.palette.text!}
186306
inputProps={{ "aria-label": "search" }}
@@ -192,3 +312,4 @@ const SearchBar = () => {
192312
};
193313

194314
export default SearchBar;
315+

0 commit comments

Comments
 (0)