Skip to content

Commit

Permalink
#2 [ Frontend ] Header & Footer
Browse files Browse the repository at this point in the history
  • Loading branch information
fdhhhdjd committed Oct 17, 2023
1 parent 368b136 commit 72c1967
Show file tree
Hide file tree
Showing 18 changed files with 882 additions and 26 deletions.
11 changes: 0 additions & 11 deletions app/Header.tsx

This file was deleted.

64 changes: 64 additions & 0 deletions app/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Link from "next/link";

const Footer = () => {
return (
<div className="sm:mx-3 mx-1 px-2 sm:py-5 py-4 border-t border-zinc-300">
<div className="mx-auto max-w-6xl flex md:flex-row md:justify-between flex-col gap-2">
<div className="sm:p-5">
<h1 className="text-2xl text-red-800 font-black mb-3">Checks!</h1>
<p className="text-[14px]">
Built by{" "}
<Link
className="underline"
target="_blank"
href="https://twitter.com/amirfkrlh"
>
@TaiDev
</Link>{" "}
(2023). See source code{" "}
<Link
className="underline"
target="_blank"
href="https://github.com/amirfakhrullah/ecommerce-next13beta"
>
here.
</Link>
</p>
<p className="text-[14px]">
Powered by{" "}
<Link
className="underline"
href="https://vercel.com/"
target="_blank"
>
Vercel
</Link>
,{" "}
<Link
className="underline"
href="https://railway.app/"
target="_blank"
>
Railway
</Link>{" "}
&{" "}
<Link
className="underline"
href="https://aws.amazon.com/s3/"
target="_blank"
>
AWS S3
</Link>
.
</p>
</div>

<div className="flex flex-col justify-end sm:p-5">
<p className="text-[14px]">This is not a real e-commerce site.</p>
</div>
</div>
</div>
);
};

export default Footer;
60 changes: 60 additions & 0 deletions app/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"use client";

import { usePathname, useRouter } from "next/navigation";
import cn from "../helpers/cn";
import { useUserContext } from "../providers/UserProvider";
import SearchSection from "./SearchSection/SearchSection";
import TitleClick from "./TitleClick";
import AuthButton from "./buttons/AuthButton";
import CheckoutBtn from "./menus/CartMenu";

const Header = () => {
const { isAdmin } = useUserContext();
const path = usePathname();
const router = useRouter();

const isInPath = (route: string) => path === route;

const adminSection = isAdmin && !!path?.startsWith("/admin");

return (
<div className="sticky top-0 bg-gray-100 mx-1 px-2 py-3 border-b border-zinc-300 flex flex-row items-center justify-between z-[1]">
<div className="flex flex-row items-center gap-5">
<TitleClick className="text-2xl text-red-800 font-black" route="/">
Checks!
</TitleClick>
{!adminSection ? (
<SearchSection />
) : (
<div className="flex flex-row items-center gap-2">
<p
className={cn(
"text-[16px] font-bold cursor-pointer hover:underline hover:text-red-800",
isInPath("/admin") && "text-red-800"
)}
onClick={() => router.push("/admin")}
>
Orders
</p>
<p
className={cn(
"text-[16px] font-bold cursor-pointer hover:underline hover:text-red-800",
isInPath("/admin/products") && "text-red-800"
)}
onClick={() => router.push("/admin/products")}
>
Products
</p>
</div>
)}
</div>
<div className="flex flex-row items-center justify-center">
<AuthButton />
<div className="m-1" />
{!adminSection && <CheckoutBtn />}
</div>
</div>
);
};

export default Header;
7 changes: 7 additions & 0 deletions app/components/SearchSection/SearchSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from "react";

const SearchSection = () => {
return <div>SearchSection</div>;
};

export default SearchSection;
30 changes: 30 additions & 0 deletions app/components/TitleClick.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

import { useRouter } from "next/navigation";
import React from "react";
import cn from "../helpers/cn";

interface TitleClickProps extends React.HTMLAttributes<HTMLDivElement> {
route: string;
}

const TitleClick = ({
route,
children,
className = "",
...props
}: TitleClickProps) => {
const router = useRouter();

return (
<div
{...props}
className={cn("cursor-pointer", className)}
onClick={() => router.push(route)}
>
{children}
</div>
);
};

export default TitleClick;
20 changes: 20 additions & 0 deletions app/components/buttons/AuthButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use client";

import { useUserContext } from "../../providers/UserProvider";
import UserMenu from "../menus/UserMenu";
import { SignInButton } from "./SignInButton";
const AuthButton = () => {
const { user, isAdmin } = useUserContext();

if (!user) {
return <SignInButton />;
}

return (
<div className="flex flex-row items-center justify-center">
<UserMenu user={user} isAdmin={isAdmin} />
</div>
);
};

