-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from WebmasterCamp/feature/add-cart-and-modal-p…
…ayment Feature/add cart and modal payment
- Loading branch information
Showing
8 changed files
with
403 additions
and
81 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import React, { useState } from "react"; | ||
import { LazyImage } from "./LazyImage"; | ||
import { twMerge } from "tailwind-merge"; | ||
|
||
export default function CartItem({ | ||
name, | ||
price, | ||
img, | ||
total, | ||
onDelete, | ||
}: { | ||
name: string; | ||
price: string; | ||
img: string; | ||
total: number; | ||
onDelete?: () => void; | ||
}) { | ||
const [totalItem, setTotalItem] = useState<number>(total); | ||
return ( | ||
<div | ||
className={twMerge( | ||
"flex gap-x-4 rounded-md p-3 shadow-md", | ||
(total === 0 || totalItem === 0) && "hidden" | ||
)} | ||
> | ||
<div className="h-fit overflow-hidden rounded-lg"> | ||
<LazyImage src={img} ratio="1/1" width={120} height={120} /> | ||
</div> | ||
|
||
<div> | ||
<p className="text-h5 font-bold">{name}</p> | ||
<p className="text-h6 font-bold">{price}</p> | ||
<div className="mt-6 flex w-fit items-center gap-x-3 rounded-lg bg-[#F5F5F5] p-1"> | ||
<svg | ||
width="18" | ||
height="18" | ||
viewBox="0 0 18 18" | ||
fill="none" | ||
xmlns="http://www.w3.org/2000/svg" | ||
className="cursor-pointer" | ||
onClick={() => { | ||
setTotalItem(0); | ||
onDelete?.(); | ||
}} | ||
> | ||
<path | ||
d="M15.75 4.5H12V3.2475C11.9824 2.76736 11.7752 2.31379 11.4237 1.98622C11.0722 1.65864 10.6052 1.48379 10.125 1.5H7.875C7.39482 1.48379 6.9278 1.65864 6.57632 1.98622C6.22485 2.31379 6.01759 2.76736 6 3.2475V4.5H2.25C2.05109 4.5 1.86032 4.57901 1.71967 4.71967C1.57902 4.86032 1.5 5.05108 1.5 5.25C1.5 5.44891 1.57902 5.63967 1.71967 5.78033C1.86032 5.92098 2.05109 6 2.25 6H3V14.25C3 14.8467 3.23705 15.419 3.65901 15.841C4.08097 16.2629 4.65326 16.5 5.25 16.5H12.75C13.3467 16.5 13.919 16.2629 14.341 15.841C14.7629 15.419 15 14.8467 15 14.25V6H15.75C15.9489 6 16.1397 5.92098 16.2803 5.78033C16.421 5.63967 16.5 5.44891 16.5 5.25C16.5 5.05108 16.421 4.86032 16.2803 4.71967C16.1397 4.57901 15.9489 4.5 15.75 4.5ZM7.5 3.2475C7.5 3.1275 7.6575 3 7.875 3H10.125C10.3425 3 10.5 3.1275 10.5 3.2475V4.5H7.5V3.2475ZM13.5 14.25C13.5 14.4489 13.421 14.6397 13.2803 14.7803C13.1397 14.921 12.9489 15 12.75 15H5.25C5.05109 15 4.86032 14.921 4.71967 14.7803C4.57902 14.6397 4.5 14.4489 4.5 14.25V6H13.5V14.25Z" | ||
fill="#5F5F5F" | ||
/> | ||
<path | ||
d="M6.75 12.75C6.94891 12.75 7.13968 12.671 7.28033 12.5303C7.42098 12.3897 7.5 12.1989 7.5 12V9C7.5 8.80108 7.42098 8.61032 7.28033 8.46967C7.13968 8.32901 6.94891 8.25 6.75 8.25C6.55109 8.25 6.36032 8.32901 6.21967 8.46967C6.07902 8.61032 6 8.80108 6 9V12C6 12.1989 6.07902 12.3897 6.21967 12.5303C6.36032 12.671 6.55109 12.75 6.75 12.75Z" | ||
fill="#5F5F5F" | ||
/> | ||
<path | ||
d="M11.25 12.75C11.4489 12.75 11.6397 12.671 11.7803 12.5303C11.921 12.3897 12 12.1989 12 12V9C12 8.80108 11.921 8.61032 11.7803 8.46967C11.6397 8.32901 11.4489 8.25 11.25 8.25C11.0511 8.25 10.8603 8.32901 10.7197 8.46967C10.579 8.61032 10.5 8.80108 10.5 9V12C10.5 12.1989 10.579 12.3897 10.7197 12.5303C10.8603 12.671 11.0511 12.75 11.25 12.75Z" | ||
fill="#5F5F5F" | ||
/> | ||
</svg> | ||
<p>{totalItem}</p> | ||
<svg | ||
className="cursor-pointer" | ||
onClick={() => setTotalItem(totalItem + 1)} | ||
width="24" | ||
height="24" | ||
viewBox="0 0 24 24" | ||
fill="none" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
d="M12 6V18M18 12L6 12" | ||
stroke="#5F5F5F" | ||
stroke-width="1.5" | ||
stroke-linecap="round" | ||
stroke-linejoin="round" | ||
/> | ||
</svg> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import React, { useEffect, useState } from "react"; | ||
import { LazyImage } from "./LazyImage"; | ||
import { useSearchParams, useRouter } from "next/navigation"; | ||
|
||
export default function ModalConfirm() { | ||
const [open, setOpen] = useState<boolean>(false); | ||
const searchParams = useSearchParams(); | ||
const route = useRouter(); | ||
const search = searchParams.get("status"); | ||
useEffect(() => { | ||
if (search?.includes("success")) { | ||
setOpen(true); | ||
} | ||
}, [search]); | ||
useEffect(() => { | ||
if (open) { | ||
document.body.style.overflow = "hidden"; | ||
} else { | ||
document.body.style.overflow = "visible"; | ||
} | ||
|
||
return () => { | ||
document.body.style.overflow = "visible"; | ||
}; | ||
}, [open]); | ||
return ( | ||
open && ( | ||
<div | ||
id="popup-modal" | ||
className="fixed left-0 right-0 top-0 z-[99] flex h-screen max-h-full items-center justify-center overflow-y-auto overflow-x-hidden bg-[#000]/80 p-4 md:inset-0" | ||
> | ||
<div className="relative max-h-full w-full max-w-md "> | ||
<div className="relative w-[380px] rounded-lg bg-white shadow"> | ||
<button | ||
type="button" | ||
className="absolute right-2.5 top-3 ml-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white" | ||
data-modal-hide="popup-modal" | ||
onClick={() => { | ||
setOpen(false); | ||
route.push("/"); | ||
}} | ||
> | ||
<svg | ||
width="32" | ||
height="32" | ||
viewBox="0 0 32 32" | ||
fill="none" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
d="M17.8709 15.9922L23.5968 10.2792C24.1189 9.75703 24.1189 8.9104 23.5968 8.38822C23.0746 7.86604 22.228 7.86604 21.7059 8.38822L15.9934 14.1145L10.2808 8.38822C9.75869 7.86604 8.91212 7.86604 8.38998 8.38822C7.86783 8.9104 7.86783 9.75703 8.38998 10.2792L14.1158 15.9922L8.38998 21.7051C8.13789 21.9551 7.99609 22.2955 7.99609 22.6506C7.99609 23.0057 8.13789 23.346 8.38998 23.5961C8.64 23.8482 8.98035 23.99 9.33541 23.99C9.69046 23.99 10.0308 23.8482 10.2808 23.5961L15.9934 17.8698L21.7059 23.5961C21.9559 23.8482 22.2963 23.99 22.6513 23.99C23.0064 23.99 23.3467 23.8482 23.5968 23.5961C23.8488 23.346 23.9906 23.0057 23.9906 22.6506C23.9906 22.2955 23.8488 21.9551 23.5968 21.7051L17.8709 15.9922Z" | ||
fill="#637381" | ||
/> | ||
</svg> | ||
<span className="sr-only">Close modal</span> | ||
</button> | ||
<div className="flex flex-col items-center justify-center p-6 text-center "> | ||
<LazyImage src="/assets/price.gif" width={174} height={174} /> | ||
<h3 className="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400"> | ||
Sustainability is served. Keep going champ. | ||
</h3> | ||
<button | ||
data-modal-hide="popup-modal" | ||
type="button" | ||
className="flex items-center rounded-lg bg-primary-main px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-primary-dark focus:outline-none focus:ring-4 focus:ring-primary-light" | ||
onClick={() => { | ||
setOpen(false); | ||
route.push("/"); | ||
}} | ||
> | ||
Back to home | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,63 +1,105 @@ | ||
import { signIn, signOut, useSession } from "next-auth/react" | ||
import { useRouter } from "next/router" | ||
import { useCallback, type FC, useEffect, useState } from "react" | ||
import { twMerge } from 'tailwind-merge' | ||
|
||
import { signIn, signOut, useSession } from "next-auth/react"; | ||
import { useRouter } from "next/router"; | ||
import { useCallback, type FC, useEffect, useState } from "react"; | ||
import { twMerge } from "tailwind-merge"; | ||
|
||
export interface INavbarProps { | ||
data: { name: string, href: string }[], | ||
data: { name: string; href: string }[]; | ||
} | ||
|
||
export const Navbar: FC<INavbarProps> = (props) => { | ||
const { data } = props | ||
const router = useRouter() | ||
const [path, setPath] = useState<string>('/') | ||
const user = useSession() | ||
const { data } = props; | ||
const router = useRouter(); | ||
const [path, setPath] = useState<string>("/"); | ||
const user = useSession(); | ||
|
||
const isActive = useCallback((href: string): boolean => { | ||
const res = path.includes(href) | ||
return res | ||
}, [path]) | ||
const isActive = useCallback( | ||
(href: string): boolean => { | ||
const res = path.includes(href); | ||
return res; | ||
}, | ||
[path] | ||
); | ||
|
||
useEffect(() => { | ||
setPath(router.asPath) | ||
}, [router.asPath]) | ||
useEffect(() => { | ||
setPath(router.asPath); | ||
}, [router.asPath]); | ||
|
||
return <nav className="bg-white fixed w-full z-20 top-0 left-0 border-b border-gray-200 dark:border-gray-600"> | ||
<div className="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4"> | ||
<a href="https://flowbite.com/" className="flex items-center"> | ||
<img src="https://flowbite.com/docs/images/logo.svg" className="h-8 mr-3" alt="Flowbite Logo" /> | ||
<span className="self-center text-2xl font-semibold whitespace-nowrap">Flowbite</span> | ||
</a> | ||
<div className="flex md:order-2"> | ||
<button type="button" onClick={() => { | ||
if (user.status == 'unauthenticated') { | ||
void signIn('google') | ||
return | ||
} else { | ||
void signOut() | ||
} | ||
}} className="text-white bg-green-700 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2 text-center mr-3 md:mr-0">{user.status === 'unauthenticated' ? 'Log in' : 'Log out'}</button> | ||
<button data-collapse-toggle="navbar-sticky" type="button" className="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200" aria-controls="navbar-sticky" aria-expanded="false"> | ||
<span className="sr-only">Open main menu</span> | ||
<svg className="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14"> | ||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15" /> | ||
</svg> | ||
</button> | ||
</div> | ||
<div className="items-center justify-between hidden w-full md:flex md:w-auto md:order-1" id="navbar-sticky"> | ||
<ul className="flex flex-col p-4 md:p-0 mt-4 font-medium border border-gray-100 rounded-lg bg-gray-50 md:flex-row md:space-x-8 md:mt-0 md:border-0 md:bg-white"> | ||
{ | ||
data.map(({ name, href }, i) => | ||
<li key={i}> | ||
<a href={href} className={twMerge("block py-2 pl-3 pr-4 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:hover:text-blue-700 md:p-0", isActive(href) && "text-white md:text-blue-700 bg-blue-700 rounded md:bg-transparent")}> | ||
{name} | ||
</a> | ||
</li>) | ||
} | ||
</ul> | ||
</div> | ||
return ( | ||
<nav className="fixed left-0 top-0 z-20 w-full border-b border-gray-200 bg-white dark:border-gray-600"> | ||
<div className="mx-auto flex max-w-screen-xl flex-wrap items-center justify-between p-4"> | ||
<a href="https://flowbite.com/" className="flex items-center"> | ||
<img | ||
src="https://flowbite.com/docs/images/logo.svg" | ||
className="mr-3 h-8" | ||
alt="Flowbite Logo" | ||
/> | ||
<span className="self-center whitespace-nowrap text-2xl font-semibold"> | ||
Flowbite | ||
</span> | ||
</a> | ||
<div className="flex md:order-2"> | ||
<button | ||
type="button" | ||
onClick={() => { | ||
if (user.status == "unauthenticated") { | ||
void signIn("google"); | ||
return; | ||
} else { | ||
void signOut(); | ||
} | ||
}} | ||
className="mr-3 rounded-lg bg-green-700 px-4 py-2 text-center text-sm font-medium text-white hover:bg-green-800 focus:outline-none focus:ring-4 focus:ring-blue-300 md:mr-0" | ||
> | ||
{user.status === "unauthenticated" ? "Log in" : "Log out"} | ||
</button> | ||
<button | ||
data-collapse-toggle="navbar-sticky" | ||
type="button" | ||
className="inline-flex h-10 w-10 items-center justify-center rounded-lg p-2 text-sm text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 md:hidden" | ||
aria-controls="navbar-sticky" | ||
aria-expanded="false" | ||
> | ||
<span className="sr-only">Open main menu</span> | ||
<svg | ||
className="h-5 w-5" | ||
aria-hidden="true" | ||
xmlns="http://www.w3.org/2000/svg" | ||
fill="none" | ||
viewBox="0 0 17 14" | ||
> | ||
<path | ||
stroke="currentColor" | ||
strokeLinecap="round" | ||
strokeLinejoin="round" | ||
strokeWidth="2" | ||
d="M1 1h15M1 7h15M1 13h15" | ||
/> | ||
</svg> | ||
</button> | ||
</div> | ||
<div | ||
className="hidden w-full items-center justify-between md:order-1 md:flex md:w-auto" | ||
id="navbar-sticky" | ||
> | ||
<ul className="mt-4 flex flex-col rounded-lg border border-gray-100 bg-gray-50 p-4 font-medium md:mt-0 md:flex-row md:space-x-8 md:border-0 md:bg-white md:p-0"> | ||
{data.map(({ name, href }, i) => ( | ||
<li key={i}> | ||
<a | ||
href={href} | ||
className={twMerge( | ||
"block rounded py-2 pl-3 pr-4 text-gray-900 hover:bg-gray-100 md:p-0 md:hover:bg-transparent md:hover:text-blue-700", | ||
isActive(href) && | ||
"rounded bg-blue-700 text-white md:bg-transparent md:text-blue-700" | ||
)} | ||
> | ||
{name} | ||
</a> | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
</div> | ||
</nav> | ||
} | ||
|
||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { useRouter } from "next/navigation"; | ||
import React from "react"; | ||
import { twMerge } from "tailwind-merge"; | ||
import CartItem from "./CartItem"; | ||
|
||
export const Sidebar = ({ | ||
open, | ||
handleClose, | ||
}: { | ||
open: boolean; | ||
handleClose: () => void; | ||
}) => { | ||
const route = useRouter(); | ||
return ( | ||
<> | ||
{open && ( | ||
<div className="fixed left-0 right-0 top-0 z-[50] flex h-screen max-h-full items-center justify-center overflow-y-auto overflow-x-hidden bg-[#000]/80 p-4 md:inset-0" /> | ||
)} | ||
<div | ||
id="drawer-navigation" | ||
className={twMerge( | ||
"fixed right-0 top-0 z-[60] h-screen w-[465px] overflow-y-auto bg-white transition-all duration-300", | ||
!open && "-right-[1000px]" | ||
)} | ||
aria-labelledby="drawer-navigation-label" | ||
> | ||
<div className="flex h-full flex-col overflow-y-auto py-4"> | ||
<p className="px-6 text-h6 font-bold">Your Basket</p> | ||
<div className="my-6 h-[0.5px] w-full bg-[#919EAB]/30 " /> | ||
<div className="px-6"> | ||
<CartItem | ||
name="Chicken Salad" | ||
price="50" | ||
total={1} | ||
img="https://images.unsplash.com/photo-1484659619207-9165d119dafe?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80" | ||
/> | ||
</div> | ||
<div className="mt-auto border-t-[0.5px] border-[#DEDEDE]" /> | ||
<div className="px-6"> | ||
<div className="mb-3 flex items-center justify-between pt-3"> | ||
<p className="text-subtitle1 font-bold">Total</p> | ||
<p className="font-semibold">฿50</p> | ||
</div> | ||
<div className="flex items-center gap-x-2"> | ||
<button | ||
data-modal-hide="popup-modal" | ||
type="button" | ||
onClick={handleClose} | ||
className="flex w-full items-center justify-center rounded-lg border border-primary-main px-5 py-2.5 text-center text-sm font-medium text-primary-main" | ||
> | ||
Continue Shopping | ||
</button> | ||
|
||
<button | ||
data-modal-hide="popup-modal" | ||
type="button" | ||
className="flex w-full items-center justify-center rounded-lg bg-primary-main px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-primary-dark focus:outline-none focus:ring-4 focus:ring-primary-light" | ||
onClick={() => | ||
route.push("https://buy.stripe.com/test_fZe3f2gSYcSL9moaEE") | ||
} | ||
> | ||
Purchase | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</> | ||
); | ||
}; |
Oops, something went wrong.