Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@ import Header from "./components/header/Header";
import AppRoutes from "./AppRoutes.tsx";

function App() {
const [isSidebarOpen] = useState(false);
const [isSidebarOpen, setIsSidebarOpen] = useState(false);

const openSidebar = () => {
setIsSidebarOpen(true);
};

const closeSidebar = () => {
setIsSidebarOpen(false);
};

return (
<>
<Sidebar isOpen={isSidebarOpen} />
<Header />
<Sidebar isOpen={isSidebarOpen} onCloseSidebar={closeSidebar} />
<Header isSidebarOpen={isSidebarOpen} onOpenSidebar={openSidebar} />
<AppRoutes />
</>
);
Expand Down
24 changes: 24 additions & 0 deletions frontend/src/components/header/Header.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,33 @@
letter-spacing: -0.3px;
}

.menu-button {
display: none;
}

@media (max-width: 767px) {
.header {
height: 70px;
margin-left: 0;
width: 100%;
}

.header[data-sidebar-open="true"] {
display: none;
}

.header h1 {
font-size: 37px;
}

.menu-button {
display: block;
position: absolute;
left: 20px;
background: none;
border: none;
font-size: 40px;
cursor: pointer;
color: var(--text-dark);
}
}
18 changes: 16 additions & 2 deletions frontend/src/components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { useTranslation } from "react-i18next";
import { useLocation } from "react-router-dom";
import { ROUTES } from "../../AppRoutes";

type HeaderProps = {
isSidebarOpen: boolean;
onOpenSidebar: () => void;
};

// Maps each route path to the translation key used for its title
const pageTitles: Record<string, string> = {
[ROUTES.plantMatch]: "sidebar.plantMatch",
Expand All @@ -11,13 +16,22 @@ const pageTitles: Record<string, string> = {
[ROUTES.profile]: "sidebar.profile",
};

export default function Header() {
export default function Header({ isSidebarOpen, onOpenSidebar }: HeaderProps) {
const { t } = useTranslation();
const { pathname } = useLocation();

const titleKey = pageTitles[pathname] ?? "sidebar.plantMatch";

return (
<header className="header">
<header className="header" data-sidebar-open={isSidebarOpen}>
<button
className="menu-button"
onClick={onOpenSidebar}
aria-label="Open sidebar"
>
☰
</button>

<h1>{t(titleKey)}</h1>
</header>
);
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/components/sidebar/Sidebar.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@
text-align: center;
}

.close-button {
display: none;
}

@media (max-width: 767px) {
.sidebar {
width: 100%;
Expand All @@ -82,4 +86,16 @@
.sidebar[data-open="true"] {
transform: translateX(0);
}

.close-button {
display: block;
position: absolute;
top: 15px;
right: 20px;
background: none;
border: none;
font-size: 32px;
color: var(--text-dark);
cursor: pointer;
}
}
35 changes: 30 additions & 5 deletions frontend/src/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,61 @@ import "./Sidebar.css";

type SidebarProps = {
isOpen: boolean;
onCloseSidebar: () => void;
};

export default function Sidebar({ isOpen }: SidebarProps) {
export default function Sidebar({ isOpen, onCloseSidebar }: SidebarProps) {
const { t } = useTranslation();

return (
<aside className="sidebar" id="sidebar" data-open={isOpen}>
<button
className="close-button"
onClick={onCloseSidebar}
aria-label="Close menu"
>
×
</button>

<p className="sidebar_title">{t("sidebar.title")}</p>
<nav className="sidebar__nav" aria-label="Sidebar navigation">
<ul>
<li>
<NavLink to={ROUTES.plantMatch} className="sidebar__link">
<NavLink
to={ROUTES.plantMatch}
className="sidebar__link"
onClick={onCloseSidebar}
>
{t("sidebar.plantMatch")}
</NavLink>
</li>

<li>
<NavLink to={ROUTES.explore} className="sidebar__link">
<NavLink
to={ROUTES.explore}
className="sidebar__link"
onClick={onCloseSidebar}
>
{t("sidebar.explorePlants")}
</NavLink>
</li>

<li>
<NavLink to={ROUTES.myMatches} className="sidebar__link">
<NavLink
to={ROUTES.myMatches}
className="sidebar__link"
onClick={onCloseSidebar}
>
{t("sidebar.myMatches")}
</NavLink>
</li>

<li>
<NavLink to={ROUTES.profile} className="sidebar__link">
<NavLink
to={ROUTES.profile}
className="sidebar__link"
onClick={onCloseSidebar}
>
{t("sidebar.profile")}
</NavLink>
</li>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/hooks/useSessionState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export function useSessionState(key: string, initialValue = "") {
try {
sessionStorage.setItem(key, value);
} catch {
// Storage can fail (private mode, full quota). The app still works,
// the value just won't be remembered.
// Storage can fail (private mode, full quota). The app still works,
// the value just won't be remembered.
}
}, [key, value]);

Expand Down