From 3496025d97140268dc2e899adca994b5b9f343c0 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 31 Jan 2023 14:32:38 +0700 Subject: quotation and categories --- src/components/layouts/AppBar.js | 2 +- src/components/layouts/Header.js | 89 ++++++++++++- .../transactions/TransactionStatusBadge.js | 41 ++++++ src/pages/my/transaction/[id].js | 5 +- src/pages/my/transactions.js | 6 +- src/pages/my/wishlist.js | 20 ++- src/pages/shop/cart.js | 1 + src/pages/shop/checkout.js | 1 - src/pages/shop/product/[slug].js | 2 +- src/pages/shop/quotation/finish.js | 39 ++++++ src/pages/shop/quotation/index.js | 142 +++++++++++++++++++++ src/pages/shop/search.js | 4 +- src/styles/globals.css | 12 +- 13 files changed, 349 insertions(+), 15 deletions(-) create mode 100644 src/components/transactions/TransactionStatusBadge.js create mode 100644 src/pages/shop/quotation/finish.js create mode 100644 src/pages/shop/quotation/index.js (limited to 'src') diff --git a/src/components/layouts/AppBar.js b/src/components/layouts/AppBar.js index b4c7703c..c3d38707 100644 --- a/src/components/layouts/AppBar.js +++ b/src/components/layouts/AppBar.js @@ -43,7 +43,7 @@ const AppBar = ({ title }) => { {/* --- Start Icons */}
- + diff --git a/src/components/layouts/Header.js b/src/components/layouts/Header.js index 4741905f..bc740810 100644 --- a/src/components/layouts/Header.js +++ b/src/components/layouts/Header.js @@ -1,5 +1,5 @@ import Image from "next/image"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { Fragment, useCallback, useEffect, useRef, useState } from "react"; import Head from "next/head"; import { useRouter } from "next/router"; import axios from "axios"; @@ -9,7 +9,9 @@ import { ShoppingCartIcon, ChevronRightIcon, Cog6ToothIcon, - HeartIcon + HeartIcon, + ChevronDownIcon, + ChevronUpIcon } from "@heroicons/react/24/outline"; // Helpers @@ -19,11 +21,11 @@ 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: 'Kategori', href: '/' }, ]; export default function Header({ title }) { @@ -71,6 +73,47 @@ export default function Header({ title }) { } } + 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 ( <> @@ -108,6 +151,46 @@ export default function Header({ title }) {
)) } +
setOpenCategory(!isOpenCategory)}> + Kategori +
+ { !isOpenCategory && } + { isOpenCategory && } +
+
+ { isOpenCategory && categories.map((category) => ( + +
+ + { category.name } + +
toggleCategories(category.id)}> + { !category.isOpen && } + { category.isOpen && } +
+
+ { category.isOpen && category.childs.map((child1Category) => ( + +
+ + { child1Category.name } + + { child1Category.childs.length > 0 && ( +
toggleCategories(child1Category.id)}> + { !child1Category.isOpen && } + { child1Category.isOpen && } +
+ ) } +
+ { child1Category.isOpen && child1Category.childs.map((child2Category) => ( + + { child2Category.name } + + )) } +
+ )) } +
+ )) } diff --git a/src/components/transactions/TransactionStatusBadge.js b/src/components/transactions/TransactionStatusBadge.js new file mode 100644 index 00000000..588542fe --- /dev/null +++ b/src/components/transactions/TransactionStatusBadge.js @@ -0,0 +1,41 @@ +const TransactionStatusBadge = ({ status }) => { + let badgeProps = { + className: ['h-fit'], + text: '' + }; + switch (status) { + case 'cancel': + badgeProps.className.push('badge-red'); + badgeProps.text = 'Batal' + break; + case 'draft': + badgeProps.className.push('badge-gray'); + badgeProps.text = 'Pending Quotation' + break; + case 'waiting': + badgeProps.className.push('badge-yellow'); + badgeProps.text = 'Menunggu Konfirmasi' + break; + case 'sale': + badgeProps.className.push('badge-blue'); + badgeProps.text = 'Pesanan Diproses' + break; + case 'shipping': + badgeProps.className.push('badge-blue'); + badgeProps.text = 'Pesanan Dikirim' + break; + case 'done': + badgeProps.className.push('badge-green'); + badgeProps.text = 'Selesai' + break; + } + badgeProps.className = badgeProps.className.join(' '); + + return ( +
+ { badgeProps.text } +
+ ) +}; + +export default TransactionStatusBadge; \ No newline at end of file diff --git a/src/pages/my/transaction/[id].js b/src/pages/my/transaction/[id].js index 4c33c97a..a508ef77 100644 --- a/src/pages/my/transaction/[id].js +++ b/src/pages/my/transaction/[id].js @@ -15,6 +15,7 @@ import { SkeletonList } from "@/components/elements/Skeleton"; import Link from "@/components/elements/Link"; import { ChevronRightIcon } from "@heroicons/react/24/outline"; import Alert from "@/components/elements/Alert"; +import TransactionStatusBadge from "@/components/transactions/TransactionStatusBadge"; export default function DetailTransaction() { const router = useRouter(); @@ -41,7 +42,9 @@ export default function DetailTransaction() { <>
- Pending Quotation +
+ +
{ transaction?.name } diff --git a/src/pages/my/transactions.js b/src/pages/my/transactions.js index 85f0935f..221859f9 100644 --- a/src/pages/my/transactions.js +++ b/src/pages/my/transactions.js @@ -11,6 +11,7 @@ import { EllipsisVerticalIcon, MagnifyingGlassIcon } from "@heroicons/react/24/o import Link from "@/components/elements/Link"; import Pagination from "@/components/elements/Pagination"; import Alert from "@/components/elements/Alert"; +import TransactionStatusBadge from "@/components/transactions/TransactionStatusBadge"; export default function Transactions() { const [ auth ] = useAuth(); @@ -40,7 +41,8 @@ export default function Transactions() { const dataTransactions = await apiOdoo('GET', `/api/v1/partner/${auth.partner_id}/sale_order${queryParams}`); setTransactions(dataTransactions); - setPageCount(Math.ceil(dataTransactions.sale_order_total / limit)); + console.log(dataTransactions); + setPageCount(Math.ceil(dataTransactions?.sale_order_total / limit)); setIsLoading(false); }; } @@ -88,7 +90,7 @@ export default function Transactions() {

{ transaction.name }

-
Pending
+ setActivePopupId(transaction.id)} />
diff --git a/src/pages/my/wishlist.js b/src/pages/my/wishlist.js index 175bfa08..9683c785 100644 --- a/src/pages/my/wishlist.js +++ b/src/pages/my/wishlist.js @@ -1,24 +1,33 @@ import WithAuth from "@/components/auth/WithAuth"; +import Pagination from "@/components/elements/Pagination"; +import Spinner from "@/components/elements/Spinner"; import AppBar from "@/components/layouts/AppBar"; import Layout from "@/components/layouts/Layout"; import ProductCard from "@/components/products/ProductCard"; import apiOdoo from "@/core/utils/apiOdoo"; import { useAuth } from "@/core/utils/auth"; +import { useRouter } from "next/router"; import { useEffect, useState } from "react"; export default function Wishlist() { const [ auth ] = useAuth(); + const router = useRouter(); + const { page = 1 } = router.query; const [ wishlists, setWishlists ] = useState(null); + const [ pageCount, setPageCount ] = useState(0); useEffect(() => { const loadWishlist = async () => { + const limit = 10; + const offset = (page - 1) * limit; if (auth) { - const dataWishlist = await apiOdoo('GET', `/api/v1/user/${auth.id}/wishlist`); + const dataWishlist = await apiOdoo('GET', `/api/v1/user/${auth.id}/wishlist?limit=${limit}&offset=${offset}`); setWishlists(dataWishlist); + setPageCount(Math.ceil(dataWishlist.product_total / limit)); } } loadWishlist(); - }, [ auth ]); + }, [ auth, page ]); return ( @@ -26,11 +35,18 @@ export default function Wishlist() {
+ { !wishlists && ( + + ) }
{wishlists?.products.map((product) => ( ))}
+ +
+ +
diff --git a/src/pages/shop/cart.js b/src/pages/shop/cart.js index 53d5e648..aaf67e1f 100644 --- a/src/pages/shop/cart.js +++ b/src/pages/shop/cart.js @@ -276,6 +276,7 @@ export default function Cart() { diff --git a/src/pages/shop/checkout.js b/src/pages/shop/checkout.js index 49d1a848..f55b200f 100644 --- a/src/pages/shop/checkout.js +++ b/src/pages/shop/checkout.js @@ -2,7 +2,6 @@ import { ExclamationCircleIcon } from "@heroicons/react/24/solid"; import { useEffect, useState } from "react"; import Alert from "@/components/elements/Alert"; import AppBar from "@/components/layouts/AppBar"; -import Image from "@/components/elements/Image"; import Layout from "@/components/layouts/Layout"; import LineDivider from "@/components/elements/LineDivider"; import Link from "@/components/elements/Link"; diff --git a/src/pages/shop/product/[slug].js b/src/pages/shop/product/[slug].js index dd097bbe..bcfb12ba 100644 --- a/src/pages/shop/product/[slug].js +++ b/src/pages/shop/product/[slug].js @@ -149,7 +149,7 @@ export default function ProductDetail({ product }) { />
-
+
{product.manufacture.name ?? '-'} diff --git a/src/pages/shop/quotation/finish.js b/src/pages/shop/quotation/finish.js new file mode 100644 index 00000000..b1f092d3 --- /dev/null +++ b/src/pages/shop/quotation/finish.js @@ -0,0 +1,39 @@ +import WithAuth from "@/components/auth/WithAuth"; +import Link from "@/components/elements/Link"; +import Header from "@/components/layouts/Header"; +import Layout from "@/components/layouts/Layout"; +import { useAuth } from "@/core/utils/auth"; +import { EnvelopeIcon } from "@heroicons/react/24/outline"; +import { useRouter } from "next/router"; + +export default function FinishQuotation() { + const router = useRouter(); + const { id } = router.query; + const [ auth ] = useAuth(); + + return ( + + +
+ +
+
+ + + +
+

+ Terima Kasih { auth?.name } +

+

+ Penawaran harga kamu di Indoteknik.com berhasil dikirimkan, tim kami akan segera menghubungi anda. +

+ { id && ( + Lihat Penawaran + )} + Ke Halaman Utama +
+ + + ); +} \ No newline at end of file diff --git a/src/pages/shop/quotation/index.js b/src/pages/shop/quotation/index.js new file mode 100644 index 00000000..cf2b956d --- /dev/null +++ b/src/pages/shop/quotation/index.js @@ -0,0 +1,142 @@ +import WithAuth from "@/components/auth/WithAuth"; +import LineDivider from "@/components/elements/LineDivider"; +import Link from "@/components/elements/Link"; +import AppBar from "@/components/layouts/AppBar"; +import Layout from "@/components/layouts/Layout"; +import VariantCard from "@/components/variants/VariantCard"; +import apiOdoo from "@/core/utils/apiOdoo"; +import { useAuth } from "@/core/utils/auth"; +import { deleteItemCart, getCart } from "@/core/utils/cart"; +import currencyFormat from "@/core/utils/currencyFormat"; +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; +import { toast } from "react-hot-toast"; + +export default function Quotation() { + const router = useRouter(); + const [ auth ] = useAuth(); + const [ products, setProducts ] = useState([]); + const [ totalPriceBeforeTax, setTotalPriceBeforeTax ] = useState(0); + const [ totalTaxAmount, setTotalTaxAmount ] = useState(0); + const [ totalDiscountAmount, setTotalDiscountAmount ] = useState(0); + + useEffect(() => { + const getProducts = async () => { + let cart = getCart(); + let productIds = Object + .values(cart) + .filter((itemCart) => itemCart.selected == true) + .map((itemCart) => itemCart.product_id); + if (productIds.length > 0) { + productIds = productIds.join(','); + let dataProducts = await apiOdoo('GET', `/api/v1/product_variant/${productIds}`); + dataProducts = dataProducts.map((product) => ({ + ...product, + quantity: cart[product.id].quantity, + selected: cart[product.id].selected, + })); + setProducts(dataProducts); + } + }; + getProducts(); + }, [ router, auth ]); + + useEffect(() => { + if (products) { + const productsSelected = products.filter((product) => product.selected == true); + let calculateTotalPriceBeforeTax = 0; + let calculateTotalTaxAmount = 0; + let calculateTotalDiscountAmount = 0; + productsSelected.forEach(product => { + let priceBeforeTax = product.price.price / 1.11; + calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity; + calculateTotalTaxAmount += (product.price.price - priceBeforeTax) * product.quantity; + calculateTotalDiscountAmount += (product.price.price - product.price.price_discount) * product.quantity; + }); + setTotalPriceBeforeTax(calculateTotalPriceBeforeTax); + setTotalTaxAmount(calculateTotalTaxAmount); + setTotalDiscountAmount(calculateTotalDiscountAmount); + } + }, [products]); + + const submitQuotation = async () => { + let productOrder = products.map((product) => ({ 'product_id': product.id, 'quantity': product.quantity })); + let data = { + 'partner_shipping_id': auth.partner_id, + 'partner_invoice_id': auth.partner_id, + 'order_line': JSON.stringify(productOrder) + }; + const quotation = await apiOdoo('POST', `/api/v1/partner/${auth.partner_id}/sale_order/checkout`, data); + for (const product of products) { + deleteItemCart(product.id); + } + if (quotation?.id) { + router.push(`/shop/quotation/finish?id=${quotation.id}`); + return; + }; + toast.error('Terdapat kesalahan internal, hubungi kami'); + } + return ( + + + + +
+

Produk

+ {products.map((product, index) => ( + + ))} +
+ + + +
+
+

Ringkasan Penawaran

+

{products.length} Barang

+
+
+
+
+

Subtotal

+

{currencyFormat(totalPriceBeforeTax)}

+
+
+

PPN 11%

+

{currencyFormat(totalTaxAmount)}

+
+
+

Total Diskon

+

- {currencyFormat(totalDiscountAmount)}

+
+
+
+
+

Grand Total

+

{currencyFormat(totalPriceBeforeTax + totalTaxAmount - totalDiscountAmount)}

+
+

*) Belum termasuk biaya pengiriman

+

+ Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui Syarat & Ketentuan yang berlaku +

+
+ + + +
+ +
+
+
+ ) +} \ No newline at end of file diff --git a/src/pages/shop/search.js b/src/pages/shop/search.js index 2521c7a2..4152bd43 100644 --- a/src/pages/shop/search.js +++ b/src/pages/shop/search.js @@ -10,7 +10,7 @@ import Footer from "@/components/layouts/Footer"; export async function getServerSideProps(context) { const { - q, + q = '*', page = 1, brand = '', category = '', @@ -100,7 +100,7 @@ export default function ShopSearch({ ) : ''} {searchResults.response.numFound} -  produk untuk pencarian {q} +  produk { q != '*' && (<>untuk pencarian {q}) } ) : 'Mungkin yang anda cari'}
diff --git a/src/styles/globals.css b/src/styles/globals.css index e43ad1a9..3a890a0e 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -56,6 +56,7 @@ html, body { .badge-red, .badge-gray, .badge-yellow, + .badge-blue, .badge-green { @apply text-caption-2 @@ -84,8 +85,15 @@ html, body { .badge-yellow { @apply - bg-yellow_r-5 - text-yellow_r-10 + bg-yellow_r-3 + text-yellow_r-11 + ; + } + + .badge-blue { + @apply + bg-blue-200 + text-blue-600 ; } -- cgit v1.2.3