export default AuthButton;
76 changes: 76 additions & 0 deletions app/components/buttons/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"use client";

import React, { useState } from "react";
import { FiLoader } from "react-icons/fi";
import cn from "../../helpers/cn";

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
onClick?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
isLoading?: boolean;
localLoaderOnClick?: boolean; // default to true
disabled?: boolean;
children: React.ReactNode;
color?: "primary" | "secondary" | "red";
}

const Button = ({
onClick,
className = "",
isLoading = false,
localLoaderOnClick = true,
disabled = false,
children,
color,
...props
}: ButtonProps) => {
const [localLoading, setLocalLoading] = useState(false);

const loadStatus = isLoading || (localLoaderOnClick && localLoading);

const baseClassName =
"rounded-md ease-in duration-100 flex flex-row items-center justify-center text-[14px]";
const primaryClassName = cn(
baseClassName,
"bg-zinc-700 hover:bg-zinc-900 text-gray-100",
loadStatus || (disabled && "bg-zinc-900 cursor-not-allowed")
);
const secondaryClassName = cn(
baseClassName,
"border border-zinc-300 hover:bg-zinc-200 text-zinc-600",
loadStatus || (disabled && "bg-zinc-200 cursor-not-allowed")
);
const redClassName = cn(
baseClassName,
"bg-red-800 hover:bg-red-900 text-gray-100",
loadStatus || (disabled && "bg-red-900 cursor-not-allowed")
);

const handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
if (disabled || loadStatus) return;
setLocalLoading(true);
if (onClick) {
return onClick(e);
}
};

return (
<button
className={cn(
disabled && "cursor-not-allowed",
color === "primary"
? primaryClassName
: color === "secondary"
? secondaryClassName
: color === "red" && redClassName,
className
)}
disabled={disabled || loadStatus}
onClick={handleClick}
{...props}
>
{loadStatus ? <FiLoader className="m-1" /> : <>{children}</>}
</button>
);
};

export default Button;
36 changes: 36 additions & 0 deletions app/components/buttons/SignInButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client";

import { Menu, MenuHandler, MenuList } from "@material-tailwind/react";
import Button from "./Button";

export const SignInButton = () => {
const handleSignIn = async (provider: "github" | "twitch") => {
alert(provider);
};

return (
<Menu placement="bottom-end">
<MenuHandler>
<div className="py-2 px-6 rounded-md cursor-pointer bg-zinc-700 hover:bg-zinc-800 text-white text-sm">
Login
</div>
</MenuHandler>
<MenuList className="rounded-md bg-zinc-100 p-3 shadow-md mb-2 z-[2] min-w-[200px]">
<p className="text-[14px]">Select provider:</p>
<Button
className="w-full py-2 mb-1 bg-gray-800 hover:bg-gray-900 text-white rounded-md flex flex-row items-center justify-center"
onClick={() => handleSignIn("github")}
>
GitHub
</Button>

<Button
className="w-full py-2 mb-1 bg-purple-600 hover:bg-purple-700 text-white rounded-md flex flex-row items-center justify-center"
onClick={() => handleSignIn("twitch")}
>
Twitch
</Button>
</MenuList>
</Menu>
);
};
7 changes: 7 additions & 0 deletions app/components/menus/CartMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from "react";

const CheckoutBtn = () => {
return <div>CheckoutBtn</div>;
};

export default CheckoutBtn;
9 changes: 9 additions & 0 deletions app/components/menus/UserMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use client";

import React from "react";

const UserMenu = () => {
return <div>UserMenu</div>;
};

export default UserMenu;
10 changes: 10 additions & 0 deletions app/head.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function Head() {
return (
<>
<title>Checks!</title>
<meta content="width=device-width, initial-scale=1" name="viewport" />
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.png" />
</>
);
}
6 changes: 6 additions & 0 deletions app/helpers/cn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import clsx, { ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));

export default cn;
28 changes: 17 additions & 11 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import './globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import Footer from "./components/Footer";
import Header from "./components/Header";

const inter = Inter({ subsets: ['latin'] })
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });

export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
title: "Create Next App",
description: "Generated by create next app",
};

export default function RootLayout({
children,
}: {
children: React.ReactNode
children: React.ReactNode;
}) {
return (
<html lang="en">
<html lang="en" className={inter.className}>
<head />
<body className={inter.className}>{children}</body>
<body suppressHydrationWarning={true} className="bg-gray-100">
<Header />
{children}
<Footer />
</body>
</html>
)
);
}
Loading

0 comments on commit 72c1967

Please sign in to comment.