summaryrefslogtreecommitdiff
path: root/src2/components/layouts
diff options
context:
space:
mode:
authorIT Fixcomart <it@fixcomart.co.id>2023-03-01 09:18:52 +0000
committerIT Fixcomart <it@fixcomart.co.id>2023-03-01 09:18:52 +0000
commita7abbf4ddc70068620e9f44b74dc162ce2e16ee2 (patch)
tree74f66253717515d364ce74bd8275015c1f829cbc /src2/components/layouts
parent90e1edab9b6a8ccc09a49fed3addbec2cbc4e4c3 (diff)
parenta1b9b647a6c4bda1f5db63879639d44543f9557e (diff)
Merged in refactor (pull request #1)
Refactor
Diffstat (limited to 'src2/components/layouts')
-rw-r--r--src2/components/layouts/AppBar.js47
-rw-r--r--src2/components/layouts/Footer.js91
-rw-r--r--src2/components/layouts/Header.js253
-rw-r--r--src2/components/layouts/Layout.js20
4 files changed, 411 insertions, 0 deletions
diff --git a/src2/components/layouts/AppBar.js b/src2/components/layouts/AppBar.js
new file mode 100644
index 00000000..fe74c940
--- /dev/null
+++ b/src2/components/layouts/AppBar.js
@@ -0,0 +1,47 @@
+import { Bars3Icon, ChevronLeftIcon, HomeIcon, ShoppingCartIcon } from "@heroicons/react/24/outline";
+import Head from "next/head";
+import { useRouter } from "next/router";
+import Link from "../elements/Link";
+
+const AppBar = ({ title }) => {
+ const router = useRouter();
+
+ const handleBackButtonClick = (event) => {
+ event.currentTarget.disabled = true;
+ router.back();
+ }
+
+ return (
+ <>
+ <Head>
+ <title>{ title } - Indoteknik</title>
+ </Head>
+ <div className="sticky-header flex justify-between !p-0 !pr-4 idt-transition">
+ {/* --- Start Title */}
+ <div className="flex items-center">
+ <button type="button" onClick={handleBackButtonClick} className="text-gray_r-12 px-4 py-5">
+ <ChevronLeftIcon className="w-6 stroke-2"/>
+ </button>
+ <h1 className="text-h-md">{ title }</h1>
+ </div>
+ {/* --- End Title */}
+
+ {/* --- Start Icons */}
+ <div className="flex gap-x-4 items-center">
+ <Link href="/shop/cart">
+ <ShoppingCartIcon className="w-6 stroke-2 text-gray_r-12"/>
+ </Link>
+ <Link href="/">
+ <HomeIcon className="w-6 stroke-2 text-gray_r-12"/>
+ </Link>
+ <Link href="/my/menu">
+ <Bars3Icon className="w-6 stroke-2 text-gray_r-12"/>
+ </Link>
+ </div>
+ {/* --- End Icons */}
+ </div>
+ </>
+ );
+};
+
+export default AppBar; \ No newline at end of file
diff --git a/src2/components/layouts/Footer.js b/src2/components/layouts/Footer.js
new file mode 100644
index 00000000..d173a525
--- /dev/null
+++ b/src2/components/layouts/Footer.js
@@ -0,0 +1,91 @@
+import {
+ PhoneIcon,
+ DevicePhoneMobileIcon,
+ EnvelopeIcon
+} from "@heroicons/react/24/outline";
+import Image from "next/image";
+import InstagramIcon from "@/icons/instagram.svg";
+import LinkedinIcon from "@/icons/linkedin.svg";
+import Link from "../elements/Link";
+
+export default function Footer() {
+ return (
+ <div className="p-4 bg-gray_r-3">
+ <div className="grid grid-cols-2 gap-x-2 mb-4">
+ <div>
+ <p className="font-medium mb-2">Kantor Pusat</p>
+ <p className="text-gray_r-11 leading-6 text-caption-2">
+ Jl. Bandengan Utara 85A No. 8-9 RT.3/RW.16, Penjaringan, Kec. Penjaringan
+ </p>
+
+ <p className="font-medium mb-2 mt-6">Layanan Informasi</p>
+ <div className="flex items-center gap-x-2 text-gray_r-11 text-caption-2 mb-2">
+ <PhoneIcon className="w-5 h-5 stroke-2"/>
+ <a className="text-gray_r-11 font-normal" href="tel:02129338828">
+ (021) 2933-8828
+ </a>
+ {'/'}
+ <a className="text-gray_r-11 font-normal" href="tel:02129338829">
+ 29
+ </a>
+ </div>
+ <div className="flex items-center gap-x-2 text-gray_r-11 text-caption-2 mb-2">
+ <DevicePhoneMobileIcon className="w-5 h-5 stroke-2"/>
+ <a className="text-gray_r-11 font-normal" href="https://wa.me/628128080622">
+ 0812-8080-622
+ </a>
+ </div>
+ <div className="flex items-center gap-x-2 text-gray_r-11 text-caption-2">
+ <EnvelopeIcon className="w-5 h-5 stroke-2"/>
+ <a className="text-gray_r-11 font-normal" href="mailto:sales@indoteknik.com">
+ sales@indoteknik.com
+ </a>
+ </div>
+
+ <p className="font-medium mb-2 mt-6">Panduan Pelanggan</p>
+ <div className="text-caption-2 flex flex-col gap-y-2">
+ <Link className="text-gray_r-11 font-normal" href="/faqs">FAQ</Link>
+ <Link className="text-gray_r-11 font-normal" href="/">Kebijakan Privasi</Link>
+ <Link className="text-gray_r-11 font-normal" href="/">Pengajuan Tempo</Link>
+ <Link className="text-gray_r-11 font-normal" href="/">Garansi Produk</Link>
+ <Link className="text-gray_r-11 font-normal" href="/">Online Quotation</Link>
+ <Link className="text-gray_r-11 font-normal" href="/">Pengiriman</Link>
+ <Link className="text-gray_r-11 font-normal" href="/">Pembayaran</Link>
+ <Link className="text-gray_r-11 font-normal" href="/">Syarat & Ketentuan</Link>
+
+ </div>
+ </div>
+ <div>
+ <p className="font-medium mb-2">Jam Operasional</p>
+ <p className="text-gray_r-11 leading-6 text-caption-2">
+ <span className="font-medium">Senin - Jumat:</span> 08:30 - 17:00
+ </p>
+ <p className="text-gray_r-11 leading-6 text-caption-2">
+ <span className="font-medium">Sabtu:</span> 08:30 - 14:00
+ </p>
+
+ <p className="font-medium mb-2 mt-6">Temukan Kami</p>
+ <div className="flex gap-x-2">
+ <InstagramIcon className="w-5 h-5 stroke-gray_r-11" />
+ <LinkedinIcon className="w-5 h-5 stroke-gray_r-11" />
+ </div>
+
+ <p className="font-medium mb-2 mt-6">Pembayaran</p>
+ <div className="grid grid-cols-4 gap-2">
+ <Image src="/images/payments/bca.webp" alt="BCA Payment" width={48} height={48} className="w-full" />
+ <Image src="/images/payments/bca.webp" alt="BCA Payment" width={48} height={48} className="w-full" />
+ <Image src="/images/payments/bca.webp" alt="BCA Payment" width={48} height={48} className="w-full" />
+ <Image src="/images/payments/bca.webp" alt="BCA Payment" width={48} height={48} className="w-full" />
+ <Image src="/images/payments/bca.webp" alt="BCA Payment" width={48} height={48} className="w-full" />
+ <Image src="/images/payments/bca.webp" alt="BCA Payment" width={48} height={48} className="w-full" />
+ <Image src="/images/payments/bca.webp" alt="BCA Payment" width={48} height={48} className="w-full" />
+ <Image src="/images/payments/bca.webp" alt="BCA Payment" width={48} height={48} className="w-full" />
+ </div>
+
+ {/* <p className="font-medium mb-2 mt-6">Pengiriman</p> */}
+ </div>
+ </div>
+ <h6 className="h2">PT. Indoteknik Dotcom Gemilang</h6>
+ </div>
+ );
+} \ No newline at end of file
diff --git a/src2/components/layouts/Header.js b/src2/components/layouts/Header.js
new file mode 100644
index 00000000..23fda642
--- /dev/null
+++ b/src2/components/layouts/Header.js
@@ -0,0 +1,253 @@
+import Image from "next/image";
+import { Fragment, useCallback, useEffect, useRef, useState } from "react";
+import Head from "next/head";
+import { useRouter } from "next/router";
+import axios from "axios";
+import {
+ MagnifyingGlassIcon,
+ Bars3Icon,
+ ShoppingCartIcon,
+ ChevronRightIcon,
+ Cog6ToothIcon,
+ HeartIcon,
+ ChevronDownIcon,
+ ChevronUpIcon
+} from "@heroicons/react/24/outline";
+
+// Helpers
+import { useAuth } from "@/core/utils/auth";
+// Components
+import Link from "../elements/Link";
+// Images
+import Logo from "@/images/logo.png";
+import greeting from "@/core/utils/greeting";
+import apiOdoo from "@/core/utils/apiOdoo";
+
+const menus = [
+ { name: 'Semua Brand', href: '/shop/brands' },
+ { name: 'Blog Indoteknik', href: '/' },
+ { name: 'Tentang Indoteknik', href: '/' },
+ { name: 'Pusat Bantuan', href: '/' },
+];
+
+export default function Header({ title }) {
+ const router = useRouter();
+ const { q = '' } = router.query;
+ const [searchQuery, setSearchQuery] = useState(q != '*' ? q : '');
+ const [suggestions, setSuggestions] = useState([]);
+ const searchQueryRef = useRef();
+ const [isMenuActive, setIsMenuActive] = useState(false);
+ const [auth] = useAuth();
+
+ useEffect(() => {
+ if (q) {
+ searchQueryRef.current.blur();
+ setSuggestions([]);
+ };
+ }, [q]);
+
+ const clickSuggestion = (value) => {
+ router.push(`/shop/search?q=${value}`, undefined, { scroll: false });
+ };
+
+ const getSuggestion = useCallback(async () => {
+ if (searchQuery.trim().length > 0) {
+ let result = await axios(`${process.env.SELF_HOST}/api/shop/suggest?q=${searchQuery.trim()}`);
+ setSuggestions(result.data.suggest.mySuggester[searchQuery.trim()].suggestions);
+ } else {
+ setSuggestions([]);
+ }
+ }, [searchQuery]);
+
+ useEffect(() => {
+ if (document.activeElement == searchQueryRef.current) getSuggestion();
+ }, [getSuggestion]);
+
+ const openMenu = () => setIsMenuActive(true);
+ const closeMenu = () => setIsMenuActive(false);
+
+ const searchSubmit = (e) => {
+ e.preventDefault();
+ if (searchQuery.length > 0) {
+ router.push(`/shop/search?q=${searchQuery}`, undefined, { scroll: false });
+ } else {
+ searchQueryRef.current.focus();
+ }
+ }
+
+ const [ isOpenCategory, setOpenCategory ] = useState(false);
+ const [ categories, setCategories ] = useState([]);
+
+ useEffect(() => {
+ const loadCategories = async () => {
+ if (isOpenCategory && categories.length == 0) {
+ let dataCategories = await apiOdoo('GET', '/api/v1/category/tree');
+ dataCategories = dataCategories.map((category) => {
+ category.childs = category.childs.map((child1Category) => {
+ return {
+ ...child1Category,
+ isOpen: false
+ }
+ })
+ return {
+ ...category,
+ isOpen: false
+ }
+ });
+ setCategories(dataCategories);
+ }
+ }
+ loadCategories();
+ }, [ isOpenCategory, categories ]);
+
+ const toggleCategories = (id = 0) => {
+ let newCategories = categories.map((category) => {
+ category.childs = category.childs.map((child1Category) => {
+ return {
+ ...child1Category,
+ isOpen: id == child1Category.id ? !child1Category.isOpen : child1Category.isOpen
+ }
+ })
+ return {
+ ...category,
+ isOpen: id == category.id ? !category.isOpen : category.isOpen
+ }
+ });
+ setCategories(newCategories);
+ }
+
+ return (
+ <>
+ <Head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+ <title>{title}</title>
+ </Head>
+
+ <div className={'menu-wrapper' + (isMenuActive ? ' active ' : '')}>
+ <div className="flex gap-x-2 items-center border-b border-gray_r-6 p-4">
+ { auth && (
+ <Link href="/my/menu" className="w-full flex items-center text-gray_r-12" onClick={closeMenu}>
+ <div>
+ <p className="text-gray_r-11 text-caption-2">{ greeting() },</p>
+ <h1>{auth.name}</h1>
+ </div>
+ <div className="ml-auto">
+ <Cog6ToothIcon className="w-5" />
+ </div>
+ </Link>
+ ) }
+
+ { !auth && (
+ <>
+ <Link href="/login" onClick={closeMenu} className="w-full py-2 btn-light text-gray_r-12">Masuk</Link>
+ <Link href="/register" onClick={closeMenu} className="w-full py-2 btn-yellow text-gray_r-12">Daftar</Link>
+ </>
+ ) }
+ </div>
+ <div className="flex flex-col">
+ { menus.map((menu, index) => (
+ <Link className="flex w-full font-normal text-gray_r-11 border-b border-gray_r-6 p-4" href={menu.href} key={index} onClick={closeMenu}>
+ <span>{ menu.name }</span>
+ <div className="ml-auto">
+ <ChevronRightIcon className="text-gray_r-12 w-5" />
+ </div>
+ </Link>
+ )) }
+ <div className="flex w-full font-normal text-gray_r-11 border-b border-gray_r-6 p-4" onClick={() => setOpenCategory(!isOpenCategory)}>
+ <span>Kategori</span>
+ <div className="ml-auto">
+ { !isOpenCategory && <ChevronDownIcon className="text-gray_r-12 w-5" /> }
+ { isOpenCategory && <ChevronUpIcon className="text-gray_r-12 w-5" /> }
+ </div>
+ </div>
+ { isOpenCategory && categories.map((category) => (
+ <Fragment key={category.id}>
+ <div className="flex w-full text-gray_r-11 border-b border-gray_r-6 px-4 pl-8">
+ <Link href={`/shop/search?category=${category.name}`} className="flex-1 font-normal text-gray_r-11 py-4">
+ { category.name }
+ </Link>
+ <div className="ml-4 h-full py-4" onClick={() => toggleCategories(category.id)}>
+ { !category.isOpen && <ChevronDownIcon className="text-gray_r-12 w-5" /> }
+ { category.isOpen && <ChevronUpIcon className="text-gray_r-12 w-5" /> }
+ </div>
+ </div>
+ { category.isOpen && category.childs.map((child1Category) => (
+ <Fragment key={child1Category.id}>
+ <div className={`flex w-full text-gray_r-11 border-b border-gray_r-6 p-4 pl-12 ${category.isOpen ? 'bg-gray_r-2' : ''}`}>
+ <Link href={`/shop/search?category=${child1Category.name}`} className="flex-1 font-normal text-gray_r-11">
+ { child1Category.name }
+ </Link>
+ { child1Category.childs.length > 0 && (
+ <div className="ml-4 h-full" onClick={() => toggleCategories(child1Category.id)}>
+ { !child1Category.isOpen && <ChevronDownIcon className="text-gray_r-12 w-5" /> }
+ { child1Category.isOpen && <ChevronUpIcon className="text-gray_r-12 w-5" /> }
+ </div>
+ ) }
+ </div>
+ { child1Category.isOpen && child1Category.childs.map((child2Category) => (
+ <Link key={child2Category.id} href={`/shop/search?category=${child2Category.name}`} className="flex w-full font-normal text-gray_r-11 border-b border-gray_r-6 p-4 pl-16">
+ { child2Category.name }
+ </Link>
+ )) }
+ </Fragment>
+ )) }
+ </Fragment>
+ )) }
+ </div>
+ </div>
+ <div className={isMenuActive ? 'menu-overlay block opacity-100' : 'menu-overlay hidden opacity-0'} onClick={closeMenu}></div>
+
+ <div className="sticky-header">
+ <div className="flex justify-between items-center">
+ <Link href="/" scroll={false}>
+ <Image src={Logo} alt="Logo Indoteknik" width={120} height={40} />
+ </Link>
+ <div className="flex gap-x-4">
+ <Link href="/my/wishlist">
+ <HeartIcon className="w-6 text-gray_r-12" />
+ </Link>
+ <Link href="/shop/cart">
+ <ShoppingCartIcon className="w-6 text-gray_r-12" />
+ </Link>
+ <button onClick={openMenu}>
+ <Bars3Icon className="w-6 text-gray_r-12" />
+ </button>
+ </div>
+ </div>
+ <form onSubmit={searchSubmit} className="relative flex mt-2">
+ <input
+ ref={searchQueryRef}
+ type="text"
+ name="q"
+ onChange={(e) => setSearchQuery(e.target.value)}
+ onFocus={getSuggestion}
+ value={searchQuery}
+ className="form-input rounded-r-none border-r-0 focus:border-gray_r-7"
+ placeholder="Ketikan nama, merek, part number"
+ autoComplete="off"
+ />
+
+ <button
+ type="submit"
+ aria-label="search"
+ className="btn-light bg-transparent px-2 py-1 rounded-l-none border-l-0"
+ >
+ <MagnifyingGlassIcon className="w-6" />
+ </button>
+
+ {suggestions.length > 1 && (
+ <div className="absolute w-full top-[50px] rounded-b bg-gray_r-2 border border-gray_r-6">
+ {suggestions.map((suggestion, index) => (
+ <p onClick={() => clickSuggestion(suggestion.term)} className="w-full p-2" key={index}>{suggestion.term}</p>
+ ))}
+ </div>
+ )}
+ </form>
+ </div>
+
+ {suggestions.length > 1 && (
+ <div className="menu-overlay !z-40" onClick={() => setSuggestions([])}></div>
+ )}
+ </>
+ )
+} \ No newline at end of file
diff --git a/src2/components/layouts/Layout.js b/src2/components/layouts/Layout.js
new file mode 100644
index 00000000..fd507963
--- /dev/null
+++ b/src2/components/layouts/Layout.js
@@ -0,0 +1,20 @@
+import { motion } from 'framer-motion';
+
+export default function Layout({ children, ...pageProps }) {
+ const transition = {
+ ease: 'easeOut',
+ duration: 0.3
+ };
+
+ return children && (
+ <motion.main
+ initial={{ opacity: 0, x: 30, y: 0 }}
+ animate={{ opacity: 1, x: 0, y: 0 }}
+ exit={{ opacity: 0, x: 30, y: 0 }}
+ transition={transition}
+ {...pageProps}
+ >
+ {children}
+ </motion.main>
+ );
+} \ No newline at end of file