From e6766cba56fee33129ae5ef1bddffe26a4c7441c Mon Sep 17 00:00:00 2001 From: mina Date: Tue, 15 Sep 2026 17:03:28 +0200 Subject: [PATCH 1/2] chore: persist filters and favorites using Web Storage --- src/pages/CharactersPage.tsx | 40 +++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/pages/CharactersPage.tsx b/src/pages/CharactersPage.tsx index e4df42c..737d3a9 100644 --- a/src/pages/CharactersPage.tsx +++ b/src/pages/CharactersPage.tsx @@ -20,12 +20,42 @@ export default function CharactersPage() { const [characters, setCharacters] = useState([]); const [selectedCharacter, setSelectedCharacter] = useState(null); const [searchQuery, setsearchQuery] = useState(""); - const [selectedFilters, setSelectedFilters] = useState>({ - gender: [], - species: [], - status: [] + const [selectedFilters, setSelectedFilters] = useState>(() => { + const storedFilters = sessionStorage.getItem("selectedFilters"); + + if (storedFilters) { + return JSON.parse(storedFilters); + } + + return { + gender: [], + species: [], + status: [] + }; + }); + const [favoriteCharacterIds, setFavoriteCharacterIds] = useState(() => { + const storedFavorites = localStorage.getItem("favoriteCharacterIds"); + + if (storedFavorites) { + return JSON.parse(storedFavorites); + } + + return []; }); - const [favoriteCharacterIds, setFavoriteCharacterIds] = useState([]); + + useEffect(() => { + sessionStorage.setItem( + "selectedFilters", + JSON.stringify(selectedFilters) + ); + }, [selectedFilters]); + + useEffect(() => { + localStorage.setItem( + "favoriteCharacterIds", + JSON.stringify(favoriteCharacterIds) + ); + }, [favoriteCharacterIds]); useEffect(() => { async function fetchCharacters() { From 0289a9eaef397cfd96785e8fbb8ecb853862baea Mon Sep 17 00:00:00 2001 From: mina Date: Tue, 15 Sep 2026 17:07:29 +0200 Subject: [PATCH 2/2] feat: add favorite toggle to character detail card --- src/components/CharacterDetailCard.css | 4 ++++ src/components/CharacterDetailCard.tsx | 21 ++++++++++++++++++--- src/pages/CharactersPage.tsx | 2 ++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/components/CharacterDetailCard.css b/src/components/CharacterDetailCard.css index 2d0fd84..4cc43b2 100644 --- a/src/components/CharacterDetailCard.css +++ b/src/components/CharacterDetailCard.css @@ -167,6 +167,10 @@ filter: brightness(0) invert(1); } +.detail-star-favorite { + filter: brightness(0) saturate(100%) invert(84%) sepia(85%) saturate(829%) hue-rotate(359deg) brightness(105%) contrast(104%); +} + @media (max-width: 600px) { .detail-overlay { padding: 1rem; diff --git a/src/components/CharacterDetailCard.tsx b/src/components/CharacterDetailCard.tsx index 6fe547c..fb1b0f7 100644 --- a/src/components/CharacterDetailCard.tsx +++ b/src/components/CharacterDetailCard.tsx @@ -13,6 +13,8 @@ type CharacterDetailCardProps = { onClose: () => void; onPrevious: () => void; onNext: () => void; + isFavorite: boolean; + onToggleFavorite: () => void; } type Episode = { @@ -21,7 +23,7 @@ type Episode = { episode: string; } -export default function CharacterDetailCard({ character, onClose, onPrevious, onNext }: CharacterDetailCardProps) { +export default function CharacterDetailCard({ character, onClose, onPrevious, onNext, isFavorite, onToggleFavorite }: CharacterDetailCardProps) { const [episodes, setEpisodes] = useState([]); @@ -61,8 +63,21 @@ export default function CharacterDetailCard({ character, onClose, onPrevious, on

{character.name}

-
diff --git a/src/pages/CharactersPage.tsx b/src/pages/CharactersPage.tsx index 737d3a9..727c985 100644 --- a/src/pages/CharactersPage.tsx +++ b/src/pages/CharactersPage.tsx @@ -180,6 +180,8 @@ export default function CharactersPage() { onClose={() => setSelectedCharacter(null)} onPrevious={showPreviousCharacter} onNext={showNextCharacter} + isFavorite={favoriteCharacterIds.includes(selectedCharacter.id)} + onToggleFavorite={() => handleToggleFavorite(selectedCharacter.id)} /> )}