From ee4297280c1305c7e03bedd4df63ccf136c28c6c Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 24 Jan 2023 15:54:48 +0700 Subject: Merapihkan struktur folder --- src/components/Alert.js | 19 --- src/components/AppBar.js | 59 --------- src/components/BottomPopup.js | 25 ---- src/components/ConfirmAlert.js | 25 ---- src/components/Fields.js | 21 --- src/components/Filter.js | 176 ------------------------- src/components/Footer.js | 69 ---------- src/components/Header.js | 164 ----------------------- src/components/Image.js | 13 -- src/components/Layout.js | 20 --- src/components/LineDivider.js | 7 - src/components/Link.js | 9 -- src/components/ManufactureCard.js | 18 --- src/components/Pagination.js | 58 -------- src/components/ProductCard.js | 66 ---------- src/components/ProgressBar.js | 25 ---- src/components/Spinner.js | 13 -- src/components/WithAuth.js | 20 --- src/components/auth/WithAuth.js | 20 +++ src/components/elements/Alert.js | 19 +++ src/components/elements/BottomPopup.js | 25 ++++ src/components/elements/ConfirmAlert.js | 25 ++++ src/components/elements/Fields.js | 21 +++ src/components/elements/Filter.js | 176 +++++++++++++++++++++++++ src/components/elements/Image.js | 13 ++ src/components/elements/LineDivider.js | 7 + src/components/elements/Link.js | 9 ++ src/components/elements/Pagination.js | 58 ++++++++ src/components/elements/ProgressBar.js | 25 ++++ src/components/elements/Spinner.js | 13 ++ src/components/layouts/AppBar.js | 59 +++++++++ src/components/layouts/Footer.js | 69 ++++++++++ src/components/layouts/Header.js | 164 +++++++++++++++++++++++ src/components/layouts/Layout.js | 20 +++ src/components/manufactures/ManufactureCard.js | 18 +++ src/components/product/ProductSlider.js | 44 ------- src/components/products/ProductCard.js | 66 ++++++++++ src/components/products/ProductSlider.js | 44 +++++++ 38 files changed, 851 insertions(+), 851 deletions(-) delete mode 100644 src/components/Alert.js delete mode 100644 src/components/AppBar.js delete mode 100644 src/components/BottomPopup.js delete mode 100644 src/components/ConfirmAlert.js delete mode 100644 src/components/Fields.js delete mode 100644 src/components/Filter.js delete mode 100644 src/components/Footer.js delete mode 100644 src/components/Header.js delete mode 100644 src/components/Image.js delete mode 100644 src/components/Layout.js delete mode 100644 src/components/LineDivider.js delete mode 100644 src/components/Link.js delete mode 100644 src/components/ManufactureCard.js delete mode 100644 src/components/Pagination.js delete mode 100644 src/components/ProductCard.js delete mode 100644 src/components/ProgressBar.js delete mode 100644 src/components/Spinner.js delete mode 100644 src/components/WithAuth.js create mode 100644 src/components/auth/WithAuth.js create mode 100644 src/components/elements/Alert.js create mode 100644 src/components/elements/BottomPopup.js create mode 100644 src/components/elements/ConfirmAlert.js create mode 100644 src/components/elements/Fields.js create mode 100644 src/components/elements/Filter.js create mode 100644 src/components/elements/Image.js create mode 100644 src/components/elements/LineDivider.js create mode 100644 src/components/elements/Link.js create mode 100644 src/components/elements/Pagination.js create mode 100644 src/components/elements/ProgressBar.js create mode 100644 src/components/elements/Spinner.js create mode 100644 src/components/layouts/AppBar.js create mode 100644 src/components/layouts/Footer.js create mode 100644 src/components/layouts/Header.js create mode 100644 src/components/layouts/Layout.js create mode 100644 src/components/manufactures/ManufactureCard.js delete mode 100644 src/components/product/ProductSlider.js create mode 100644 src/components/products/ProductCard.js create mode 100644 src/components/products/ProductSlider.js (limited to 'src/components') diff --git a/src/components/Alert.js b/src/components/Alert.js deleted file mode 100644 index 914d1590..00000000 --- a/src/components/Alert.js +++ /dev/null @@ -1,19 +0,0 @@ -const Alert = ({ children, className, type }) => { - let typeClass = ''; - switch (type) { - case 'info': - typeClass = ' bg-blue-100 text-blue-900 border-blue-400 ' - break; - case 'success': - typeClass = ' bg-green-100 text-green-900 border-green-400 ' - break; - case 'warning': - typeClass = ' bg-yellow-100 text-yellow-900 border-yellow-400 ' - break; - } - return ( -
{children}
- ); -} - -export default Alert; \ No newline at end of file diff --git a/src/components/AppBar.js b/src/components/AppBar.js deleted file mode 100644 index 4cac8ce5..00000000 --- a/src/components/AppBar.js +++ /dev/null @@ -1,59 +0,0 @@ -import { ChevronLeftIcon, HeartIcon, HomeIcon } from "@heroicons/react/24/outline"; -import Head from "next/head"; -import { useRouter } from "next/router"; -import { useEffect, useState } from "react"; -import Link from "./Link"; - -const AppBar = ({ title }) => { - const router = useRouter(); - - const [scrollPosition, setScrollPosition] = useState(0); - const handleScrollPosition = () => { - const position = window.pageYOffset; - setScrollPosition(position); - } - - useEffect(() => { - window.addEventListener('scroll', handleScrollPosition, { passive: true }); - - return () => { - window.addEventListener('scroll', handleScrollPosition); - }; - }, []); - - const handleBackButtonClick = (event) => { - event.currentTarget.disabled = true; - router.back(); - } - - return ( - <> - - { title } - Indoteknik - -
0 && "shadow border-b-transparent" ) }> - {/* --- Start Title */} -
- -

