mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
Merge pull request #24 from aaronpo97/navbar-update
Update navbar, implement useNavbar hook, style updates
This commit is contained in:
@@ -1,45 +1,63 @@
|
||||
/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
|
||||
/* eslint-disable jsx-a11y/label-has-associated-control */
|
||||
/* eslint-disable jsx-a11y/label-has-for */
|
||||
|
||||
import UserContext from '@/contexts/userContext';
|
||||
import useMediaQuery from '@/hooks/useMediaQuery';
|
||||
import useNavbar from '@/hooks/useNavbar';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { FC } from 'react';
|
||||
|
||||
import { GiHamburgerMenu } from 'react-icons/gi';
|
||||
|
||||
const DesktopLinks: FC = () => {
|
||||
const { pages, currentURL } = useNavbar();
|
||||
|
||||
return (
|
||||
<div className="block flex-none">
|
||||
<ul className="menu menu-horizontal p-0">
|
||||
{pages.map((page) => {
|
||||
return (
|
||||
<li key={page.slug}>
|
||||
<Link tabIndex={0} href={page.slug}>
|
||||
<span
|
||||
className={`text-lg uppercase ${
|
||||
currentURL === page.slug ? 'font-extrabold' : 'font-semibold'
|
||||
} text-primary-content`}
|
||||
>
|
||||
{page.name}
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const MobileLinks: FC = () => {
|
||||
const { pages } = useNavbar();
|
||||
return (
|
||||
<div className="flex-none lg:hidden">
|
||||
<div className="dropdown-end dropdown">
|
||||
<label tabIndex={0} className="btn-ghost btn-circle btn">
|
||||
<GiHamburgerMenu />
|
||||
</label>
|
||||
<ul
|
||||
tabIndex={0}
|
||||
className="dropdown-content menu rounded-box menu-compact mt-3 w-48 bg-base-100 p-2 shadow"
|
||||
>
|
||||
{pages.map((page) => (
|
||||
<li key={page.slug}>
|
||||
<Link href={page.slug}>
|
||||
<span className="select-none text-primary-content">{page.name}</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface Page {
|
||||
slug: string;
|
||||
name: string;
|
||||
}
|
||||
const Navbar = () => {
|
||||
const router = useRouter();
|
||||
const [currentURL, setCurrentURL] = useState('/');
|
||||
|
||||
const { user } = useContext(UserContext);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentURL(router.asPath);
|
||||
}, [router.asPath]);
|
||||
|
||||
const authenticatedPages: readonly Page[] = [
|
||||
{ slug: '/account', name: 'Account' },
|
||||
{ slug: '/api/users/logout', name: 'Logout' },
|
||||
];
|
||||
|
||||
const unauthenticatedPages: readonly Page[] = [
|
||||
{ slug: '/login', name: 'Login' },
|
||||
{ slug: '/register', name: 'Register' },
|
||||
];
|
||||
|
||||
const otherPages: readonly Page[] = [
|
||||
{ slug: '/beers', name: 'Beers' },
|
||||
{ slug: '/breweries', name: 'Breweries' },
|
||||
];
|
||||
|
||||
const pages: readonly Page[] = [
|
||||
...otherPages,
|
||||
...(user ? authenticatedPages : unauthenticatedPages),
|
||||
];
|
||||
const isDesktopView = useMediaQuery('(min-width: 1024px)');
|
||||
|
||||
return (
|
||||
<nav className="navbar sticky top-0 z-50 bg-primary text-primary-content">
|
||||
@@ -48,58 +66,7 @@ const Navbar = () => {
|
||||
<span className="cursor-pointer text-lg font-bold">The Biergarten App</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="hidden flex-none lg:block">
|
||||
<ul className="menu menu-horizontal p-0">
|
||||
{pages.map((page) => {
|
||||
return (
|
||||
<li key={page.slug}>
|
||||
<Link tabIndex={0} href={page.slug}>
|
||||
<span
|
||||
className={`text-lg uppercase ${
|
||||
currentURL === page.slug ? 'font-extrabold' : 'font-semibold'
|
||||
} text-primary-content`}
|
||||
>
|
||||
{page.name}
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex-none lg:hidden">
|
||||
<div className="dropdown dropdown-end">
|
||||
<label tabIndex={0} className="btn-ghost btn-circle btn">
|
||||
<span className="w-10 rounded-full">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="inline-block h-5 w-5 stroke-white"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</label>
|
||||
<ul
|
||||
tabIndex={0}
|
||||
className="dropdown-content menu rounded-box menu-compact mt-3 w-48 bg-base-100 p-2 shadow"
|
||||
>
|
||||
{pages.map((page) => (
|
||||
<li key={page.slug}>
|
||||
<Link href={page.slug}>
|
||||
<span className="select-none text-primary-content">{page.name}</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div>{isDesktopView ? <DesktopLinks /> : <MobileLinks />}</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
64
src/hooks/useNavbar.ts
Normal file
64
src/hooks/useNavbar.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import UserContext from '@/contexts/userContext';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState, useEffect, useContext } from 'react';
|
||||
|
||||
interface Page {
|
||||
slug: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom hook that returns the current URL and the pages to display in the navbar. It
|
||||
* uses the user context to determine whether the user is authenticated or not.
|
||||
*
|
||||
* @returns An object containing the current URL and the pages to display in the navbar.
|
||||
*/
|
||||
const useNavbar = () => {
|
||||
const router = useRouter();
|
||||
const [currentURL, setCurrentURL] = useState('/');
|
||||
|
||||
const { user } = useContext(UserContext);
|
||||
|
||||
const authenticatedPages: readonly Page[] = [
|
||||
{ slug: '/account', name: 'Account' },
|
||||
{ slug: '/api/users/logout', name: 'Logout' },
|
||||
];
|
||||
|
||||
const unauthenticatedPages: readonly Page[] = [
|
||||
{ slug: '/login', name: 'Login' },
|
||||
{ slug: '/register', name: 'Register' },
|
||||
];
|
||||
|
||||
/** These pages are accessible to both authenticated and unauthenticated users. */
|
||||
const otherPages: readonly Page[] = [
|
||||
{ slug: '/beers', name: 'Beers' },
|
||||
{ slug: '/breweries', name: 'Breweries' },
|
||||
];
|
||||
|
||||
/**
|
||||
* The pages to display in the navbar. If the user is authenticated, the authenticated
|
||||
* pages are displayed. Otherwise, the unauthenticated pages are displayed. The other
|
||||
* pages are always displayed.
|
||||
*/
|
||||
const pages: readonly Page[] = [
|
||||
...otherPages,
|
||||
...(user ? authenticatedPages : unauthenticatedPages),
|
||||
];
|
||||
|
||||
/**
|
||||
* Sets the current URL to the current URL when the router's asPath changes. This
|
||||
* ensures that the current URL is always up to date. When the component unmounts, the
|
||||
* current URL is set to '/'.
|
||||
*/
|
||||
useEffect(() => {
|
||||
setCurrentURL(router.asPath);
|
||||
|
||||
return () => {
|
||||
setCurrentURL('/');
|
||||
};
|
||||
}, [router.asPath]);
|
||||
|
||||
return { currentURL, pages };
|
||||
};
|
||||
|
||||
export default useNavbar;
|
||||
@@ -29,7 +29,7 @@ interface BeerPageProps {
|
||||
}
|
||||
|
||||
const BeerByIdPage: NextPage<BeerPageProps> = ({ beerPost, beerRecommendations }) => {
|
||||
const isMd = useMediaQuery('(min-width: 768px)');
|
||||
const isDesktop = useMediaQuery('(min-width: 1024px)');
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -64,7 +64,7 @@ const BeerByIdPage: NextPage<BeerPageProps> = ({ beerPost, beerRecommendations }
|
||||
<div className="w-11/12 space-y-3 xl:w-9/12">
|
||||
<BeerInfoHeader beerPost={beerPost} />
|
||||
|
||||
{isMd ? (
|
||||
{isDesktop ? (
|
||||
<div className="mt-4 flex flex-row space-x-3 space-y-0">
|
||||
<div className="w-[60%]">
|
||||
<BeerPostCommentsSection beerPost={beerPost} />
|
||||
@@ -75,12 +75,12 @@ const BeerByIdPage: NextPage<BeerPageProps> = ({ beerPost, beerRecommendations }
|
||||
</div>
|
||||
) : (
|
||||
<Tab.Group>
|
||||
<Tab.List className="card flex flex-row bg-base-300">
|
||||
<Tab className="ui-selected:bg-gray w-1/2 p-3 uppercase">
|
||||
<Tab.List className="tabs tabs-boxed items-center justify-center rounded-2xl bg-base-300">
|
||||
<Tab className="tab tab-lg w-1/2 uppercase ui-selected:tab-active">
|
||||
Comments
|
||||
</Tab>
|
||||
<Tab className="ui-selected:bg-gray w-1/2 p-3 uppercase">
|
||||
Recommendations
|
||||
<Tab className="tab tab-lg w-1/2 uppercase ui-selected:tab-active">
|
||||
Other Beers
|
||||
</Tab>
|
||||
</Tab.List>
|
||||
<Tab.Panels className="mt-2">
|
||||
|
||||
@@ -5,29 +5,33 @@ import UserContext from '@/contexts/userContext';
|
||||
|
||||
import { GetServerSideProps, NextPage } from 'next';
|
||||
import { useContext } from 'react';
|
||||
import useMediaQuery from '@/hooks/useMediaQuery';
|
||||
|
||||
const ProtectedPage: NextPage = () => {
|
||||
const { user, isLoading } = useContext(UserContext);
|
||||
|
||||
const currentTime = new Date().getHours();
|
||||
|
||||
const isMorning = currentTime > 4 && currentTime < 12;
|
||||
const isAfternoon = currentTime > 12 && currentTime < 18;
|
||||
const isEvening = (currentTime > 18 && currentTime < 24) || currentTime < 4;
|
||||
const isMorning = currentTime >= 3 && currentTime < 12;
|
||||
const isAfternoon = currentTime >= 12 && currentTime < 18;
|
||||
const isEvening = currentTime >= 18 || currentTime < 3;
|
||||
|
||||
const isDesktop = useMediaQuery('(min-width: 768px)');
|
||||
return (
|
||||
<Layout>
|
||||
<div className="flex h-full flex-col items-center justify-center space-y-3">
|
||||
{isLoading && <Spinner size="xl" />}
|
||||
{user && (
|
||||
<div className="flex h-full flex-col items-center justify-center space-y-3 text-center">
|
||||
{isLoading && <Spinner size={isDesktop ? 'xl' : 'md'} />}
|
||||
{user && !isLoading && (
|
||||
<>
|
||||
<h1 className="text-7xl font-bold">
|
||||
<h1 className="text-2xl font-bold lg:text-7xl">
|
||||
Good {isMorning && 'morning'}
|
||||
{isAfternoon && 'afternoon'}
|
||||
{isEvening && 'evening'}
|
||||
{`, ${user?.firstName}!`}
|
||||
</h1>
|
||||
<h2 className="text-4xl font-bold">Welcome to the Biergarten App!</h2>
|
||||
<h2 className="text-xl font-bold lg:text-4xl">
|
||||
Welcome to the Biergarten App!
|
||||
</h2>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.card {
|
||||
@apply shadow-md
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
//themes
|
||||
|
||||
const myTheme = {
|
||||
const darkTheme = {
|
||||
default: {
|
||||
primary: 'hsl(227, 23%, 20%)',
|
||||
secondary: 'hsl(255, 9%, 69%)',
|
||||
@@ -20,7 +20,7 @@ const myTheme = {
|
||||
|
||||
const pastelTheme = {
|
||||
default: {
|
||||
primary: 'hsl(180, 28%, 65%)',
|
||||
primary: 'hsl(180, 15%, 60%)',
|
||||
secondary: 'hsl(21, 54%, 83%)',
|
||||
error: 'hsl(4, 87%, 74%)',
|
||||
accent: 'hsl(93, 27%, 73%)',
|
||||
@@ -33,7 +33,6 @@ const pastelTheme = {
|
||||
'base-100': 'hsl(0, 0%, 85%)',
|
||||
'base-200': 'hsl(0, 0%, 82%)',
|
||||
'base-300': 'hsl(0, 0%, 78%)',
|
||||
'base-400': 'hsl(0, 0%, 75%)',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -55,6 +54,6 @@ module.exports = {
|
||||
|
||||
daisyui: {
|
||||
logs: false,
|
||||
themes: [myTheme],
|
||||
themes: [darkTheme, pastelTheme],
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user