Skip to content

Commit 745d0d2

Browse files
Merge pull request #117 from Rish6392/favorites-section
added favorites section
2 parents 6729b2d + a3b74e6 commit 745d0d2

File tree

16 files changed

+651
-28
lines changed

16 files changed

+651
-28
lines changed

src/App.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Route, Routes} from "react-router-dom";
33
import {Home} from "./pages/Home";
44
import {Games} from "./pages/Games";
55
import {Activities} from "./pages/Activities";
6+
import Favorites from "./pages/Favorites";
67
import {activities, games} from "./data/content";
78
import {Navbar} from './components/common/Navbar';
89
import "slick-carousel/slick/slick.css";
@@ -24,6 +25,7 @@ function App() {
2425
})
2526
}
2627
<Route exact path="/activities" element={<Activities />} />
28+
<Route exact path="/favorites" element={<Favorites />} />
2729
{
2830
activities.map(activity => {
2931
return (
Lines changed: 4 additions & 0 deletions
Loading

src/assets/icons/star-outline.svg

Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
.fav-button {
2+
/* Match the Generate button visual style from RandomQuote.css */
3+
font-size: 1.2rem;
4+
background: white;
5+
border-radius: 5px;
6+
padding: 18px 20px;
7+
border: 1px solid #4b4b4b;
8+
box-shadow: 1px 1px 3px #4b4b4b;
9+
margin: 10px 0;
10+
transition-duration: 300ms;
11+
cursor: pointer;
12+
}
13+
.fav-button:hover {
14+
transform: scale(1.05);
15+
background: black;
16+
color: white;
17+
}
18+
.fav-button.saved {
19+
background: #ffeaa7;
20+
border-color: #ffcc00;
21+
}
22+
23+
.fav-button.small {
24+
/* helper class if a smaller variant is needed */
25+
padding: 6px 10px;
26+
font-size: 1rem;
27+
box-shadow: none;
28+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React, {useEffect, useState} from 'react';
2+
import './FavoriteButton.css';
3+
import { listFavorites, addFavorite, removeFavorite } from '../../utils/favoritesStorage';
4+
5+
export default function FavoriteButton({ item }) {
6+
const [saved, setSaved] = useState(false);
7+
8+
useEffect(() => {
9+
const all = listFavorites();
10+
const exists = all.some((it) => JSON.stringify(it.content) === JSON.stringify(item.content));
11+
setSaved(!!exists);
12+
}, [item]);
13+
14+
const handleToggle = () => {
15+
if (saved) {
16+
// remove
17+
const all = listFavorites();
18+
const found = all.find((it) => JSON.stringify(it.content) === JSON.stringify(item.content));
19+
if (found) {
20+
removeFavorite(found.id);
21+
setSaved(false);
22+
}
23+
} else {
24+
const added = addFavorite(item);
25+
if (added) setSaved(true);
26+
}
27+
};
28+
29+
return (
30+
<button className={`fav-button ${saved ? 'saved' : ''}`} onClick={handleToggle} title={saved ? 'Remove from favorites' : 'Save to favorites'}>
31+
{saved ? '★ Saved' : '☆ Save'}
32+
</button>
33+
);
34+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.fav-button {
2+
background: transparent;
3+
border: 1px solid #ccc;
4+
padding: 6px 10px;
5+
border-radius: 6px;
6+
cursor: pointer;
7+
}
8+
.fav-button.saved {
9+
background: #ffeaa7;
10+
border-color: #ffcc00;
11+
}

src/components/common/Navbar.js

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import logo from '../../logo.png';
55
import home_icon from '../../assets/icons/home-outline.svg';
66
import game_icon from '../../assets/icons/game-controller-outline.svg';
77
import pulse_icon from '../../assets/icons/pulse-outline.svg';
8+
import star_icon from '../../assets/icons/star-outline.svg';
89

910
const navbarOptions = [
1011
{
@@ -22,39 +23,46 @@ const navbarOptions = [
2223
title: "Activities",
2324
url: "/activities"
2425
}
26+
,
27+
{
28+
icon: star_icon,
29+
title: "Favorites",
30+
url: "/favorites"
31+
}
2532
]
2633
export const Navbar = () => {
2734
let location = useLocation();
2835

2936
useEffect(() => {
30-
const list = document.querySelectorAll('.list');
31-
32-
if (location.pathname === "/") {
33-
list[0].classList.add("active");
34-
list[1].classList.remove("active");
35-
list[2].classList.remove("active");
36-
}
37-
38-
if (location.pathname.includes("/games")) {
39-
list[0].classList.remove("active");
40-
list[1].classList.add("active");
41-
list[2].classList.remove("active");
42-
}
37+
const listItems = document.querySelectorAll('.list');
38+
// clear all
39+
listItems.forEach(li => li.classList.remove('active'));
4340

44-
if (location.pathname.includes("/activities")) {
45-
list[0].classList.remove("active");
46-
list[1].classList.remove("active");
47-
list[2].classList.add("active");
41+
// find the link whose href best matches the current path
42+
const anchors = document.querySelectorAll('.list a');
43+
let matched = null;
44+
anchors.forEach(a => {
45+
const href = a.getAttribute('href');
46+
if (!href) return;
47+
// exact match or startsWith
48+
if (location.pathname === href || location.pathname.startsWith(href)) {
49+
matched = a;
50+
}
51+
});
52+
if (matched && matched.parentElement) {
53+
matched.parentElement.classList.add('active');
54+
} else if (location.pathname === '/') {
55+
// default to first
56+
if (listItems[0]) listItems[0].classList.add('active');
4857
}
4958

5059
function handleClick() {
51-
list.forEach((item) => item.classList.remove("active"));
52-
this.classList.add("active")
60+
listItems.forEach((item) => item.classList.remove('active'));
61+
this.classList.add('active')
5362
}
54-
list.forEach((item) =>
55-
item.addEventListener("click", handleClick));
63+
listItems.forEach((item) => item.addEventListener('click', handleClick));
5664

57-
console.log('location', location)
65+
// console.log('location', location)
5866
}, [location])
5967

6068
return (

src/data/content.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { RandomQuote } from "../pages/activities/RandomQuote";
1+
// RandomQuote wrapper is used instead of original to provide favorite/save UI
2+
import { RandomQuoteWithFav } from "../pages/activities_wrappers/RandomQuoteWithFav";
23
import { MagicSquares } from "../pages/games/MagicSquares";
34
import { TicTacToe } from "../pages/games/TicTacToe";
45
import { Wordle } from "../pages/games/Wordle";
@@ -7,8 +8,9 @@ import { FortuneCard } from "../pages/activities/FotuneCard";
78
import { SearchWord } from "../pages/activities/getDefinition";
89
import { Jitter } from "../pages/games/Jitter";
910
import { RandomMeme } from "../pages/activities/RandomMemes";
10-
import { RandomJoke } from "../pages/activities/RandomJoke";
11-
import { RandomAnimeQuote } from "../pages/activities/RandomAnimeQuote";
11+
// Use wrapper components that include favorite button functionality
12+
import { RandomJokeWithFav } from "../pages/activities_wrappers/RandomJokeWithFav";
13+
import { RandomAnimeQuoteWithFav } from "../pages/activities_wrappers/RandomAnimeQuoteWithFav";
1214
import { SimonSays } from "../pages/games/SimonSays";
1315
import { ReactionTime } from "../pages/games/ReactionTime";
1416
import MemeCaptionMaker from "../pages/games/MemeCaptionMaker";
@@ -28,14 +30,14 @@ export const activities = [
2830
description: "Get random quotes",
2931
icon: "https://cdn-icons-png.flaticon.com/512/2541/2541991.png",
3032
urlTerm: "random-quotes",
31-
element: <RandomQuote />,
33+
element: <RandomQuoteWithFav />,
3234
},
3335
{
3436
title: "Random Anime Quotes",
3537
description: "Get random anime quotes",
3638
icon: "https://64.media.tumblr.com/7b526ba246f48e294ebc87fe2cbd8e1b/1a4bdb8275a18adc-c7/s250x400/94d6c7e70601111ba79b8801cd939694d0000018.jpg",
3739
urlTerm: "random-anime-quotes",
38-
element: <RandomAnimeQuote />,
40+
element: <RandomAnimeQuoteWithFav />,
3941
},
4042
{
4143
title: "Random memes",
@@ -63,7 +65,7 @@ export const activities = [
6365
description: "Get random jokes",
6466
icon: "https://www.troublefreepool.com/media/joke-png.127455/full",
6567
urlTerm: "random-jokes",
66-
element: <RandomJoke />,
68+
element: <RandomJokeWithFav />,
6769
},
6870
{
6971
title: "Calculator",

0 commit comments

Comments
 (0)