{ title }

-
- {/* --- End Title */} - - {/* --- Start Icons */} -
- - - - - - -
- {/* --- End Icons */} -
- - ); -}; - -export default AppBar; \ No newline at end of file diff --git a/src/components/BottomPopup.js b/src/components/BottomPopup.js deleted file mode 100644 index b7f5f2de..00000000 --- a/src/components/BottomPopup.js +++ /dev/null @@ -1,25 +0,0 @@ -import CloseIcon from "../icons/close.svg"; - -const BottomPopup = ({ - active = false, - title, - children, - closePopup = () => {} -}) => { - return ( - <> -
-
-
-

{ title }

- -
- { children } -
- - ); -}; - -export default BottomPopup; \ No newline at end of file diff --git a/src/components/ConfirmAlert.js b/src/components/ConfirmAlert.js deleted file mode 100644 index 27155011..00000000 --- a/src/components/ConfirmAlert.js +++ /dev/null @@ -1,25 +0,0 @@ -const ConfirmAlert = ({ - title, - caption, - show, - onClose, - onSubmit, -}) => { - return ( - <> - {show && ( -
- )} -
-

{title}

-

{caption}

-
- - -
-
- - ); -}; - -export default ConfirmAlert; \ No newline at end of file diff --git a/src/components/Fields.js b/src/components/Fields.js deleted file mode 100644 index 586a6a22..00000000 --- a/src/components/Fields.js +++ /dev/null @@ -1,21 +0,0 @@ -import ReactSelect from "react-select"; - -const Select = ({ - field, - ...props -}) => ( - <> - field.onChange(option.value)} - value={field.value ? props.options.find(option => option.value === field.value) : ''} - isDisabled={props.disabled} - {...props} - /> - -); - -export { - Select -}; \ No newline at end of file diff --git a/src/components/Filter.js b/src/components/Filter.js deleted file mode 100644 index f2051ba8..00000000 --- a/src/components/Filter.js +++ /dev/null @@ -1,176 +0,0 @@ -import { useRouter } from "next/router"; -import { useEffect, useState } from "react"; -import BottomPopup from "./BottomPopup"; - -const Filter = ({ - isActive, - closeFilter, - defaultRoute, - defaultPriceFrom, - defaultPriceTo, - defaultCategory, - defaultBrand, - defaultOrderBy, - searchResults, - disableFilter = [] -}) => { - const router = useRouter(); - - const [priceFrom, setPriceFrom] = useState(defaultPriceFrom); - const [priceTo, setPriceTo] = useState(defaultPriceTo); - const [orderBy, setOrderBy] = useState(defaultOrderBy); - const [selectedCategory, setSelectedCategory] = useState(defaultCategory); - const [selectedBrand, setSelectedBrand] = useState(defaultBrand); - const [categories, setCategories] = useState([]); - const [brands, setBrands] = useState([]); - - const filterRoute = () => { - let filterRoute = []; - let filterRoutePrefix = '?'; - if (selectedBrand) filterRoute.push(`brand=${selectedBrand}`); - if (selectedCategory) filterRoute.push(`category=${selectedCategory}`); - if (priceFrom) filterRoute.push(`price_from=${priceFrom}`); - if (priceTo) filterRoute.push(`price_to=${priceTo}`); - if (orderBy) filterRoute.push(`order_by=${orderBy}`); - - if (defaultRoute.includes('?')) filterRoutePrefix = '&'; - if (filterRoute.length > 0) { - filterRoute = filterRoutePrefix + filterRoute.join('&'); - } else { - filterRoute = ''; - } - - return defaultRoute + filterRoute; - } - - useEffect(() => { - const filterCategory = searchResults.facet_counts.facet_fields.category_name_str.filter((category, index) => { - if (index % 2 == 0) { - const productCountInCategory = searchResults.facet_counts.facet_fields.category_name_str[index + 1]; - if (productCountInCategory > 0) return category; - } - }); - setCategories(filterCategory); - - const filterBrand = searchResults.facet_counts.facet_fields.brand_str.filter((brand, index) => { - if (index % 2 == 0) { - const productCountInBrand = searchResults.facet_counts.facet_fields.brand_str[index + 1]; - if (productCountInBrand > 0) return brand; - } - }); - setBrands(filterBrand); - }, [searchResults]); - - const submit = (e) => { - e.preventDefault(); - closeFilter(); - router.push(filterRoute(), undefined, { scroll: false }); - } - - const reset = () => { - setSelectedBrand(''); - setSelectedCategory(''); - setPriceFrom(''); - setPriceTo(''); - setOrderBy(''); - } - - const changeOrderBy = (value) => { - if (orderBy == value) { - setOrderBy(''); - } else { - setOrderBy(value); - } - } - - const sortOptions = [ - { - name: 'Harga Terendah', - value: 'price-asc', - }, - { - name: 'Harga Tertinggi', - value: 'price-desc', - }, - { - name: 'Populer', - value: 'popular', - }, - { - name: 'Ready Stock', - value: 'stock', - }, - ]; - - return ( - <> - -
- {(selectedBrand || selectedCategory || priceFrom || priceTo || orderBy) && ( - - )} - - {!disableFilter.includes('orderBy') && ( -
- -
- {sortOptions.map((sortOption, index) => ( - - ))} -
-
- )} - - {!disableFilter.includes('category') && ( -
- - -
- )} - - {!disableFilter.includes('brand') && ( -
- - -
- )} - - {!disableFilter.includes('price') && ( -
- -
- setPriceFrom(e.target.value)}/> - - setPriceTo(e.target.value)}/> -
-
- )} - -
-
- - ) -}; - -export default Filter; \ No newline at end of file diff --git a/src/components/Footer.js b/src/components/Footer.js deleted file mode 100644 index f0ae66b3..00000000 --- a/src/components/Footer.js +++ /dev/null @@ -1,69 +0,0 @@ -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"; - -export default function Footer() { - return ( -
-
-
-

