Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Build nav column and header #51

Merged
merged 21 commits into from
Dec 8, 2024
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
6 changes: 5 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import NavSystem from '@/components/NavSystem';
import StyledComponentsRegistry from '@/lib/registry';
import ProfileProvider from '@/utils/ProfileProvider';
import { AuthProvider } from '../utils/AuthProvider';
Expand Down Expand Up @@ -27,7 +28,10 @@ export default function RootLayout({
<body className={sans.className}>
<AuthProvider>
<ProfileProvider>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
<StyledComponentsRegistry>
<NavSystem />
{children}
</StyledComponentsRegistry>
</ProfileProvider>
</AuthProvider>
</body>
Expand Down
58 changes: 58 additions & 0 deletions components/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import Link from 'next/link';
import CONFIG from '@/lib/configs';
import { Flex } from '@/styles/containers';
import { useAuth } from '@/utils/AuthProvider';
import { useProfile } from '@/utils/ProfileProvider';
import Icon from '../Icon';
import { Container, HamburgerButton } from './styles';

interface HeaderProps {
toggleNavColumn: () => void;
}

export default function Header({ toggleNavColumn }: HeaderProps) {
const { profileReady, profileData } = useProfile();
const { userId, loading: authLoading } = useAuth();

const onNavColumnClick = () => {
toggleNavColumn();
};

const AuthOrProfileButtons = () => {
// If not (both profile and auth ready)
if (authLoading || !profileReady) return <div></div>;

// Logged-in user
if (userId) {
// Logged in AND onboarded
// TODO: this should route to /my-account in the future
if (profileData) {
return <Icon type="profile" />;
}

// Not onboarded
return <Link href={CONFIG.onboarding}>Complete Onboarding</Link>;
}

// Not logged-in user
return (
<Flex $direction="row" $gap="8px" $w="max-content">
<Link href={CONFIG.login}>Login</Link>
<Link href={CONFIG.signup}>Sign Up</Link>
</Flex>
);
};

return (
<Container>
<HamburgerButton onClick={onNavColumnClick}>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might want to eventually consolidate all button styles and include them in components/Button.tsx. in this case, we could use a button that basically has no background, outline etc.

<Icon type="hamburger" />
</HamburgerButton>
<Link href={CONFIG.home}>
<Icon type="logo" />
</Link>
<AuthOrProfileButtons />
</Container>
);
}
16 changes: 16 additions & 0 deletions components/Header/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import styled from 'styled-components';

export const Container = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 24px 12px;
z-index: 500;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
`;

export const HamburgerButton = styled.button`
background: none;
border: none;
cursor: pointer;
`;
157 changes: 157 additions & 0 deletions components/NavColumn/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import React from 'react';
import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation';
import CONFIG from '@/lib/configs';
import { IconType } from '@/lib/icons';
import COLORS from '@/styles/colors';
import { Flex } from '@/styles/containers';
import { H4, P3 } from '@/styles/text';
import { UserTypeEnum } from '@/types/schema';
import { useAuth } from '@/utils/AuthProvider';
import { formatUserType } from '@/utils/helpers';
import { useProfile } from '@/utils/ProfileProvider';
import Icon from '../Icon';
import NavColumnItem from '../NavColumnItem';
import {
HamburgerButton,
HamburgerIcon,
LoginButton,
LoginButtonsContainer,
NameAndStatus,
NavColumnContainer,
NavColumnHeader,
NavLinksContainer,
OnboardingButton,
Overlay,
Profile,
ProfileDisplayContainer,
ProfileIcon,
SignOutButton,
SignUpButton,
} from './styles';

interface NavColumnProps {
isOpen: boolean;
onClose: () => void;
}

type NavLink = {
name: string;
path: string;
iconName: IconType;
};

const navLinks: NavLink[] = [
{ name: 'View Plants', path: CONFIG.viewPlants, iconName: 'plant' },
{
name: 'Planting Timeline',
path: CONFIG.plantingTimeline,
iconName: 'calendar',
},
];

export default function NavColumn({ isOpen, onClose }: NavColumnProps) {
const currentPath = usePathname();
const { signOut, userId, loading: authLoading } = useAuth();
const router = useRouter();
const { profileData, profileReady } = useProfile();

const handleSignOut = async () => {
await signOut();
onClose();
router.push(CONFIG.login);
};

const AuthOrProfileButtons = () => {
const authAndProfileReady = profileReady && !authLoading;
if (!authAndProfileReady) {
return <div>Loading...</div>;
}

// Logged in Users
if (userId) {
// Logged in, not onboarded -> Go To Onboarding button
// Logged in, Onboarded -> Show My Account Info
return (
<ProfileDisplayContainer>
{!profileData ? (
<OnboardingButton href={CONFIG.onboarding} onClick={onClose}>
Go to Onboarding
</OnboardingButton>
) : (
<Profile>
<ProfileIcon type="profile" />
<NameAndStatus>
<H4 $color={COLORS.shrub} $fontWeight={300}>
Your Account
</H4>
<P3 $color={COLORS.shrub} $fontWeight={300}>
{formatUserType(profileData.user_type as UserTypeEnum)}
</P3>
</NameAndStatus>
</Profile>
)}
<SignOutButton onClick={handleSignOut}>Sign Out</SignOutButton>
</ProfileDisplayContainer>
);
}

// Not logged -> Go to Auth Pages
return (
<LoginButtonsContainer>
<LoginButton href={CONFIG.login} onClick={onClose}>
Log In
</LoginButton>
<SignUpButton href={CONFIG.signup} onClick={onClose}>
Sign Up
</SignUpButton>
</LoginButtonsContainer>
);
};

return (
<>
{isOpen && (
<>
<Overlay onClick={onClose} $isOpen={isOpen} />
<NavColumnContainer>
<div>
<NavColumnHeader>
<div>
{/* empty whitespace for positioning logo and hamburger */}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oooo i see what you're doing here!

hmm... perhaps it would be cleaner if we made the Logo justify-self: center and the Hamburger Icon justify-self: flex-end or smth? but honestly this way is fine, so i'm ok w keeping it too!

</div>
<Link onClick={onClose} href={CONFIG.home}>
<Icon type="logo" />
</Link>
<HamburgerButton onClick={onClose}>
<HamburgerIcon type="hamburger" />
</HamburgerButton>
</NavColumnHeader>
<NavLinksContainer>
{navLinks.map((link: NavLink, key) => (
<NavColumnItem
key={key}
routeName={link.name}
path={link.path}
isSelected={currentPath === link.path}
icon={link.iconName}
onClose={onClose}
/>
))}
</NavLinksContainer>
</div>
<Flex
$direction="column"
$pb="52px"
$px="16px"
$w="100%"
$h="max-content"
>
<AuthOrProfileButtons />
</Flex>
</NavColumnContainer>
</>
)}
</>
);
}
140 changes: 140 additions & 0 deletions components/NavColumn/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import Link from 'next/link';
import styled from 'styled-components';
import COLORS from '@/styles/colors';
import Icon from '../Icon';

export const NavColumnContainer = styled.div`
min-width: 289px;
height: 100vh;
background: ${COLORS.glimpse};
position: fixed;
z-index: 1000;
top: 0;
left: 0;
display: flex;
flex-direction: column;
transition: transform 1s ease-in-out;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fancy! do we want to make this a slightly longer transition? maybe to discuss w kyrene

justify-content: space-between;
`;

export const Overlay = styled.div<{ $isOpen: boolean }>`
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
display: ${({ $isOpen }) => ($isOpen ? 'block' : 'none')};
`;

export const HamburgerButton = styled.button`
background: none;
border: none;
cursor: pointer;
color: ${COLORS.shrub};
justify-self: flex-end;
`;

// not working
export const HamburgerIcon = styled(Icon)`
fill: ${COLORS.shrub};
`;
Comment on lines +40 to +42
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm... i think kevin was able to do this before in the Review Onboarding PR - i'll try to link it soon


export const NavColumnHeader = styled.div`
display: grid;
grid-template-columns: 1fr 1fr 1fr;
align-items: center;
padding: 24px 16px 12px;
z-index: 1001;
`;

export const NavLinksContainer = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
`;

export const LoginButtonsContainer = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
`;

export const LoginButton = styled(Link)`
display: flex;
justify-content: center;
align-items: center;
border-radius: 20px;
border: 1px solid ${COLORS.shrub};
background-color: inherit;
padding: 12px 0px 12px 0px;
color: ${COLORS.shrub};
text-decoration: none;
font-size: 0.875rem;
`;

export const SignUpButton = styled(Link)`
display: flex;
justify-content: center;
align-items: center;
border-radius: 20px;
border: none;
background-color: ${COLORS.shrub};
padding: 12px 0px 12px 0px;
color: ${COLORS.glimpse};
text-decoration: none;
font-size: 0.875rem;
`;

export const OnboardingButton = styled(Link)`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, eventually we can consolidate the button styles in component/Button.tsx so that they can be reusable! this big button style is used quite frequently in the app

display: flex;
justify-content: center;
align-items: center;
border-radius: 20px;
border: none;
background-color: ${COLORS.shrub};
color: ${COLORS.glimpse};
text-decoration: none;
font-size: 0.875rem;
height: 40px;
`;

export const ProfileDisplayContainer = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
`;

export const Profile = styled.div`
display: flex;
flex-direction: row;
gap: 16px;
align-items: center;
`;

export const NameAndStatus = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
`;

export const SignOutButton = styled.button`
display: flex;
width: 100%;
justify-content: center;
align-items: center;
border-radius: 20px;
border: 1px solid ${COLORS.errorRed};
background-color: ${COLORS.glimpse};
padding: 12px 0px;
color: ${COLORS.errorRed};
text-decoration: none;
cursor: pointer;
font-size: 0.875rem;
`;

export const ProfileIcon = styled(Icon)`
min-height: 40px;
min-width: 40px;
`;
Loading
Loading