mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 18:52:06 +00:00
Merge pull request #24 from aaronpo97/navbar-update
Update navbar, implement useNavbar hook, style updates
This commit is contained in:
@@ -1,54 +1,15 @@
|
|||||||
/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
|
import useMediaQuery from '@/hooks/useMediaQuery';
|
||||||
/* eslint-disable jsx-a11y/label-has-associated-control */
|
import useNavbar from '@/hooks/useNavbar';
|
||||||
/* eslint-disable jsx-a11y/label-has-for */
|
|
||||||
|
|
||||||
import UserContext from '@/contexts/userContext';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useRouter } from 'next/router';
|
import { FC } from 'react';
|
||||||
import { useContext, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
interface Page {
|
import { GiHamburgerMenu } from 'react-icons/gi';
|
||||||
slug: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
const Navbar = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const [currentURL, setCurrentURL] = useState('/');
|
|
||||||
|
|
||||||
const { user } = useContext(UserContext);
|
const DesktopLinks: FC = () => {
|
||||||
|
const { pages, currentURL } = useNavbar();
|
||||||
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),
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="navbar sticky top-0 z-50 bg-primary text-primary-content">
|
<div className="block flex-none">
|
||||||
<div className="flex-1">
|
|
||||||
<Link className="btn-ghost btn normal-case" href="/">
|
|
||||||
<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">
|
<ul className="menu menu-horizontal p-0">
|
||||||
{pages.map((page) => {
|
{pages.map((page) => {
|
||||||
return (
|
return (
|
||||||
@@ -67,24 +28,16 @@ const Navbar = () => {
|
|||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const MobileLinks: FC = () => {
|
||||||
|
const { pages } = useNavbar();
|
||||||
|
return (
|
||||||
<div className="flex-none lg:hidden">
|
<div className="flex-none lg:hidden">
|
||||||
<div className="dropdown dropdown-end">
|
<div className="dropdown-end dropdown">
|
||||||
<label tabIndex={0} className="btn-ghost btn-circle btn">
|
<label tabIndex={0} className="btn-ghost btn-circle btn">
|
||||||
<span className="w-10 rounded-full">
|
<GiHamburgerMenu />
|
||||||
<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>
|
</label>
|
||||||
<ul
|
<ul
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@@ -100,6 +53,20 @@ const Navbar = () => {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Navbar = () => {
|
||||||
|
const isDesktopView = useMediaQuery('(min-width: 1024px)');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className="navbar sticky top-0 z-50 bg-primary text-primary-content">
|
||||||
|
<div className="flex-1">
|
||||||
|
<Link className="btn-ghost btn normal-case" href="/">
|
||||||
|
<span className="cursor-pointer text-lg font-bold">The Biergarten App</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div>{isDesktopView ? <DesktopLinks /> : <MobileLinks />}</div>
|
||||||
</nav>
|
</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 BeerByIdPage: NextPage<BeerPageProps> = ({ beerPost, beerRecommendations }) => {
|
||||||
const isMd = useMediaQuery('(min-width: 768px)');
|
const isDesktop = useMediaQuery('(min-width: 1024px)');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -64,7 +64,7 @@ const BeerByIdPage: NextPage<BeerPageProps> = ({ beerPost, beerRecommendations }
|
|||||||
<div className="w-11/12 space-y-3 xl:w-9/12">
|
<div className="w-11/12 space-y-3 xl:w-9/12">
|
||||||
<BeerInfoHeader beerPost={beerPost} />
|
<BeerInfoHeader beerPost={beerPost} />
|
||||||
|
|
||||||
{isMd ? (
|
{isDesktop ? (
|
||||||
<div className="mt-4 flex flex-row space-x-3 space-y-0">
|
<div className="mt-4 flex flex-row space-x-3 space-y-0">
|
||||||
<div className="w-[60%]">
|
<div className="w-[60%]">
|
||||||
<BeerPostCommentsSection beerPost={beerPost} />
|
<BeerPostCommentsSection beerPost={beerPost} />
|
||||||
@@ -75,12 +75,12 @@ const BeerByIdPage: NextPage<BeerPageProps> = ({ beerPost, beerRecommendations }
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Tab.Group>
|
<Tab.Group>
|
||||||
<Tab.List className="card flex flex-row bg-base-300">
|
<Tab.List className="tabs tabs-boxed items-center justify-center rounded-2xl bg-base-300">
|
||||||
<Tab className="ui-selected:bg-gray w-1/2 p-3 uppercase">
|
<Tab className="tab tab-lg w-1/2 uppercase ui-selected:tab-active">
|
||||||
Comments
|
Comments
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab className="ui-selected:bg-gray w-1/2 p-3 uppercase">
|
<Tab className="tab tab-lg w-1/2 uppercase ui-selected:tab-active">
|
||||||
Recommendations
|
Other Beers
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tab.List>
|
</Tab.List>
|
||||||
<Tab.Panels className="mt-2">
|
<Tab.Panels className="mt-2">
|
||||||
|
|||||||
@@ -5,29 +5,33 @@ import UserContext from '@/contexts/userContext';
|
|||||||
|
|
||||||
import { GetServerSideProps, NextPage } from 'next';
|
import { GetServerSideProps, NextPage } from 'next';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
import useMediaQuery from '@/hooks/useMediaQuery';
|
||||||
|
|
||||||
const ProtectedPage: NextPage = () => {
|
const ProtectedPage: NextPage = () => {
|
||||||
const { user, isLoading } = useContext(UserContext);
|
const { user, isLoading } = useContext(UserContext);
|
||||||
|
|
||||||
const currentTime = new Date().getHours();
|
const currentTime = new Date().getHours();
|
||||||
|
|
||||||
const isMorning = currentTime > 4 && currentTime < 12;
|
const isMorning = currentTime >= 3 && currentTime < 12;
|
||||||
const isAfternoon = currentTime > 12 && currentTime < 18;
|
const isAfternoon = currentTime >= 12 && currentTime < 18;
|
||||||
const isEvening = (currentTime > 18 && currentTime < 24) || currentTime < 4;
|
const isEvening = currentTime >= 18 || currentTime < 3;
|
||||||
|
|
||||||
|
const isDesktop = useMediaQuery('(min-width: 768px)');
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<div className="flex h-full flex-col items-center justify-center space-y-3">
|
<div className="flex h-full flex-col items-center justify-center space-y-3 text-center">
|
||||||
{isLoading && <Spinner size="xl" />}
|
{isLoading && <Spinner size={isDesktop ? 'xl' : 'md'} />}
|
||||||
{user && (
|
{user && !isLoading && (
|
||||||
<>
|
<>
|
||||||
<h1 className="text-7xl font-bold">
|
<h1 className="text-2xl font-bold lg:text-7xl">
|
||||||
Good {isMorning && 'morning'}
|
Good {isMorning && 'morning'}
|
||||||
{isAfternoon && 'afternoon'}
|
{isAfternoon && 'afternoon'}
|
||||||
{isEvening && 'evening'}
|
{isEvening && 'evening'}
|
||||||
{`, ${user?.firstName}!`}
|
{`, ${user?.firstName}!`}
|
||||||
</h1>
|
</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>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
.card {
|
||||||
|
@apply shadow-md
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
//themes
|
//themes
|
||||||
|
|
||||||
const myTheme = {
|
const darkTheme = {
|
||||||
default: {
|
default: {
|
||||||
primary: 'hsl(227, 23%, 20%)',
|
primary: 'hsl(227, 23%, 20%)',
|
||||||
secondary: 'hsl(255, 9%, 69%)',
|
secondary: 'hsl(255, 9%, 69%)',
|
||||||
@@ -20,7 +20,7 @@ const myTheme = {
|
|||||||
|
|
||||||
const pastelTheme = {
|
const pastelTheme = {
|
||||||
default: {
|
default: {
|
||||||
primary: 'hsl(180, 28%, 65%)',
|
primary: 'hsl(180, 15%, 60%)',
|
||||||
secondary: 'hsl(21, 54%, 83%)',
|
secondary: 'hsl(21, 54%, 83%)',
|
||||||
error: 'hsl(4, 87%, 74%)',
|
error: 'hsl(4, 87%, 74%)',
|
||||||
accent: 'hsl(93, 27%, 73%)',
|
accent: 'hsl(93, 27%, 73%)',
|
||||||
@@ -33,7 +33,6 @@ const pastelTheme = {
|
|||||||
'base-100': 'hsl(0, 0%, 85%)',
|
'base-100': 'hsl(0, 0%, 85%)',
|
||||||
'base-200': 'hsl(0, 0%, 82%)',
|
'base-200': 'hsl(0, 0%, 82%)',
|
||||||
'base-300': 'hsl(0, 0%, 78%)',
|
'base-300': 'hsl(0, 0%, 78%)',
|
||||||
'base-400': 'hsl(0, 0%, 75%)',
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,6 +54,6 @@ module.exports = {
|
|||||||
|
|
||||||
daisyui: {
|
daisyui: {
|
||||||
logs: false,
|
logs: false,
|
||||||
themes: [myTheme],
|
themes: [darkTheme, pastelTheme],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user