Kantor Pusat

-

- Jl. Bandengan Utara 85A No. 8-9 RT.3/RW.16, Penjaringan, Kec. Penjaringan -

- -

Layanan Informasi

-
- -

(021) 2933-8828 / 29

-
-
- -

0812-8080-622

-
-
- -

sales@indoteknik.com

-
- - {/*

Panduan Pelanggan

*/} -
-
-

Jam Operasional

-

- Senin - Jumat: 08:30 - 17:00 -

-

- Sabtu: 08:30 - 14:00 -

- -

Temukan Kami

-
- - -
- -

Pembayaran

-
- BCA Payment - BCA Payment - BCA Payment - BCA Payment - BCA Payment - BCA Payment - BCA Payment - BCA Payment -
- - {/*

Pengiriman

*/} -
-
-
PT. Indoteknik Dotcom Gemilang
-
- ); -} \ No newline at end of file diff --git a/src/components/Header.js b/src/components/Header.js deleted file mode 100644 index 71f8fd58..00000000 --- a/src/components/Header.js +++ /dev/null @@ -1,164 +0,0 @@ -import Image from "next/image"; -import { 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 -} from "@heroicons/react/24/outline"; - -// Helpers -import { useAuth } from "../helpers/auth"; -// Components -import Link from "./Link"; -// Images -import Logo from "../images/logo.png"; -import greeting from "../helpers/greeting"; - -const menus = [ - { name: 'Semua Brand', href: '/shop/brands' }, - { name: 'Blog Indoteknik', href: '/' }, - { name: 'Kategori', href: '/' }, -]; - -export default function Header({ title }) { - const router = useRouter(); - const { q = '' } = router.query; - const [searchQuery, setSearchQuery] = useState(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(); - } - } - - return ( - <> - - - {title} - - -
-
- { auth && ( - -
-

{ greeting() },

-

{auth.name}

-
-
- -
- - ) } - - { !auth && ( - <> - Masuk - Daftar - - ) } -
-
- { menus.map((menu, index) => ( - - { menu.name } -
- -
- - )) } -
-
- - -
-
- - Logo Indoteknik - -
- - - - -
-
-
- 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" - /> - - - - {suggestions.length > 1 && ( -
- {suggestions.map((suggestion, index) => ( -

clickSuggestion(suggestion.term)} className="w-full p-2" key={index}>{suggestion.term}

- ))} -
- )} -
-
- - {suggestions.length > 1 && ( -
setSuggestions([])}>
- )} - - ) -} \ No newline at end of file diff --git a/src/components/Image.js b/src/components/Image.js deleted file mode 100644 index f06272b0..00000000 --- a/src/components/Image.js +++ /dev/null @@ -1,13 +0,0 @@ -import { LazyLoadImage } from "react-lazy-load-image-component"; -import 'react-lazy-load-image-component/src/effects/blur.css'; - -export default function Image({ src, alt, className = "" }) { - return ( - - ); -} \ No newline at end of file diff --git a/src/components/Layout.js b/src/components/Layout.js deleted file mode 100644 index fd507963..00000000 --- a/src/components/Layout.js +++ /dev/null @@ -1,20 +0,0 @@ -import { motion } from 'framer-motion'; - -export default function Layout({ children, ...pageProps }) { - const transition = { - ease: 'easeOut', - duration: 0.3 - }; - - return children && ( - - {children} - - ); -} \ No newline at end of file diff --git a/src/components/LineDivider.js b/src/components/LineDivider.js deleted file mode 100644 index 4e8c7b52..00000000 --- a/src/components/LineDivider.js +++ /dev/null @@ -1,7 +0,0 @@ -const LineDivider = () => { - return ( -
- ); -}; - -export default LineDivider; \ No newline at end of file diff --git a/src/components/Link.js b/src/components/Link.js deleted file mode 100644 index d354bb1b..00000000 --- a/src/components/Link.js +++ /dev/null @@ -1,9 +0,0 @@ -import NextLink from "next/link"; - -export default function Link({ children, ...pageProps }) { - return ( - - {children} - - ) -} \ No newline at end of file diff --git a/src/components/ManufactureCard.js b/src/components/ManufactureCard.js deleted file mode 100644 index 52a964d5..00000000 --- a/src/components/ManufactureCard.js +++ /dev/null @@ -1,18 +0,0 @@ -import { createSlug } from "../helpers/slug"; -import Image from "./Image"; -import Link from "./Link"; - -export default function ManufactureCard({ data }) { - const manufacture = data; - return ( - - {manufacture.logo ? ( - {manufacture.name} - ) : manufacture.name} - - ); -} \ No newline at end of file diff --git a/src/components/Pagination.js b/src/components/Pagination.js deleted file mode 100644 index 1cb1bca0..00000000 --- a/src/components/Pagination.js +++ /dev/null @@ -1,58 +0,0 @@ -import Link from "./Link"; - -export default function Pagination({ pageCount, currentPage, url }) { - let firstPage = false; - let lastPage = false; - let dotsPrevPage = false; - let dotsNextPage = false; - let urlParameterPrefix = url.includes('?') ? '&' : '?'; - - return ( -
- {Array.from(Array(pageCount)).map((v, i) => { - let page = i + 1; - let rangePrevPage = currentPage - 2; - let rangeNextPage = currentPage + 2; - let PageComponent = {page}; - let DotsComponent =
...
; - - if (pageCount == 7) { - return PageComponent; - } - - if (currentPage == 1) rangeNextPage += 3; - if (currentPage == 2) rangeNextPage += 2; - if (currentPage == 3) rangeNextPage += 1; - if (currentPage == 4) rangePrevPage -= 1; - if (currentPage == pageCount) rangePrevPage -= 3; - if (currentPage == pageCount - 1) rangePrevPage -= 2; - if (currentPage == pageCount - 2) rangePrevPage -= 1; - if (currentPage == pageCount - 3) rangeNextPage += 1; - - if (page > rangePrevPage && page < rangeNextPage) { - return PageComponent; - } - - if (page == 1 && rangePrevPage >= 1 && !firstPage) { - firstPage = true; - return PageComponent; - } - - if (page == pageCount && rangeNextPage <= pageCount && !lastPage) { - lastPage = true; - return PageComponent; - } - - if (page > currentPage && (pageCount - currentPage) > 1 && !dotsNextPage) { - dotsNextPage = true; - return DotsComponent; - } - - if (page < currentPage && (currentPage - 1) > 1 && !dotsPrevPage) { - dotsPrevPage = true; - return DotsComponent; - } - })} -
- ) -} \ No newline at end of file diff --git a/src/components/ProductCard.js b/src/components/ProductCard.js deleted file mode 100644 index 2cb0d99b..00000000 --- a/src/components/ProductCard.js +++ /dev/null @@ -1,66 +0,0 @@ -import Link from "./Link"; -import currencyFormat from "../helpers/currencyFormat"; -import { createSlug } from "../helpers/slug"; -import { ChevronRightIcon } from "@heroicons/react/20/solid"; -import Image from "./Image"; - - -export default function ProductCard({ data }) { - let product = data; - return ( -
- - {product.name} - {product.variant_total > 1 ? ( -
{product.variant_total} Varian
- ) : ''} - -
-
- {typeof product.manufacture.name !== "undefined" ? ( - {product.manufacture.name} - ) : ( - - - )} - - {product.name} - -
-
- {product.lowest_price.discount_percentage > 0 ? ( -
-

{currencyFormat(product.lowest_price.price)}

- {product.lowest_price.discount_percentage}% -
- ) : ''} - - {product.lowest_price.price_discount > 0 ? ( -

- {currencyFormat(product.lowest_price.price_discount)} -

- ) : ( - - Tanya Harga - - )} - - {product.stock_total > 0 ? ( -
-
Ready Stock
-
{product.stock_total > 5 ? '> 5' : '< 5'}
-
- ) : ''} -
-
-
- ) -} \ No newline at end of file diff --git a/src/components/ProgressBar.js b/src/components/ProgressBar.js deleted file mode 100644 index 0adedcdf..00000000 --- a/src/components/ProgressBar.js +++ /dev/null @@ -1,25 +0,0 @@ -import { Fragment } from "react"; - -const ProgressBar = ({ current, labels }) => { - return ( -
- {labels.map((label, index) => ( - -
-
- { index + 1 } -
-

{ label }

-
- { index < (labels.length - 1) && ( -
-
-
- ) } -
- ))} -
- ) -} - -export default ProgressBar; \ No newline at end of file diff --git a/src/components/Spinner.js b/src/components/Spinner.js deleted file mode 100644 index 21006ecd..00000000 --- a/src/components/Spinner.js +++ /dev/null @@ -1,13 +0,0 @@ -const Spinner = ({ className }) => { - return ( -
- - Loading... -
- ) -} - -export default Spinner; \ No newline at end of file diff --git a/src/components/WithAuth.js b/src/components/WithAuth.js deleted file mode 100644 index 74518b3b..00000000 --- a/src/components/WithAuth.js +++ /dev/null @@ -1,20 +0,0 @@ -import { useRouter } from "next/router"; -import { useEffect, useState } from "react"; -import { getAuth } from "../helpers/auth"; - -const WithAuth = ({ children }) => { - const router = useRouter(); - const [response, setResponse] = useState(<>); - - useEffect(() => { - if (!getAuth()) { - router.replace('/login'); - } else { - setResponse(children); - } - }, [children, router]); - - return response; -} - -export default WithAuth; \ No newline at end of file diff --git a/src/components/auth/WithAuth.js b/src/components/auth/WithAuth.js new file mode 100644 index 00000000..ef975873 --- /dev/null +++ b/src/components/auth/WithAuth.js @@ -0,0 +1,20 @@ +import { getAuth } from "@/core/utils/auth"; +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; + +const WithAuth = ({ children }) => { + const router = useRouter(); + const [response, setResponse] = useState(<>); + + useEffect(() => { + if (!getAuth()) { + router.replace('/login'); + } else { + setResponse(children); + } + }, [children, router]); + + return response; +} + +export default WithAuth; \ No newline at end of file diff --git a/src/components/elements/Alert.js b/src/components/elements/Alert.js new file mode 100644 index 00000000..914d1590 --- /dev/null +++ b/src/components/elements/Alert.js @@ -0,0 +1,19 @@ +const Alert = ({ children, className, type }) => { + let typeClass = ''; + switch (type) { + case 'info': + typeClass = ' bg-blue-100 text-blue-900 border-blue-400 ' + break; + case 'success': + typeClass = ' bg-green-100 text-green-900 border-green-400 ' + break; + case 'warning': + typeClass = ' bg-yellow-100 text-yellow-900 border-yellow-400 ' + break; + } + return ( +
{children}
+ ); +} + +export default Alert; \ No newline at end of file diff --git a/src/components/elements/BottomPopup.js b/src/components/elements/BottomPopup.js new file mode 100644 index 00000000..deb1b895 --- /dev/null +++ b/src/components/elements/BottomPopup.js @@ -0,0 +1,25 @@ +import CloseIcon from "@/icons/close.svg"; + +const BottomPopup = ({ + active = false, + title, + children, + closePopup = () => {} +}) => { + return ( + <> +
+
+
+

{ title }

+ +
+ { children } +
+ + ); +}; + +export default BottomPopup; \ No newline at end of file diff --git a/src/components/elements/ConfirmAlert.js b/src/components/elements/ConfirmAlert.js new file mode 100644 index 00000000..27155011 --- /dev/null +++ b/src/components/elements/ConfirmAlert.js @@ -0,0 +1,25 @@ +const ConfirmAlert = ({ + title, + caption, + show, + onClose, + onSubmit, +}) => { + return ( + <> + {show && ( +
+ )} +
+

{title}

+

{caption}

+
+ + +
+
+ + ); +}; + +export default ConfirmAlert; \ No newline at end of file diff --git a/src/components/elements/Fields.js b/src/components/elements/Fields.js new file mode 100644 index 00000000..586a6a22 --- /dev/null +++ b/src/components/elements/Fields.js @@ -0,0 +1,21 @@ +import ReactSelect from "react-select"; + +const Select = ({ + field, + ...props +}) => ( + <> + field.onChange(option.value)} + value={field.value ? props.options.find(option => option.value === field.value) : ''} + isDisabled={props.disabled} + {...props} + /> + +); + +export { + Select +}; \ No newline at end of file diff --git a/src/components/elements/Filter.js b/src/components/elements/Filter.js new file mode 100644 index 00000000..f2051ba8 --- /dev/null +++ b/src/components/elements/Filter.js @@ -0,0 +1,176 @@ +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; +import BottomPopup from "./BottomPopup"; + +const Filter = ({ + isActive, + closeFilter, + defaultRoute, + defaultPriceFrom, + defaultPriceTo, + defaultCategory, + defaultBrand, + defaultOrderBy, + searchResults, + disableFilter = [] +}) => { + const router = useRouter(); + + const [priceFrom, setPriceFrom] = useState(defaultPriceFrom); + const [priceTo, setPriceTo] = useState(defaultPriceTo); + const [orderBy, setOrderBy] = useState(defaultOrderBy); + const [selectedCategory, setSelectedCategory] = useState(defaultCategory); + const [selectedBrand, setSelectedBrand] = useState(defaultBrand); + const [categories, setCategories] = useState([]); + const [brands, setBrands] = useState([]); + + const filterRoute = () => { + let filterRoute = []; + let filterRoutePrefix = '?'; + if (selectedBrand) filterRoute.push(`brand=${selectedBrand}`); + if (selectedCategory) filterRoute.push(`category=${selectedCategory}`); + if (priceFrom) filterRoute.push(`price_from=${priceFrom}`); + if (priceTo) filterRoute.push(`price_to=${priceTo}`); + if (orderBy) filterRoute.push(`order_by=${orderBy}`); + + if (defaultRoute.includes('?')) filterRoutePrefix = '&'; + if (filterRoute.length > 0) { + filterRoute = filterRoutePrefix + filterRoute.join('&'); + } else { + filterRoute = ''; + } + + return defaultRoute + filterRoute; + } + + useEffect(() => { + const filterCategory = searchResults.facet_counts.facet_fields.category_name_str.filter((category, index) => { + if (index % 2 == 0) { + const productCountInCategory = searchResults.facet_counts.facet_fields.category_name_str[index + 1]; + if (productCountInCategory > 0) return category; + } + }); + setCategories(filterCategory); + + const filterBrand = searchResults.facet_counts.facet_fields.brand_str.filter((brand, index) => { + if (index % 2 == 0) { + const productCountInBrand = searchResults.facet_counts.facet_fields.brand_str[index + 1]; + if (productCountInBrand > 0) return brand; + } + }); + setBrands(filterBrand); + }, [searchResults]); + + const submit = (e) => { + e.preventDefault(); + closeFilter(); + router.push(filterRoute(), undefined, { scroll: false }); + } + + const reset = () => { + setSelectedBrand(''); + setSelectedCategory(''); + setPriceFrom(''); + setPriceTo(''); + setOrderBy(''); + } + + const changeOrderBy = (value) => { + if (orderBy == value) { + setOrderBy(''); + } else { + setOrderBy(value); + } + } + + const sortOptions = [ + { + name: 'Harga Terendah', + value: 'price-asc', + }, + { + name: 'Harga Tertinggi', + value: 'price-desc', + }, + { + name: 'Populer', + value: 'popular', + }, + { + name: 'Ready Stock', + value: 'stock', + }, + ]; + + return ( + <> + +
+ {(selectedBrand || selectedCategory || priceFrom || priceTo || orderBy) && ( + + )} + + {!disableFilter.includes('orderBy') && ( +
+ +
+ {sortOptions.map((sortOption, index) => ( + + ))} +
+
+ )} + + {!disableFilter.includes('category') && ( +
+ + +
+ )} + + {!disableFilter.includes('brand') && ( +
+ + +
+ )} + + {!disableFilter.includes('price') && ( +
+ +
+ setPriceFrom(e.target.value)}/> + + setPriceTo(e.target.value)}/> +
+
+ )} + +
+
+ + ) +}; + +export default Filter; \ No newline at end of file diff --git a/src/components/elements/Image.js b/src/components/elements/Image.js new file mode 100644 index 00000000..f06272b0 --- /dev/null +++ b/src/components/elements/Image.js @@ -0,0 +1,13 @@ +import { LazyLoadImage } from "react-lazy-load-image-component"; +import 'react-lazy-load-image-component/src/effects/blur.css'; + +export default function Image({ src, alt, className = "" }) { + return ( + + ); +} \ No newline at end of file diff --git a/src/components/elements/LineDivider.js b/src/components/elements/LineDivider.js new file mode 100644 index 00000000..4e8c7b52 --- /dev/null +++ b/src/components/elements/LineDivider.js @@ -0,0 +1,7 @@ +const LineDivider = () => { + return ( +
+ ); +}; + +export default LineDivider; \ No newline at end of file diff --git a/src/components/elements/Link.js b/src/components/elements/Link.js new file mode 100644 index 00000000..d354bb1b --- /dev/null +++ b/src/components/elements/Link.js @@ -0,0 +1,9 @@ +import NextLink from "next/link"; + +export default function Link({ children, ...pageProps }) { + return ( + + {children} + + ) +} \ No newline at end of file diff --git a/src/components/elements/Pagination.js b/src/components/elements/Pagination.js new file mode 100644 index 00000000..1cb1bca0 --- /dev/null +++ b/src/components/elements/Pagination.js @@ -0,0 +1,58 @@ +import Link from "./Link"; + +export default function Pagination({ pageCount, currentPage, url }) { + let firstPage = false; + let lastPage = false; + let dotsPrevPage = false; + let dotsNextPage = false; + let urlParameterPrefix = url.includes('?') ? '&' : '?'; + + return ( +
+ {Array.from(Array(pageCount)).map((v, i) => { + let page = i + 1; + let rangePrevPage = currentPage - 2; + let rangeNextPage = currentPage + 2; + let PageComponent = {page}; + let DotsComponent =
...
; + + if (pageCount == 7) { + return PageComponent; + } + + if (currentPage == 1) rangeNextPage += 3; + if (currentPage == 2) rangeNextPage += 2; + if (currentPage == 3) rangeNextPage += 1; + if (currentPage == 4) rangePrevPage -= 1; + if (currentPage == pageCount) rangePrevPage -= 3; + if (currentPage == pageCount - 1) rangePrevPage -= 2; + if (currentPage == pageCount - 2) rangePrevPage -= 1; + if (currentPage == pageCount - 3) rangeNextPage += 1; + + if (page > rangePrevPage && page < rangeNextPage) { + return PageComponent; + } + + if (page == 1 && rangePrevPage >= 1 && !firstPage) { + firstPage = true; + return PageComponent; + } + + if (page == pageCount && rangeNextPage <= pageCount && !lastPage) { + lastPage = true; + return PageComponent; + } + + if (page > currentPage && (pageCount - currentPage) > 1 && !dotsNextPage) { + dotsNextPage = true; + return DotsComponent; + } + + if (page < currentPage && (currentPage - 1) > 1 && !dotsPrevPage) { + dotsPrevPage = true; + return DotsComponent; + } + })} +
+ ) +} \ No newline at end of file diff --git a/src/components/elements/ProgressBar.js b/src/components/elements/ProgressBar.js new file mode 100644 index 00000000..0adedcdf --- /dev/null +++ b/src/components/elements/ProgressBar.js @@ -0,0 +1,25 @@ +import { Fragment } from "react"; + +const ProgressBar = ({ current, labels }) => { + return ( +
+ {labels.map((label, index) => ( + +
+
+ { index + 1 } +
+

{ label }

+
+ { index < (labels.length - 1) && ( +
+
+
+ ) } +
+ ))} +
+ ) +} + +export default ProgressBar; \ No newline at end of file diff --git a/src/components/elements/Spinner.js b/src/components/elements/Spinner.js new file mode 100644 index 00000000..21006ecd --- /dev/null +++ b/src/components/elements/Spinner.js @@ -0,0 +1,13 @@ +const Spinner = ({ className }) => { + return ( +
+ + Loading... +
+ ) +} + +export default Spinner; \ No newline at end of file diff --git a/src/components/layouts/AppBar.js b/src/components/layouts/AppBar.js new file mode 100644 index 00000000..f9dddf9d --- /dev/null +++ b/src/components/layouts/AppBar.js @@ -0,0 +1,59 @@ +import { ChevronLeftIcon, HeartIcon, HomeIcon } from "@heroicons/react/24/outline"; +import Head from "next/head"; +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; +import Link from "../elements/Link"; + +const AppBar = ({ title }) => { + const router = useRouter(); + + const [scrollPosition, setScrollPosition] = useState(0); + const handleScrollPosition = () => { + const position = window.pageYOffset; + setScrollPosition(position); + } + + useEffect(() => { + window.addEventListener('scroll', handleScrollPosition, { passive: true }); + + return () => { + window.addEventListener('scroll', handleScrollPosition); + }; + }, []); + + const handleBackButtonClick = (event) => { + event.currentTarget.disabled = true; + router.back(); + } + + return ( + <> + + { title } - Indoteknik + +
0 && "shadow border-b-transparent" ) }> + {/* --- Start Title */} +
+ +

{ title }

+
+ {/* --- End Title */} + + {/* --- Start Icons */} +
+ + + + + + +
+ {/* --- End Icons */} +
+ + ); +}; + +export default AppBar; \ No newline at end of file diff --git a/src/components/layouts/Footer.js b/src/components/layouts/Footer.js new file mode 100644 index 00000000..e48daf7f --- /dev/null +++ b/src/components/layouts/Footer.js @@ -0,0 +1,69 @@ +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"; + +export default function Footer() { + return ( +
+
+
+

Kantor Pusat

+

+ Jl. Bandengan Utara 85A No. 8-9 RT.3/RW.16, Penjaringan, Kec. Penjaringan +

+ +

Layanan Informasi

+
+ +

(021) 2933-8828 / 29

+
+
+ +

0812-8080-622

+
+
+ +

sales@indoteknik.com

+
+ + {/*

Panduan Pelanggan

*/} +
+
+

Jam Operasional

+

+ Senin - Jumat: 08:30 - 17:00 +

+

+ Sabtu: 08:30 - 14:00 +

+ +

Temukan Kami

+
+ + +
+ +

Pembayaran

+
+ BCA Payment + BCA Payment + BCA Payment + BCA Payment + BCA Payment + BCA Payment + BCA Payment + BCA Payment +
+ + {/*

Pengiriman

*/} +
+
+
PT. Indoteknik Dotcom Gemilang
+
+ ); +} \ No newline at end of file diff --git a/src/components/layouts/Header.js b/src/components/layouts/Header.js new file mode 100644 index 00000000..5cd0a1bc --- /dev/null +++ b/src/components/layouts/Header.js @@ -0,0 +1,164 @@ +import Image from "next/image"; +import { 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 +} 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"; + +const menus = [ + { name: 'Semua Brand', href: '/shop/brands' }, + { name: 'Blog Indoteknik', href: '/' }, + { name: 'Kategori', href: '/' }, +]; + +export default function Header({ title }) { + const router = useRouter(); + const { q = '' } = router.query; + const [searchQuery, setSearchQuery] = useState(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(); + } + } + + return ( + <> + + + {title} + + +
+
+ { auth && ( + +
+

{ greeting() },

+

{auth.name}

+
+
+ +
+ + ) } + + { !auth && ( + <> + Masuk + Daftar + + ) } +
+
+ { menus.map((menu, index) => ( + + { menu.name } +
+ +
+ + )) } +
+
+ + +
+
+ + Logo Indoteknik + +
+ + + + +
+
+
+ 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" + /> + + + + {suggestions.length > 1 && ( +
+ {suggestions.map((suggestion, index) => ( +

clickSuggestion(suggestion.term)} className="w-full p-2" key={index}>{suggestion.term}

+ ))} +
+ )} +
+
+ + {suggestions.length > 1 && ( +
setSuggestions([])}>
+ )} + + ) +} \ No newline at end of file diff --git a/src/components/layouts/Layout.js b/src/components/layouts/Layout.js new file mode 100644 index 00000000..fd507963 --- /dev/null +++ b/src/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 && ( + + {children} + + ); +} \ No newline at end of file diff --git a/src/components/manufactures/ManufactureCard.js b/src/components/manufactures/ManufactureCard.js new file mode 100644 index 00000000..73a96902 --- /dev/null +++ b/src/components/manufactures/ManufactureCard.js @@ -0,0 +1,18 @@ +import { createSlug } from "@/core/utils/slug"; +import Image from "../elements/Image"; +import Link from "../elements/Link"; + +export default function ManufactureCard({ data }) { + const manufacture = data; + return ( + + {manufacture.logo ? ( + {manufacture.name} + ) : manufacture.name} + + ); +} \ No newline at end of file diff --git a/src/components/product/ProductSlider.js b/src/components/product/ProductSlider.js deleted file mode 100644 index 84dc4258..00000000 --- a/src/components/product/ProductSlider.js +++ /dev/null @@ -1,44 +0,0 @@ -import { Swiper, SwiperSlide } from "swiper/react"; -import ProductCard from "../ProductCard"; -import ImagePlaceholderIcon from "../../icons/image-placeholder.svg"; -import "swiper/css"; - -export default function ProductSlider({ products }) { - return ( - <> - - {products?.products?.map((product, index) => ( - - - - ))} - - {products == null ? ( -
-
-
- -
-
-
-
-
-
- Loading... -
-
-
- -
-
-
-
-
-
- Loading... -
-
- ) : ''} - - ) -} \ No newline at end of file diff --git a/src/components/products/ProductCard.js b/src/components/products/ProductCard.js new file mode 100644 index 00000000..e32463a7 --- /dev/null +++ b/src/components/products/ProductCard.js @@ -0,0 +1,66 @@ +import Link from "../elements/Link"; +import currencyFormat from "@/core/utils/currencyFormat"; +import { createSlug } from "@/core/utils/slug"; +import { ChevronRightIcon } from "@heroicons/react/20/solid"; +import Image from "../elements/Image"; + + +export default function ProductCard({ data }) { + let product = data; + return ( +
+ + {product.name} + {product.variant_total > 1 ? ( +
{product.variant_total} Varian
+ ) : ''} + +
+
+ {typeof product.manufacture.name !== "undefined" ? ( + {product.manufacture.name} + ) : ( + - + )} + + {product.name} + +
+
+ {product.lowest_price.discount_percentage > 0 ? ( +
+

{currencyFormat(product.lowest_price.price)}

+ {product.lowest_price.discount_percentage}% +
+ ) : ''} + + {product.lowest_price.price_discount > 0 ? ( +

+ {currencyFormat(product.lowest_price.price_discount)} +

+ ) : ( + + Tanya Harga + + )} + + {product.stock_total > 0 ? ( +
+
Ready Stock
+
{product.stock_total > 5 ? '> 5' : '< 5'}
+
+ ) : ''} +
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/products/ProductSlider.js b/src/components/products/ProductSlider.js new file mode 100644 index 00000000..ddc505c7 --- /dev/null +++ b/src/components/products/ProductSlider.js @@ -0,0 +1,44 @@ +import { Swiper, SwiperSlide } from "swiper/react"; +import ProductCard from "./ProductCard"; +import ImagePlaceholderIcon from "../../icons/image-placeholder.svg"; +import "swiper/css"; + +export default function ProductSlider({ products }) { + return ( + <> + + {products?.products?.map((product, index) => ( + + + + ))} + + {products == null ? ( +
+
+
+ +
+
+
+
+
+
+ Loading... +
+
+
+ +
+
+
+
+
+
+ Loading... +
+
+ ) : ''} + + ) +} \ No newline at end of file -- cgit v1.2.3