From f99e0aba70efad0deb907d8e27f09fc9f527c8a4 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Fri, 17 Feb 2023 17:07:50 +0700 Subject: Refactor --- src/lib/brand/api/BrandApi.js | 8 + src/lib/brand/components/Brand.jsx | 70 ++++++ src/lib/brand/components/BrandCard.jsx | 20 ++ src/lib/brand/hooks/useBrand.js | 13 + src/lib/cart/api/CartApi.js | 11 + src/lib/cart/components/Cart.jsx | 30 +++ src/lib/cart/hooks/useCart.js | 17 ++ src/lib/elements/hooks/useBottomPopup.js | 40 --- src/lib/elements/hooks/useConfirmAlert.js | 49 ---- src/lib/home/api/categoryHomeApi.js | 8 + src/lib/home/api/categoryHomeIdApi.js | 8 + src/lib/home/api/heroBannerApi.js | 8 + src/lib/home/api/popularProductApi.js | 8 + src/lib/home/api/preferredBrandApi.js | 8 + src/lib/home/components/CategoryHome.jsx | 28 +++ src/lib/home/components/CategoryHomeId.jsx | 19 ++ src/lib/home/components/HeroBanner.jsx | 50 ++++ src/lib/home/components/PopularProduct.jsx | 24 ++ src/lib/home/components/PreferredBrand.jsx | 30 +++ .../components/Skeleton/PopularProductSkeleton.jsx | 10 + .../components/Skeleton/PreferredBrandSkeleton.jsx | 12 + src/lib/home/hooks/useCategoryHome.js | 13 + src/lib/home/hooks/useCategoryHomeId.js | 13 + src/lib/home/hooks/useHeroBanner.js | 13 + src/lib/home/hooks/usePopularProduct.js | 13 + src/lib/home/hooks/usePreferredBrand.js | 13 + src/lib/product/api/productApi.js | 8 + src/lib/product/api/productSearchApi.js | 9 + src/lib/product/api/productSimilarApi.js | 8 + src/lib/product/components/Product.jsx | 276 +++++++++++++++++++++ src/lib/product/components/ProductCard.jsx | 68 +++++ src/lib/product/components/ProductFilter.jsx | 131 ++++++++++ src/lib/product/components/ProductSearch.jsx | 95 +++++++ src/lib/product/components/ProductSimilar.jsx | 15 ++ src/lib/product/components/ProductSlider.jsx | 51 ++++ .../components/Skeleton/ProductSearchSkeleton.jsx | 14 ++ src/lib/product/hooks/useProductSearch.js | 15 ++ src/lib/product/hooks/useProductSimilar.js | 13 + 38 files changed, 1150 insertions(+), 89 deletions(-) create mode 100644 src/lib/brand/api/BrandApi.js create mode 100644 src/lib/brand/components/Brand.jsx create mode 100644 src/lib/brand/components/BrandCard.jsx create mode 100644 src/lib/brand/hooks/useBrand.js create mode 100644 src/lib/cart/api/CartApi.js create mode 100644 src/lib/cart/components/Cart.jsx create mode 100644 src/lib/cart/hooks/useCart.js delete mode 100644 src/lib/elements/hooks/useBottomPopup.js delete mode 100644 src/lib/elements/hooks/useConfirmAlert.js create mode 100644 src/lib/home/api/categoryHomeApi.js create mode 100644 src/lib/home/api/categoryHomeIdApi.js create mode 100644 src/lib/home/api/heroBannerApi.js create mode 100644 src/lib/home/api/popularProductApi.js create mode 100644 src/lib/home/api/preferredBrandApi.js create mode 100644 src/lib/home/components/CategoryHome.jsx create mode 100644 src/lib/home/components/CategoryHomeId.jsx create mode 100644 src/lib/home/components/HeroBanner.jsx create mode 100644 src/lib/home/components/PopularProduct.jsx create mode 100644 src/lib/home/components/PreferredBrand.jsx create mode 100644 src/lib/home/components/Skeleton/PopularProductSkeleton.jsx create mode 100644 src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx create mode 100644 src/lib/home/hooks/useCategoryHome.js create mode 100644 src/lib/home/hooks/useCategoryHomeId.js create mode 100644 src/lib/home/hooks/useHeroBanner.js create mode 100644 src/lib/home/hooks/usePopularProduct.js create mode 100644 src/lib/home/hooks/usePreferredBrand.js create mode 100644 src/lib/product/api/productApi.js create mode 100644 src/lib/product/api/productSearchApi.js create mode 100644 src/lib/product/api/productSimilarApi.js create mode 100644 src/lib/product/components/Product.jsx create mode 100644 src/lib/product/components/ProductCard.jsx create mode 100644 src/lib/product/components/ProductFilter.jsx create mode 100644 src/lib/product/components/ProductSearch.jsx create mode 100644 src/lib/product/components/ProductSimilar.jsx create mode 100644 src/lib/product/components/ProductSlider.jsx create mode 100644 src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx create mode 100644 src/lib/product/hooks/useProductSearch.js create mode 100644 src/lib/product/hooks/useProductSimilar.js (limited to 'src/lib') diff --git a/src/lib/brand/api/BrandApi.js b/src/lib/brand/api/BrandApi.js new file mode 100644 index 00000000..15634cc4 --- /dev/null +++ b/src/lib/brand/api/BrandApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const BrandApi = async ({ id }) => { + const dataBrand = await odooApi('GET', `/api/v1/manufacture/${id}`) + return dataBrand +} + +export default BrandApi \ No newline at end of file diff --git a/src/lib/brand/components/Brand.jsx b/src/lib/brand/components/Brand.jsx new file mode 100644 index 00000000..a42f4c81 --- /dev/null +++ b/src/lib/brand/components/Brand.jsx @@ -0,0 +1,70 @@ +import useBrand from "../hooks/useBrand" +import Image from "@/core/components/elements/Image/Image" + +import { Swiper, SwiperSlide } from "swiper/react" +import { Pagination, Autoplay } from "swiper" +import "swiper/css" +import "swiper/css/pagination" +import "swiper/css/autoplay" +import Divider from "@/core/components/elements/Divider/Divider" +import ImageSkeleton from "@/core/components/elements/Skeleton/ImageSkeleton" + +const swiperBanner = { + pagination: { dynamicBullets: true }, + autoplay: { + delay: 6000, + disableOnInteraction: false + }, + modules: [Pagination, Autoplay] +} + +const Brand = ({ id }) => { + const { brand } = useBrand({ id }) + + return ( + <> +
+ { brand.isLoading && } + { brand.data && ( + <> + + { brand.data?.banners?.map((banner, index) => ( + + {`Brand + + )) } + +
+
Produk dari brand:
+ { brand?.data?.logo && ( + {brand?.data?.name} + ) } + { !brand?.data?.logo && ( +
+ { brand?.data?.name } +
+ ) } +
+ + ) } +
+ + + ) +} + +export default Brand \ No newline at end of file diff --git a/src/lib/brand/components/BrandCard.jsx b/src/lib/brand/components/BrandCard.jsx new file mode 100644 index 00000000..8783010e --- /dev/null +++ b/src/lib/brand/components/BrandCard.jsx @@ -0,0 +1,20 @@ +import Image from "@/core/components/elements/Image/Image" +import Link from "@/core/components/elements/Link/Link" +import { createSlug } from "@/core/utils/slug" + +const BrandCard = ({ brand }) => { + return ( + + {brand.name} + + ) +} + +export default BrandCard \ No newline at end of file diff --git a/src/lib/brand/hooks/useBrand.js b/src/lib/brand/hooks/useBrand.js new file mode 100644 index 00000000..be42a44c --- /dev/null +++ b/src/lib/brand/hooks/useBrand.js @@ -0,0 +1,13 @@ +import { useQuery } from "react-query" +import BrandApi from "../api/BrandApi" + +const useBrand = ({ id }) => { + const fetchBrand = async () => await BrandApi({ id }) + const { data, isLoading } = useQuery(`brand-${id}`, fetchBrand) + + return { + brand: { data, isLoading } + } +} + +export default useBrand \ No newline at end of file diff --git a/src/lib/cart/api/CartApi.js b/src/lib/cart/api/CartApi.js new file mode 100644 index 00000000..9a5b5053 --- /dev/null +++ b/src/lib/cart/api/CartApi.js @@ -0,0 +1,11 @@ +import odooApi from "@/core/api/odooApi" + +const CartApi = async ({ variantIds }) => { + if (variantIds) { + const dataCart = await odooApi('GET', `/api/v1/product_variant/${variantIds}`) + return dataCart + } + return null +} + +export default CartApi \ No newline at end of file diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx new file mode 100644 index 00000000..5f9ae1c0 --- /dev/null +++ b/src/lib/cart/components/Cart.jsx @@ -0,0 +1,30 @@ +import Link from "@/core/components/elements/Link/Link" +import useCart from "../hooks/useCart" +import Image from "@/core/components/elements/Image/Image" + +const Cart = () => { + const { cart } = useCart() + + return ( +
+
+

Daftar Produk Belanja

+ Cari Produk Lain +
+
+ { cart.data?.map((product) => ( +
+
+ {product?.name} +
+
+
{ product?.parent?.name }
+
+
+ )) } +
+
+ ) +} + +export default Cart \ No newline at end of file diff --git a/src/lib/cart/hooks/useCart.js b/src/lib/cart/hooks/useCart.js new file mode 100644 index 00000000..44931b8a --- /dev/null +++ b/src/lib/cart/hooks/useCart.js @@ -0,0 +1,17 @@ +import { getCart } from "@/core/utils/cart" +import { useQuery } from "react-query" +import _ from "lodash" +import CartApi from "../api/CartApi" + +const useCart = () => { + const cart = getCart() + const variantIds = _.keys(cart).join(',') + const fetchCart = async () => CartApi({ variantIds }) + const { data, isLoading } = useQuery('cart', fetchCart) + + return { + cart: { data, isLoading } + } +} + +export default useCart \ No newline at end of file diff --git a/src/lib/elements/hooks/useBottomPopup.js b/src/lib/elements/hooks/useBottomPopup.js deleted file mode 100644 index 88b72316..00000000 --- a/src/lib/elements/hooks/useBottomPopup.js +++ /dev/null @@ -1,40 +0,0 @@ -import { useState } from "react"; -import dynamic from "next/dynamic"; - -const DynamicBottomPopup = dynamic(() => import('@/components/elements/BottomPopup')); - -const useBottomPopup = ({ - title, - children -}) => { - const [ isOpen, setIsOpen ] = useState(false); - const [ dataPopup, setDataPopup ] = useState(null); - - const closePopup = () => { - setIsOpen(false); - setDataPopup(null); - }; - const openPopup = ( data = null ) => { - setIsOpen(true); - setDataPopup(data); - }; - - const BottomPopup = ( - - { children(dataPopup) } - - ); - - return { - dataPopup, - BottomPopup, - closePopup, - openPopup - } -} - -export default useBottomPopup; \ No newline at end of file diff --git a/src/lib/elements/hooks/useConfirmAlert.js b/src/lib/elements/hooks/useConfirmAlert.js deleted file mode 100644 index 4975c57d..00000000 --- a/src/lib/elements/hooks/useConfirmAlert.js +++ /dev/null @@ -1,49 +0,0 @@ -import { useState } from "react"; -import dynamic from "next/dynamic"; - -const DynamicConfirmAlert = dynamic(() => import('@/components/elements/ConfirmAlert')); - -const useConfirmAlert = ({ - title, - caption, - closeText, - submitText, - onSubmit, -}) => { - const [ isOpen, setIsOpen ] = useState(false); - const [ data, setData ] = useState(null); - - const closeConfirmAlert = () => { - setIsOpen(false); - setData(null); - }; - const openConfirmAlert = ( data = null ) => { - setIsOpen(true); - setData(data); - }; - const handleSubmit = async () => { - await onSubmit(data); - closeConfirmAlert(); - }; - - const ConfirmAlert = ( - - ); - - return { - isOpen, - closeConfirmAlert, - openConfirmAlert, - ConfirmAlert - }; -} - -export default useConfirmAlert; \ No newline at end of file diff --git a/src/lib/home/api/categoryHomeApi.js b/src/lib/home/api/categoryHomeApi.js new file mode 100644 index 00000000..efb31240 --- /dev/null +++ b/src/lib/home/api/categoryHomeApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const categoryHomeIdApi = async ({ id }) => { + const dataCategoryHomeId = await odooApi('GET', `/api/v1/categories_homepage?id=${id}`) + return dataCategoryHomeId +} + +export default categoryHomeIdApi \ No newline at end of file diff --git a/src/lib/home/api/categoryHomeIdApi.js b/src/lib/home/api/categoryHomeIdApi.js new file mode 100644 index 00000000..d5612195 --- /dev/null +++ b/src/lib/home/api/categoryHomeIdApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const categoryHomeIdApi = async () => { + const dataCategoryHomeIds = await odooApi('GET', '/api/v1/categories_homepage/ids') + return dataCategoryHomeIds +} + +export default categoryHomeIdApi \ No newline at end of file diff --git a/src/lib/home/api/heroBannerApi.js b/src/lib/home/api/heroBannerApi.js new file mode 100644 index 00000000..7ba84bc6 --- /dev/null +++ b/src/lib/home/api/heroBannerApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const heroBannerApi = async () => { + const dataHeroBanners = await odooApi('GET', '/api/v1/banner?type=index-a-1') + return dataHeroBanners +} + +export default heroBannerApi \ No newline at end of file diff --git a/src/lib/home/api/popularProductApi.js b/src/lib/home/api/popularProductApi.js new file mode 100644 index 00000000..d7adca83 --- /dev/null +++ b/src/lib/home/api/popularProductApi.js @@ -0,0 +1,8 @@ +import axios from "axios" + +const popularProductApi = async () => { + const dataPopularProducts = await axios(`${process.env.SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular`) + return dataPopularProducts.data.response +} + +export default popularProductApi \ No newline at end of file diff --git a/src/lib/home/api/preferredBrandApi.js b/src/lib/home/api/preferredBrandApi.js new file mode 100644 index 00000000..f289f387 --- /dev/null +++ b/src/lib/home/api/preferredBrandApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const preferredBrandApi = async () => { + const dataPreferredBrands = await odooApi('GET', '/api/v1/manufacture?level=prioritas') + return dataPreferredBrands +} + +export default preferredBrandApi \ No newline at end of file diff --git a/src/lib/home/components/CategoryHome.jsx b/src/lib/home/components/CategoryHome.jsx new file mode 100644 index 00000000..0bca9846 --- /dev/null +++ b/src/lib/home/components/CategoryHome.jsx @@ -0,0 +1,28 @@ +import ProductSlider from "@/lib/product/components/ProductSlider" +import useCategoryHome from "../hooks/useCategoryHome" +import PopularProductSkeleton from "./Skeleton/PopularProductSkeleton" + +const CategoryHome = ({ id }) => { + const { categoryHome } = useCategoryHome({ id }) + + return ( +
+ { categoryHome.data ? ( + + ) : } +
+ ) +} + +export default CategoryHome \ No newline at end of file diff --git a/src/lib/home/components/CategoryHomeId.jsx b/src/lib/home/components/CategoryHomeId.jsx new file mode 100644 index 00000000..4cbbd1fc --- /dev/null +++ b/src/lib/home/components/CategoryHomeId.jsx @@ -0,0 +1,19 @@ +import { LazyLoadComponent } from "react-lazy-load-image-component" +import useCategoryHomeId from "../hooks/useCategoryHomeId" +import CategoryHome from "./CategoryHome" + +const CategoryHomeId = () => { + const { categoryHomeIds } = useCategoryHomeId() + + return ( +
+ { categoryHomeIds.data?.map((id) => ( + + + + )) } +
+ ) +} + +export default CategoryHomeId \ No newline at end of file diff --git a/src/lib/home/components/HeroBanner.jsx b/src/lib/home/components/HeroBanner.jsx new file mode 100644 index 00000000..604ca8ac --- /dev/null +++ b/src/lib/home/components/HeroBanner.jsx @@ -0,0 +1,50 @@ +import ImageSkeleton from "@/core/components/elements/Skeleton/ImageSkeleton" +import useHeroBanner from "../hooks/useHeroBanner" +import Image from "@/core/components/elements/Image/Image" + +// Swiper +import { Swiper, SwiperSlide } from "swiper/react" +import { Pagination, Autoplay } from "swiper" +import "swiper/css" +import "swiper/css/pagination" +import "swiper/css/autoplay" + +const swiperBanner = { + pagination: { dynamicBullets: true }, + autoplay: { + delay: 6000, + disableOnInteraction: false + }, + modules: [Pagination, Autoplay] +} + +const HeroBanner = () => { + const { heroBanners } = useHeroBanner() + + return ( +
+ { heroBanners.isLoading && } + { !heroBanners.isLoading && ( + + { heroBanners.data?.map((banner, index) => ( + + {banner.name} + + )) } + + ) } +
+ ) +} + +export default HeroBanner \ No newline at end of file diff --git a/src/lib/home/components/PopularProduct.jsx b/src/lib/home/components/PopularProduct.jsx new file mode 100644 index 00000000..87e47218 --- /dev/null +++ b/src/lib/home/components/PopularProduct.jsx @@ -0,0 +1,24 @@ +import { Swiper, SwiperSlide } from "swiper/react" +import usePopularProduct from "../hooks/usePopularProduct" +import ProductCard from "@/lib/product/components/ProductCard" +import PopularProductSkeleton from "./Skeleton/PopularProductSkeleton" +import ProductSlider from "@/lib/product/components/ProductSlider" + +const PopularProduct = () => { + const { popularProducts } = usePopularProduct() + + return ( +
+
Produk Populer
+ { popularProducts.isLoading && } + { !popularProducts.isLoading && ( + + ) } +
+ ) +} + +export default PopularProduct \ No newline at end of file diff --git a/src/lib/home/components/PreferredBrand.jsx b/src/lib/home/components/PreferredBrand.jsx new file mode 100644 index 00000000..3d3b1b69 --- /dev/null +++ b/src/lib/home/components/PreferredBrand.jsx @@ -0,0 +1,30 @@ +import { Swiper, SwiperSlide } from "swiper/react" +import usePreferredBrand from "../hooks/usePreferredBrand" +import PreferredBrandSkeleton from "./Skeleton/PreferredBrandSkeleton" +import BrandCard from "@/lib/brand/components/BrandCard" + +const PreferredBrand = () => { + const { preferredBrands } = usePreferredBrand() + + return ( +
+
Brand Pilihan
+ { preferredBrands.isLoading && } + { !preferredBrands.isLoading && ( + + { preferredBrands.data?.manufactures.map((brand) => ( + + + + )) } + + ) } +
+ ) +} + +export default PreferredBrand \ No newline at end of file diff --git a/src/lib/home/components/Skeleton/PopularProductSkeleton.jsx b/src/lib/home/components/Skeleton/PopularProductSkeleton.jsx new file mode 100644 index 00000000..c5b0fcaa --- /dev/null +++ b/src/lib/home/components/Skeleton/PopularProductSkeleton.jsx @@ -0,0 +1,10 @@ +import ProductCardSkeleton from "@/core/components/elements/Skeleton/ProductCardSkeleton" + +const PopularProductSkeleton = () => ( +
+ + +
+) + +export default PopularProductSkeleton \ No newline at end of file diff --git a/src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx b/src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx new file mode 100644 index 00000000..6bdd3c82 --- /dev/null +++ b/src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx @@ -0,0 +1,12 @@ +import BrandSkeleton from "@/core/components/elements/Skeleton/BrandSkeleton" + +const PreferredBrandSkeleton = () => ( +
+ + + + +
+) + +export default PreferredBrandSkeleton \ No newline at end of file diff --git a/src/lib/home/hooks/useCategoryHome.js b/src/lib/home/hooks/useCategoryHome.js new file mode 100644 index 00000000..14ef2a0f --- /dev/null +++ b/src/lib/home/hooks/useCategoryHome.js @@ -0,0 +1,13 @@ +import categoryHomeApi from "../api/categoryHomeApi" +import { useQuery } from "react-query" + +const useCategoryHome = ({ id }) => { + const fetchCategoryHome = async () => await categoryHomeApi({ id }) + const { isLoading, data } = useQuery(`categoryHome-${id}`, fetchCategoryHome) + + return { + categoryHome: { data, isLoading } + } +} + +export default useCategoryHome \ No newline at end of file diff --git a/src/lib/home/hooks/useCategoryHomeId.js b/src/lib/home/hooks/useCategoryHomeId.js new file mode 100644 index 00000000..bb61b655 --- /dev/null +++ b/src/lib/home/hooks/useCategoryHomeId.js @@ -0,0 +1,13 @@ +import categoryHomeIdApi from "../api/categoryHomeIdApi" +import { useQuery } from "react-query" + +const useCategoryHomeId = () => { + const fetchCategoryHomeId = async () => await categoryHomeIdApi() + const { isLoading, data } = useQuery("categoryHomeId", fetchCategoryHomeId) + + return { + categoryHomeIds: { data, isLoading } + } +} + +export default useCategoryHomeId \ No newline at end of file diff --git a/src/lib/home/hooks/useHeroBanner.js b/src/lib/home/hooks/useHeroBanner.js new file mode 100644 index 00000000..a15dda60 --- /dev/null +++ b/src/lib/home/hooks/useHeroBanner.js @@ -0,0 +1,13 @@ +import heroBannerApi from "../api/heroBannerApi" +import { useQuery } from "react-query" + +const useHeroBanner = () => { + const fetchHeroBanner = async () => await heroBannerApi() + const { isLoading, data } = useQuery("heroBanner", fetchHeroBanner) + + return { + heroBanners: { data, isLoading } + } +} + +export default useHeroBanner \ No newline at end of file diff --git a/src/lib/home/hooks/usePopularProduct.js b/src/lib/home/hooks/usePopularProduct.js new file mode 100644 index 00000000..f69c2f71 --- /dev/null +++ b/src/lib/home/hooks/usePopularProduct.js @@ -0,0 +1,13 @@ +import popularProductApi from "../api/popularProductApi" +import { useQuery } from "react-query" + +const usePopularProduct = () => { + const fetchPopularProduct = async () => await popularProductApi() + const { data, isLoading } = useQuery('popularProduct', fetchPopularProduct) + + return { + popularProducts: { data, isLoading } + } +} + +export default usePopularProduct \ No newline at end of file diff --git a/src/lib/home/hooks/usePreferredBrand.js b/src/lib/home/hooks/usePreferredBrand.js new file mode 100644 index 00000000..4be9793e --- /dev/null +++ b/src/lib/home/hooks/usePreferredBrand.js @@ -0,0 +1,13 @@ +import preferredBrandApi from "../api/preferredBrandApi" +import { useQuery } from "react-query" + +const usePreferredBrand = () => { + const fetchPreferredBrand = async () => await preferredBrandApi() + const { data, isLoading } = useQuery('preferredBrand', fetchPreferredBrand) + + return { + preferredBrands: { data, isLoading } + } +} + +export default usePreferredBrand \ No newline at end of file diff --git a/src/lib/product/api/productApi.js b/src/lib/product/api/productApi.js new file mode 100644 index 00000000..a543f086 --- /dev/null +++ b/src/lib/product/api/productApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const productApi = async ({ id }) => { + const dataProduct = await odooApi('GET', `/api/v1/product/${id}`) + return dataProduct +} + +export default productApi \ No newline at end of file diff --git a/src/lib/product/api/productSearchApi.js b/src/lib/product/api/productSearchApi.js new file mode 100644 index 00000000..86b2914f --- /dev/null +++ b/src/lib/product/api/productSearchApi.js @@ -0,0 +1,9 @@ +import _ from "lodash-contrib" +import axios from "axios" + +const productSearchApi = async ({ query }) => { + const dataProductSearch = await axios(`${process.env.SELF_HOST}/api/shop/search?${query}`) + return dataProductSearch.data +} + +export default productSearchApi \ No newline at end of file diff --git a/src/lib/product/api/productSimilarApi.js b/src/lib/product/api/productSimilarApi.js new file mode 100644 index 00000000..1449d9ca --- /dev/null +++ b/src/lib/product/api/productSimilarApi.js @@ -0,0 +1,8 @@ +import axios from "axios" + +const productSimilarApi = async ({ query }) => { + const dataProductSimilar = await axios(`${process.env.SELF_HOST}/api/shop/search?q=${query}&page=1&orderBy=popular`) + return dataProductSimilar.data.response +} + +export default productSimilarApi \ No newline at end of file diff --git a/src/lib/product/components/Product.jsx b/src/lib/product/components/Product.jsx new file mode 100644 index 00000000..2a3624e7 --- /dev/null +++ b/src/lib/product/components/Product.jsx @@ -0,0 +1,276 @@ +import Badge from "@/core/components/elements/Badge/Badge" +import Divider from "@/core/components/elements/Divider/Divider" +import Image from "@/core/components/elements/Image/Image" +import Link from "@/core/components/elements/Link/Link" +import currencyFormat from "@/core/utils/currencyFormat" +import { useEffect, useState } from "react" +import Select from "react-select" +import ProductSimilar from "./ProductSimilar" +import LazyLoad from "react-lazy-load" +import { toast } from "react-hot-toast" +import { addItemCart } from "@/core/utils/cart" + +const informationTabOptions = [ + { value: 'specification', label: 'Spesifikasi' }, + { value: 'description', label: 'Deskripsi' }, + { value: 'important', label: 'Info Penting' }, +] + +const Product = ({ product }) => { + const [ quantity, setQuantity ] = useState('1') + const [ selectedVariant, setSelectedVariant ] = useState(null) + const [ informationTab, setInformationTab ] = useState(null) + + const [ activeVariant, setActiveVariant ] = useState({ + id: product.id, + code: product.code, + name: product.name, + price: product.lowestPrice, + stock: product.stockTotal, + weight: product.weight, + }) + + const variantOptions = product.variants?.map((variant) => ({ + value: variant.id, + label: + (variant.code ? `[${variant.code}] ` : '') + + + (variant.attributes.length > 0 ? variant.attributes.join(', ') : product.name) + })) + + useEffect(() => { + if (!selectedVariant && variantOptions.length == 1) { + setSelectedVariant(variantOptions[0]) + } + }, [selectedVariant, variantOptions]) + + useEffect(() => { + if (selectedVariant) { + const variant = product.variants.find(variant => variant.id == selectedVariant.value) + const variantAttributes = variant.attributes.length > 0 ? ' - ' + variant.attributes.join(', ') : '' + console.log(variant); + setActiveVariant({ + id: variant.id, + code: variant.code, + name: variant.parent.name + variantAttributes, + price: variant.price, + stock: variant.stock, + weight: variant.weight + }) + } + }, [selectedVariant, product]) + + useEffect(() => { + if (!informationTab) { + setInformationTab(informationTabOptions[0].value) + } + }, [informationTab]) + + const handleClickCart = () => { + if (!selectedVariant) { + toast.error('Pilih varian terlebih dahulu') + return + } + if (!quantity || quantity < 1 || isNaN(parseInt(quantity))) { + toast.error('Jumlah barang minimal 1') + return + } + addItemCart({ + productId: activeVariant.id, + quantity + }) + toast.success('Berhasil menambahkan ke keranjang') + } + + return ( + <> + {product.name} + +
+ { product.manufacture?.name } +

+ {activeVariant?.name} +

+ { activeVariant?.price?.discountPercentage > 0 && ( +
+
+ {currencyFormat(activeVariant?.price?.priceDiscount)} +
+ + {activeVariant?.price?.discountPercentage}% + +
+ ) } +

+ { activeVariant?.price?.price > 0 ? currencyFormat(activeVariant?.price?.price) : ( + + Hubungi kami untuk dapatkan harga terbaik,  + klik disini + + ) } +

+
+ + + +
+
+ + setQuantity(e.target.value)} + /> +
+ + +
+ + + + +
+

Informasi Produk

+
+ { informationTabOptions.map((option) => ( + setInformationTab(option.value)} + > + {option.label} + + )) } +
+ + + + {product?.variantTotal} Varian + + + SKU-{product?.id} + + + {activeVariant?.code || '-'} + + + { activeVariant?.stock > 0 && ( + +
Ready Stock
+
+ { activeVariant?.stock > 5 ? '> 5' : '< 5' } +
+
+ ) } + { activeVariant?.stock == 0 && ( + + Tanya Stok + + ) } +
+ + { activeVariant?.weight > 0 && ( + { activeVariant?.weight } KG + ) } + { activeVariant?.weight == 0 && ( + + Tanya Berat + + ) } + +
+ + +
+ + + +
+

Kamu Mungkin Juga Suka

+ + + +
+ + ) +} + +const TabButton = ({ children, active, ...props }) => { + const activeClassName = active ? 'text-red_r-11 border-b border-red_r-11' : 'text-gray_r-11' + return ( + + ) +} + +const TabContent = ({ children, active, className, ...props }) => ( +
+ { children } +
+) + +const SpecificationContent = ({ children, label }) => ( +
+ { label } + { children } +
+) + +export default Product \ No newline at end of file diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx new file mode 100644 index 00000000..86ac3a64 --- /dev/null +++ b/src/lib/product/components/ProductCard.jsx @@ -0,0 +1,68 @@ +import Image from "@/core/components/elements/Image/Image" +import Link from "@/core/components/elements/Link/Link" +import currencyFormat from "@/core/utils/currencyFormat" +import { createSlug } from "@/core/utils/slug" + +const ProductCard = ({ product, simpleTitle }) => { + return ( + <> +
+ + {product?.name} + { product.variantTotal > 1 && ( +
{ product.variantTotal } Varian
+ ) } + +
+ + {product?.manufacture?.name} + + + {product?.name} + + { product?.lowestPrice?.discountPercentage > 0 && ( +
+
+ {currencyFormat(product?.lowestPrice?.price)} +
+
+ {product?.lowestPrice?.discountPercentage}% +
+
+ ) } + +
+ { product?.lowestPrice?.priceDiscount > 0 ? currencyFormat(product?.lowestPrice?.priceDiscount) : ( + Call for price + ) } +
+ { product?.stockTotal > 0 && ( +
+
+ Ready Stock +
+
+ { product?.stockTotal > 5 ? '> 5' : '< 5' } +
+
+ ) } +
+
+ + ) +} + +export default ProductCard \ No newline at end of file diff --git a/src/lib/product/components/ProductFilter.jsx b/src/lib/product/components/ProductFilter.jsx new file mode 100644 index 00000000..023b6a8b --- /dev/null +++ b/src/lib/product/components/ProductFilter.jsx @@ -0,0 +1,131 @@ +import BottomPopup from "@/core/components/elements/Popup/BottomPopup" +import { useRouter } from "next/router" +import { useState } from "react" +import _ from "lodash" +import { toQuery } from "lodash-contrib" + +const orderOptions = [ + { value: 'price-asc', label: 'Harga Terendah' }, + { value: 'price-desc', label: 'Harga Tertinggi' }, + { value: 'popular', label: 'Populer' }, + { value: 'stock', label: 'Ready Stock' }, +] + +const ProductFilter = ({ + active, + close, + brands, + categories, + prefixUrl, + defaultBrand = null +}) => { + const router = useRouter() + const { query } = router + const [ order, setOrder ] = useState(query?.orderBy) + const [ brand, setBrand ] = useState(query?.brand) + const [ category, setCategory ] = useState(query?.category) + const [ priceFrom, setPriceFrom ] = useState(query?.priceFrom) + const [ priceTo, setPriceTo ] = useState(query?.priceTo) + + const handleSubmit = () => { + let params = { + q: router.query.q, + orderBy: order, + brand, + category, + priceFrom, + priceTo + } + params = _.pickBy(params, _.identity) + params = toQuery(params) + router.push(`${prefixUrl}?${params}`) + } + + return ( + +
+ { !defaultBrand && ( +
+ + +
+ ) } +
+ + +
+
+ +
+ { orderOptions.map((orderOption) => ( + + )) } +
+
+
+ +
+ setPriceFrom(e.target.value)} + /> + + setPriceTo(e.target.value)} + /> +
+
+ +
+
+ ) +} + +export default ProductFilter \ No newline at end of file diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx new file mode 100644 index 00000000..14df9864 --- /dev/null +++ b/src/lib/product/components/ProductSearch.jsx @@ -0,0 +1,95 @@ +import { useEffect, useState } from "react" +import useProductSearch from "../hooks/useProductSearch" +import ProductCard from "./ProductCard" +import Pagination from "@/core/components/elements/Pagination/Pagination" +import { toQuery } from "lodash-contrib" +import _ from "lodash" +import ProductSearchSkeleton from "./Skeleton/ProductSearchSkeleton" +import ProductFilter from "./ProductFilter" +import useActive from "@/core/hooks/useActive" + +const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { + const { page = 1 } = query + if (defaultBrand) query.brand = defaultBrand.toLowerCase() + const { productSearch } = useProductSearch({ query }) + const [ products, setProducts ] = useState(null) + const popup = useActive() + + const pageCount = Math.ceil(productSearch.data?.response.numFound / productSearch.data?.responseHeader.params.rows) + const productStart = productSearch.data?.responseHeader.params.start + const productRows = productSearch.data?.responseHeader.params.rows + const productFound = productSearch.data?.response.numFound + + const brands = productSearch.data?.facetCounts?.facetFields?.brandStr?.filter((value, index) => { + if (index % 2 === 0) { + return true + } + }) + const categories = productSearch.data?.facetCounts?.facetFields?.categoryNameStr?.filter((value, index) => { + if (index % 2 === 0) { + return true + } + }) + + useEffect(() => { + if (!products) { + setProducts(productSearch.data?.response?.products) + } + }, [query, products, productSearch]) + + if (productSearch.isLoading) { + return + } + + return ( +
+

Produk

+ +
+ { productFound > 0 ? ( + <> + Menampilkan  + {pageCount > 1 ? ( + <> + {productStart + 1}-{ + (productStart + productRows) > productFound ? productFound : productStart + productRows + } +  dari  + + ) : ''} + { productFound } +  produk { query.q && (<>untuk pencarian { query.q }) } + + ) : 'Mungkin yang anda cari'} +
+ + + +
+ { products && products.map((product) => ( + + )) } +
+ + + + +
+ ) +} + +export default ProductSearch \ No newline at end of file diff --git a/src/lib/product/components/ProductSimilar.jsx b/src/lib/product/components/ProductSimilar.jsx new file mode 100644 index 00000000..89cab536 --- /dev/null +++ b/src/lib/product/components/ProductSimilar.jsx @@ -0,0 +1,15 @@ +import PopularProductSkeleton from "@/lib/home/components/Skeleton/PopularProductSkeleton" +import useProductSimilar from "../hooks/useProductSimilar" +import ProductSlider from "./ProductSlider" + +const ProductSimilar = ({ query }) => { + const { productSimilar } = useProductSimilar({ query }) + + if (productSimilar.isLoading) { + return + } + + return +} + +export default ProductSimilar \ No newline at end of file diff --git a/src/lib/product/components/ProductSlider.jsx b/src/lib/product/components/ProductSlider.jsx new file mode 100644 index 00000000..8d677547 --- /dev/null +++ b/src/lib/product/components/ProductSlider.jsx @@ -0,0 +1,51 @@ +import { Swiper, SwiperSlide } from "swiper/react" +import ProductCard from "./ProductCard" +import "swiper/css" +import Image from "@/core/components/elements/Image/Image" +import Link from "@/core/components/elements/Link/Link" +import { useState } from "react" + +const bannerClassName = 'absolute rounded-r top-0 left-0 h-full max-w-[52%] idt-transition border border-gray_r-6' + +const ProductSlider = ({ + products, + simpleTitle = false, + bannerMode = false +}) => { + const [ activeIndex, setActiveIndex ] = useState(0) + const swiperSliderFirstMove = (swiper) => { + setActiveIndex(swiper.activeIndex) + } + + return ( + <> + { bannerMode && ( + {products.banner.name} 0 ? 'opacity-0' : 'opacity-100'}`} + /> + ) } + + { bannerMode && ( + + + + ) } + { products?.products?.map((product, index) => ( + + + + )) } + + + ) +} + +export default ProductSlider \ No newline at end of file diff --git a/src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx b/src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx new file mode 100644 index 00000000..e51a565c --- /dev/null +++ b/src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx @@ -0,0 +1,14 @@ +import ProductCardSkeleton from "@/core/components/elements/Skeleton/ProductCardSkeleton" + +const ProductSearchSkeleton = () => ( +
+ + + + + + +
+) + +export default ProductSearchSkeleton \ No newline at end of file diff --git a/src/lib/product/hooks/useProductSearch.js b/src/lib/product/hooks/useProductSearch.js new file mode 100644 index 00000000..d23a8098 --- /dev/null +++ b/src/lib/product/hooks/useProductSearch.js @@ -0,0 +1,15 @@ +import { useQuery } from "react-query" +import productSearchApi from "../api/productSearchApi" +import _ from "lodash-contrib" + +const useProductSearch = ({ query }) => { + const queryString = _.toQuery(query) + const fetchProductSearch = async () => await productSearchApi({ query: queryString }) + const { data, isLoading } = useQuery(`productSearch-${queryString}`, fetchProductSearch) + + return { + productSearch: { data, isLoading } + } +} + +export default useProductSearch \ No newline at end of file diff --git a/src/lib/product/hooks/useProductSimilar.js b/src/lib/product/hooks/useProductSimilar.js new file mode 100644 index 00000000..444fec0b --- /dev/null +++ b/src/lib/product/hooks/useProductSimilar.js @@ -0,0 +1,13 @@ +import productSimilarApi from "../api/productSimilarApi" +import { useQuery } from "react-query" + +const useProductSimilar = ({ query }) => { + const fetchProductSimilar = async () => await productSimilarApi({ query }) + const { data, isLoading } = useQuery(`productSimilar-${query}`, fetchProductSimilar) + + return { + productSimilar: { data, isLoading } + } +} + +export default useProductSimilar \ No newline at end of file -- cgit v1.2.3 From 69f55de26319e570ce0a8c4dbe8a29cb0d0b51c5 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Sat, 18 Feb 2023 22:03:55 +0700 Subject: optimization --- src/lib/product/components/Product.jsx | 2 +- src/lib/product/components/ProductCard.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/lib') diff --git a/src/lib/product/components/Product.jsx b/src/lib/product/components/Product.jsx index 2a3624e7..299cdc77 100644 --- a/src/lib/product/components/Product.jsx +++ b/src/lib/product/components/Product.jsx @@ -87,7 +87,7 @@ const Product = ({ product }) => { {product.name}
diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index 86ac3a64..9eb00ae0 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -14,7 +14,7 @@ const ProductCard = ({ product, simpleTitle }) => { {product?.name} { product.variantTotal > 1 && (
{ product.variantTotal } Varian
-- cgit v1.2.3 From 347e28848fb063ffcb7f9268384409e57b7ef3ad Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Sat, 18 Feb 2023 22:08:07 +0700 Subject: change animation --- src/lib/brand/components/Brand.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/lib') diff --git a/src/lib/brand/components/Brand.jsx b/src/lib/brand/components/Brand.jsx index a42f4c81..c1215976 100644 --- a/src/lib/brand/components/Brand.jsx +++ b/src/lib/brand/components/Brand.jsx @@ -23,7 +23,7 @@ const Brand = ({ id }) => { return ( <> -
+
{ brand.isLoading && } { brand.data && ( <> @@ -44,7 +44,7 @@ const Brand = ({ id }) => { )) } -
+
Produk dari brand:
{ brand?.data?.logo && ( Date: Sat, 18 Feb 2023 22:18:46 +0700 Subject: change animation --- src/lib/product/components/Product.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/product/components/Product.jsx b/src/lib/product/components/Product.jsx index 299cdc77..396eba5f 100644 --- a/src/lib/product/components/Product.jsx +++ b/src/lib/product/components/Product.jsx @@ -48,7 +48,7 @@ const Product = ({ product }) => { if (selectedVariant) { const variant = product.variants.find(variant => variant.id == selectedVariant.value) const variantAttributes = variant.attributes.length > 0 ? ' - ' + variant.attributes.join(', ') : '' - console.log(variant); + setActiveVariant({ id: variant.id, code: variant.code, -- cgit v1.2.3 From d22df6bd30b8ed4bcfa938dcbbedc5fc376c2304 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 20 Feb 2023 10:49:35 +0700 Subject: cart refactor --- src/lib/cart/components/Cart.jsx | 200 +++++++++++++++++++++++++++++++-- src/lib/cart/hooks/useCart.js | 4 +- src/lib/product/components/Product.jsx | 8 +- 3 files changed, 196 insertions(+), 16 deletions(-) (limited to 'src/lib') diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index 5f9ae1c0..3dd54429 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -1,28 +1,208 @@ import Link from "@/core/components/elements/Link/Link" import useCart from "../hooks/useCart" import Image from "@/core/components/elements/Image/Image" +import currencyFormat from "@/core/utils/currencyFormat" +import { useEffect, useState } from "react" +import { getItemCart, updateItemCart } from "@/core/utils/cart" +import { CheckIcon, RectangleGroupIcon } from "@heroicons/react/24/outline" +import { createSlug } from "@/core/utils/slug" +import { useRouter } from "next/router" const Cart = () => { - const { cart } = useCart() + const router = useRouter() + const [ products, setProducts ] = useState(null) + const { cart } = useCart({ enabled: !products }) + + const [ totalPriceBeforeTax, setTotalPriceBeforeTax ] = useState(0) + const [ totalTaxAmount, setTotalTaxAmount ] = useState(0) + const [ totalDiscountAmount, setTotalDiscountAmount ] = useState(0) + + useEffect(() => { + if (cart.data && !products) { + const productsWithQuantity = cart.data.map((product) => { + const productInCart = getItemCart({ productId: product.id }) + if (!productInCart) return + return { + ...product, + quantity: productInCart.quantity, + selected: productInCart.selected + } + }) + setProducts(productsWithQuantity) + } + }, [cart, products]) + + useEffect(() => { + if (!products) return + + let calculateTotalPriceBeforeTax = 0 + let calculateTotalTaxAmount = 0 + let calculateTotalDiscountAmount = 0 + for (const product of products) { + if (product.quantity == '') continue + updateItemCart({ + productId: product.id, + quantity: product.quantity, + selected: product.selected + }) + + if (!product.selected) continue + let priceBeforeTax = product.price.price / 1.11 + calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity + calculateTotalTaxAmount += (product.price.price - priceBeforeTax) * product.quantity + calculateTotalDiscountAmount += (product.price.price - product.price.priceDiscount) * product.quantity + } + setTotalPriceBeforeTax(calculateTotalPriceBeforeTax) + setTotalTaxAmount(calculateTotalTaxAmount) + setTotalDiscountAmount(calculateTotalDiscountAmount) + }, [products]) + + const updateQuantity = (value, productId, operation = '') => { + let productIndex = products.findIndex((product) => product.id == productId) + if (productIndex < 0) return + + let productsToUpdate = products + let quantity = productsToUpdate[productIndex].quantity + if (value != '' && isNaN(parseInt(value))) return + value = value != '' ? parseInt(value) : '' + switch (operation) { + case 'PLUS': + quantity += value + break + case 'MINUS': + if ((quantity - value) < 1) return + quantity -= value + break + case 'BLUR': + if (value != '') return + quantity = 1 + break + default: + quantity = value + break + } + productsToUpdate[productIndex].quantity = quantity + setProducts([ ...productsToUpdate ]) + } + + const toggleSelected = (productId) => { + let productIndex = products.findIndex((product) => product.id == productId) + if (productIndex < 0) return + + let productsToUpdate = products + productsToUpdate[productIndex].selected = !productsToUpdate[productIndex].selected + setProducts([ ...productsToUpdate ]) + } + + const selectedProduct = () => { + if (!products) return [] + return products.filter((product) => product.selected == true) + } return ( -
-
+
+

Daftar Produk Belanja

Cari Produk Lain
-
- { cart.data?.map((product) => ( +
+ { products?.map((product) => (
-
- {product?.name} -
-
-
{ product?.parent?.name }
+ + + {product?.name} + +
+ + { product?.parent?.name } + +
{ product.code }
+ { product?.price?.discountPercentage > 0 && ( +
+
+ {currencyFormat(product?.price?.price)} +
+
+ {product?.price?.discountPercentage}% +
+
+ ) } +
+ { currencyFormat(product?.price?.priceDiscount) } +
+
+
+ { currencyFormat(product?.price?.priceDiscount * product.quantity) } +
+
+ + updateQuantity(e.target.value, product.id)} + onBlur={(e) => updateQuantity(e.target.value, product.id, 'BLUR')} + /> + +
+
)) }
+
+
+
+ Total: { currencyFormat(totalPriceBeforeTax - totalDiscountAmount + totalTaxAmount) } +
+
+
+ + +
+
) } diff --git a/src/lib/cart/hooks/useCart.js b/src/lib/cart/hooks/useCart.js index 44931b8a..9eb01e74 100644 --- a/src/lib/cart/hooks/useCart.js +++ b/src/lib/cart/hooks/useCart.js @@ -3,11 +3,11 @@ import { useQuery } from "react-query" import _ from "lodash" import CartApi from "../api/CartApi" -const useCart = () => { +const useCart = ({ enabled }) => { const cart = getCart() const variantIds = _.keys(cart).join(',') const fetchCart = async () => CartApi({ variantIds }) - const { data, isLoading } = useQuery('cart', fetchCart) + const { data, isLoading } = useQuery('cart', fetchCart, { enabled }) return { cart: { data, isLoading } diff --git a/src/lib/product/components/Product.jsx b/src/lib/product/components/Product.jsx index 396eba5f..39d29d4d 100644 --- a/src/lib/product/components/Product.jsx +++ b/src/lib/product/components/Product.jsx @@ -8,7 +8,7 @@ import Select from "react-select" import ProductSimilar from "./ProductSimilar" import LazyLoad from "react-lazy-load" import { toast } from "react-hot-toast" -import { addItemCart } from "@/core/utils/cart" +import { updateItemCart } from "@/core/utils/cart" const informationTabOptions = [ { value: 'specification', label: 'Spesifikasi' }, @@ -75,7 +75,7 @@ const Product = ({ product }) => { toast.error('Jumlah barang minimal 1') return } - addItemCart({ + updateItemCart({ productId: activeVariant.id, quantity }) @@ -98,7 +98,7 @@ const Product = ({ product }) => { { activeVariant?.price?.discountPercentage > 0 && (
- {currencyFormat(activeVariant?.price?.priceDiscount)} + {currencyFormat(activeVariant?.price?.price)}
{activeVariant?.price?.discountPercentage}% @@ -106,7 +106,7 @@ const Product = ({ product }) => {
) }

- { activeVariant?.price?.price > 0 ? currencyFormat(activeVariant?.price?.price) : ( + { activeVariant?.price?.priceDiscount > 0 ? currencyFormat(activeVariant?.price?.priceDiscount) : ( Hubungi kami untuk dapatkan harga terbaik,  klik disini -- cgit v1.2.3 From 525166e62b793b351d1a6be2421c0a33b22f7b7b Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 20 Feb 2023 11:35:25 +0700 Subject: cart refactor --- src/lib/cart/components/Cart.jsx | 77 +++++++++++++++++++++++++++------- src/lib/product/components/Product.jsx | 2 +- 2 files changed, 64 insertions(+), 15 deletions(-) (limited to 'src/lib') diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index 3dd54429..69b4ded4 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -3,10 +3,12 @@ import useCart from "../hooks/useCart" import Image from "@/core/components/elements/Image/Image" import currencyFormat from "@/core/utils/currencyFormat" import { useEffect, useState } from "react" -import { getItemCart, updateItemCart } from "@/core/utils/cart" -import { CheckIcon, RectangleGroupIcon } from "@heroicons/react/24/outline" +import { deleteItemCart, getItemCart, updateItemCart } from "@/core/utils/cart" +import { CheckIcon, RectangleGroupIcon, TrashIcon } from "@heroicons/react/24/outline" import { createSlug } from "@/core/utils/slug" import { useRouter } from "next/router" +import BottomPopup from "@/core/components/elements/Popup/BottomPopup" +import { toast } from "react-hot-toast" const Cart = () => { const router = useRouter() @@ -17,6 +19,8 @@ const Cart = () => { const [ totalTaxAmount, setTotalTaxAmount ] = useState(0) const [ totalDiscountAmount, setTotalDiscountAmount ] = useState(0) + const [ deleteConfirmation, setDeleteConfirmation ] = useState(null) + useEffect(() => { if (cart.data && !products) { const productsWithQuantity = cart.data.map((product) => { @@ -98,6 +102,14 @@ const Cart = () => { if (!products) return [] return products.filter((product) => product.selected == true) } + + const deleteProduct = (productId) => { + const productsToUpdate = products.filter((product) => product.id != productId) + deleteItemCart({ productId }) + setDeleteConfirmation(null) + setProducts([...productsToUpdate]) + toast.success('Berhasil menghapus barang dari keranjang') + } return (
@@ -105,35 +117,38 @@ const Cart = () => {

Daftar Produk Belanja

Cari Produk Lain
+
{ products?.map((product) => ( -
+
{product?.name}
{ product?.parent?.name } -
{ product.code }
+
+ { product?.code } {product?.attributes.length > 0 ? `| ${product?.attributes.join(', ')}` : ''} +
{ product?.price?.discountPercentage > 0 && (
@@ -149,35 +164,43 @@ const Cart = () => {
- { currencyFormat(product?.price?.priceDiscount * product.quantity) } + { currencyFormat(product?.price?.priceDiscount * product?.quantity) }
updateQuantity(e.target.value, product.id)} - onBlur={(e) => updateQuantity(e.target.value, product.id, 'BLUR')} + type="number" + value={product?.quantity} + onChange={(e) => updateQuantity(e.target.value, product?.id)} + onBlur={(e) => updateQuantity(e.target.value, product?.id, 'BLUR')} /> +
)) }
+
@@ -203,6 +226,32 @@ const Cart = () => {
+ + setDeleteConfirmation(null)} + title="Hapus dari Keranjang" + > +
+ Apakah anda yakin menghapus barang {deleteConfirmation?.name} dari keranjang? +
+
+ + +
+
) } diff --git a/src/lib/product/components/Product.jsx b/src/lib/product/components/Product.jsx index 39d29d4d..92f4e37d 100644 --- a/src/lib/product/components/Product.jsx +++ b/src/lib/product/components/Product.jsx @@ -245,7 +245,7 @@ const Product = ({ product }) => { } const TabButton = ({ children, active, ...props }) => { - const activeClassName = active ? 'text-red_r-11 border-b border-red_r-11' : 'text-gray_r-11' + const activeClassName = active ? 'text-red_r-11 underline underline-offset-4' : 'text-gray_r-11' return (
-- cgit v1.2.3 From fdefe7cfe899125a9bd553b542976eed0de919c1 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 20 Feb 2023 13:09:22 +0700 Subject: fix --- src/lib/cart/components/Cart.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/lib') diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index 0b6ea768..4b9dd41f 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -166,11 +166,12 @@ const Cart = () => {
{ currencyFormat(product?.price?.priceDiscount * product?.quantity) }
-
+
@@ -189,7 +190,7 @@ const Cart = () => { + + +

Belum punya akun Indoteknik? Daftar

+
+ ) +} + +export default Login \ No newline at end of file diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index 4b9dd41f..bb1f21f6 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -9,6 +9,7 @@ import { createSlug } from "@/core/utils/slug" import { useRouter } from "next/router" import BottomPopup from "@/core/components/elements/Popup/BottomPopup" import { toast } from "react-hot-toast" +import Spinner from "@/core/components/elements/Spinner/Spinner" const Cart = () => { const router = useRouter() @@ -119,6 +120,11 @@ const Cart = () => {
+ { cart.isLoading && ( +
+ +
+ ) } { products?.map((product) => (
+ + +
+ + + setToDelete(null)} + title="Batalkan Transaksi" + > +
+ Apakah anda yakin membatalkan transaksi {toDelete?.name}? +
+
+ + +
+
+
+ ) +} + +export default Transactions \ No newline at end of file diff --git a/src/lib/transaction/hooks/useTransactions.js b/src/lib/transaction/hooks/useTransactions.js new file mode 100644 index 00000000..b5742cae --- /dev/null +++ b/src/lib/transaction/hooks/useTransactions.js @@ -0,0 +1,15 @@ +import { useQuery } from "react-query" +import transactionsApi from "../api/transactionsApi" +import _ from "lodash-contrib" + +const useTransactions = ({ partnerId, query }) => { + const queryString = _.toQuery(query) + const fetchTransactions = async () => await transactionsApi({ partnerId, query: queryString }) + const { data, isLoading, refetch } = useQuery(`transactions-${queryString}`, fetchTransactions) + + return { + transactions: { data, isLoading, refetch } + } +} + +export default useTransactions \ No newline at end of file diff --git a/src/lib/transaction/utils/transactions.js b/src/lib/transaction/utils/transactions.js new file mode 100644 index 00000000..166e8a7e --- /dev/null +++ b/src/lib/transaction/utils/transactions.js @@ -0,0 +1,14 @@ +const downloadPurchaseOrder = (partnerId, transaction) => { + const url = `${process.env.ODOO_HOST}/api/v1/partner/${partnerId}/sale_order/${transaction.id}/download_po/${transaction.token}` + window.open(url, 'download') +} + +const downloadQuotation = (partnerId, transaction) => { + const url = `${process.env.ODOO_HOST}/api/v1/partner/${partnerId}/sale_order/${transaction.id}/download/${transaction.token}` + window.open(url, 'download') +} + +export { + downloadPurchaseOrder, + downloadQuotation +} \ No newline at end of file -- cgit v1.2.3 From e9409874e44c5bdc7d60f6cdb8910d7bc0a12318 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 20 Feb 2023 17:24:02 +0700 Subject: fix --- src/lib/transaction/api/cancelTransactionApi.js | 6 ++- src/lib/transaction/api/transactionsApi.js | 6 ++- src/lib/transaction/components/Transactions.jsx | 59 ++++++++++++++++++++----- src/lib/transaction/hooks/useTransactions.js | 4 +- 4 files changed, 58 insertions(+), 17 deletions(-) (limited to 'src/lib') diff --git a/src/lib/transaction/api/cancelTransactionApi.js b/src/lib/transaction/api/cancelTransactionApi.js index 19891b5a..cd1798b5 100644 --- a/src/lib/transaction/api/cancelTransactionApi.js +++ b/src/lib/transaction/api/cancelTransactionApi.js @@ -1,7 +1,9 @@ import odooApi from "@/core/api/odooApi" +import { getAuth } from "@/core/utils/auth" -const cancelTransactionApi = async ({ partnerId, transaction }) => { - const dataCancelTransaction = await odooApi('POST', `/api/v1/partner/${partnerId}/sale_order/${transaction.id}/cancel`) +const cancelTransactionApi = async ({ transaction }) => { + const auth = getAuth() + const dataCancelTransaction = await odooApi('POST', `/api/v1/partner/${auth.partnerId}/sale_order/${transaction.id}/cancel`) return dataCancelTransaction } diff --git a/src/lib/transaction/api/transactionsApi.js b/src/lib/transaction/api/transactionsApi.js index d36c3664..995b4c5c 100644 --- a/src/lib/transaction/api/transactionsApi.js +++ b/src/lib/transaction/api/transactionsApi.js @@ -1,7 +1,9 @@ import odooApi from "@/core/api/odooApi" +import { getAuth } from "@/core/utils/auth" -const transactionsApi = async ({partnerId, query}) => { - const dataTransactions = await odooApi('GET', `/api/v1/partner/${partnerId}/sale_order?${query}`) +const transactionsApi = async ({ query }) => { + const auth = getAuth() + const dataTransactions = await odooApi('GET', `/api/v1/partner/${auth.partnerId}/sale_order?${query}`) return dataTransactions } diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index 97eb5f91..45a6445c 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -1,8 +1,7 @@ import { useRouter } from "next/router" -import { getAuth } from "@/core/utils/auth" import { useState } from "react" import { toast } from "react-hot-toast" -import { EllipsisVerticalIcon } from "@heroicons/react/24/outline" +import { EllipsisVerticalIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline" import { downloadPurchaseOrder, downloadQuotation } from "../utils/transactions" import useTransactions from "../hooks/useTransactions" @@ -12,23 +11,28 @@ import TransactionStatusBadge from "./TransactionStatusBadge" import Spinner from "@/core/components/elements/Spinner/Spinner" import Link from "@/core/components/elements/Link/Link" import BottomPopup from "@/core/components/elements/Popup/BottomPopup" +import Pagination from "@/core/components/elements/Pagination/Pagination" +import { toQuery } from "lodash-contrib" +import _ from "lodash" const Transactions = () => { - const auth = getAuth() const router = useRouter() const { q = '', page = 1 } = router.query - const { transactions } = useTransactions({ - partnerId: auth?.partnerId, - query: { - name: q, - limit: 30, - offset: (page - 1) * 30 - } - }) + const limit = 10 + + const query = { + name: q, + offset: (page - 1) * limit, + limit + } + + const [ inputQuery, setInputQuery ] = useState(q) + + const { transactions } = useTransactions({ query }) const [ toOthers, setToOthers ] = useState(null) const [ toDelete, setToDelete ] = useState(null) @@ -45,9 +49,35 @@ const Transactions = () => { setToDelete(null) } + const pageCount = Math.ceil(transactions?.data?.saleOrderTotal / limit) + let pageQuery = _.omit(query, ['limit', 'offset']) + pageQuery = _.pickBy(pageQuery, _.identity) + pageQuery = toQuery(pageQuery) + + const handleSubmit = (e) => { + e.preventDefault() + if (!inputQuery) return + router.push(`/my/transactions?q=${inputQuery}`) + } + return (
+
+ setInputQuery(e.target.value)} + /> + +
{ transactions.isLoading && (
@@ -89,6 +119,13 @@ const Transactions = () => {
)) } + +
setToOthers(null)}> diff --git a/src/lib/transaction/hooks/useTransactions.js b/src/lib/transaction/hooks/useTransactions.js index b5742cae..3f67fd3e 100644 --- a/src/lib/transaction/hooks/useTransactions.js +++ b/src/lib/transaction/hooks/useTransactions.js @@ -2,9 +2,9 @@ import { useQuery } from "react-query" import transactionsApi from "../api/transactionsApi" import _ from "lodash-contrib" -const useTransactions = ({ partnerId, query }) => { +const useTransactions = ({ query }) => { const queryString = _.toQuery(query) - const fetchTransactions = async () => await transactionsApi({ partnerId, query: queryString }) + const fetchTransactions = async () => await transactionsApi({ query: queryString }) const { data, isLoading, refetch } = useQuery(`transactions-${queryString}`, fetchTransactions) return { -- cgit v1.2.3 From 4fd738fd54f81fa53c2b3e78b7a80fbfda250352 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 21 Feb 2023 10:19:32 +0700 Subject: fix --- src/lib/auth/components/Login.jsx | 2 +- src/lib/transaction/components/Transactions.jsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src/lib') diff --git a/src/lib/auth/components/Login.jsx b/src/lib/auth/components/Login.jsx index e598fe48..acb6e8c3 100644 --- a/src/lib/auth/components/Login.jsx +++ b/src/lib/auth/components/Login.jsx @@ -64,7 +64,7 @@ const Login = () => {
{ const handleSubmit = (e) => { e.preventDefault() - if (!inputQuery) return router.push(`/my/transactions?q=${inputQuery}`) } -- cgit v1.2.3 From fdfb47c3a825258b871ac5921605642e5e05fdd8 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 21 Feb 2023 12:04:20 +0700 Subject: fix --- src/lib/transaction/api/transactionApi.js | 10 ++ src/lib/transaction/api/uploadPoApi.js | 10 ++ src/lib/transaction/components/Transaction.jsx | 149 ++++++++++++++++++++++++ src/lib/transaction/components/Transactions.jsx | 32 ++--- src/lib/transaction/hooks/useTransaction.js | 13 +++ src/lib/transaction/utils/transactions.js | 12 +- src/lib/variant/components/VariantCard.jsx | 97 +++++++++++++++ src/lib/variant/components/VariantGroupCard.jsx | 33 ++++++ 8 files changed, 339 insertions(+), 17 deletions(-) create mode 100644 src/lib/transaction/api/transactionApi.js create mode 100644 src/lib/transaction/api/uploadPoApi.js create mode 100644 src/lib/transaction/components/Transaction.jsx create mode 100644 src/lib/transaction/hooks/useTransaction.js create mode 100644 src/lib/variant/components/VariantCard.jsx create mode 100644 src/lib/variant/components/VariantGroupCard.jsx (limited to 'src/lib') diff --git a/src/lib/transaction/api/transactionApi.js b/src/lib/transaction/api/transactionApi.js new file mode 100644 index 00000000..7186f847 --- /dev/null +++ b/src/lib/transaction/api/transactionApi.js @@ -0,0 +1,10 @@ +import odooApi from "@/core/api/odooApi" +import { getAuth } from "@/core/utils/auth" + +const transactionApi = async ({ id }) => { + const auth = getAuth() + const dataTransaction = await odooApi('GET', `/api/v1/partner/${auth.partnerId}/sale_order/${id}`) + return dataTransaction +} + +export default transactionApi \ No newline at end of file diff --git a/src/lib/transaction/api/uploadPoApi.js b/src/lib/transaction/api/uploadPoApi.js new file mode 100644 index 00000000..00ad1d8d --- /dev/null +++ b/src/lib/transaction/api/uploadPoApi.js @@ -0,0 +1,10 @@ +import odooApi from "@/core/api/odooApi" +import { getAuth } from "@/core/utils/auth" + +const uploadPoApi = async ({ id, data }) => { + const auth = getAuth() + const dataUploadPo = await odooApi('POST', `/api/v1/partner/${auth.partnerId}/sale_order/${id}/upload_po`, data) + return dataUploadPo +} + +export default uploadPoApi \ No newline at end of file diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx new file mode 100644 index 00000000..c9bdf715 --- /dev/null +++ b/src/lib/transaction/components/Transaction.jsx @@ -0,0 +1,149 @@ +import Spinner from "@/core/components/elements/Spinner/Spinner" +import useTransaction from "../hooks/useTransaction" +import TransactionStatusBadge from "./TransactionStatusBadge" +import Divider from "@/core/components/elements/Divider/Divider" +import { useRef, useState } from "react" +import { downloadPurchaseOrder } from "../utils/transactions" +import BottomPopup from "@/core/components/elements/Popup/BottomPopup" +import uploadPoApi from "../api/uploadPoApi" +import { toast } from "react-hot-toast" +import getFileBase64 from "@/core/utils/getFileBase64" +import currencyFormat from "@/core/utils/currencyFormat" +import VariantGroupCard from "@/lib/variant/components/VariantGroupCard" + +const Transaction = ({ id }) => { + const { transaction } = useTransaction({ id }) + + const poNumber = useRef('') + const poFile = useRef('') + const [ uploadPo, setUploadPo ] = useState(false) + const openUploadPo = () => setUploadPo(true) + const closeUploadPo = () => setUploadPo(false) + const submitUploadPo = async () => { + const file = poFile.current.files[0] + const name = poNumber.current.value + if (typeof file === 'undefined' || !name) { + toast.error('Nomor dan Dokumen PO harus diisi', { position: 'bottom-center' }) + return + } + if (file.size > 5000000) { + toast.error('Maksimal ukuran file adalah 5MB', { position: 'bottom-center' }) + return + } + const data = { name, file: await getFileBase64(file) } + const isUploaded = await uploadPoApi({ id, data }) + if (isUploaded) { + toast.success('Berhasil upload PO') + transaction.refetch() + closeUploadPo() + return + } + toast.error('Terjadi kesalahan internal, coba lagi nanti atau hubungi kami') + } + + return ( + <> + { transaction.isLoading && ( +
+ +
+ ) } + + { transaction.data?.name && ( + <> +
+ +
+ +
+
+ + { transaction.data?.name } + + + { transaction.data?.paymentTerm } + + + { transaction.data?.sales } + + + { transaction.data?.dateOrder } + +
+ + + +
+ + { transaction.data?.purchaseOrderName || '-' } + +
+

Dokumen PO

+ +
+
+ + + +
Detail Produk
+ +
+ +
+

Total Belanja

+

{ currencyFormat(transaction.data?.amountTotal) }

+
+
+ + + + +
+ + +
+
+ + +
+
+ + +
+
+ + )} + + + ) +} + +const DescriptionRow = ({ children, label }) => ( +
+ { label } + { children } +
+) + +export default Transaction \ No newline at end of file diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index 5eb1d947..246a4a2c 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -14,6 +14,7 @@ import BottomPopup from "@/core/components/elements/Popup/BottomPopup" import Pagination from "@/core/components/elements/Pagination/Pagination" import { toQuery } from "lodash-contrib" import _ from "lodash" +import Alert from "@/core/components/elements/Alert/Alert" const Transactions = () => { const router = useRouter() @@ -29,24 +30,22 @@ const Transactions = () => { offset: (page - 1) * limit, limit } - - const [ inputQuery, setInputQuery ] = useState(q) - const { transactions } = useTransactions({ query }) + const [ inputQuery, setInputQuery ] = useState(q) const [ toOthers, setToOthers ] = useState(null) - const [ toDelete, setToDelete ] = useState(null) + const [ toCancel, setToCancel ] = useState(null) const submitCancelTransaction = async () => { const isCancelled = await cancelTransactionApi({ partnerId: auth.partnerId, - transaction: toDelete + transaction: toCancel }) if (isCancelled) { toast.success('Berhasil batalkan transaksi') transactions.refetch() } - setToDelete(null) + setToCancel(null) } const pageCount = Math.ceil(transactions?.data?.saleOrderTotal / limit) @@ -82,6 +81,13 @@ const Transactions = () => {
) } + + { !transactions.isLoading && transactions.data?.saleOrders?.length === 0 && ( + + Tidak ada data transaksi + + ) } + { transactions.data?.saleOrders?.map((saleOrder, index) => (
@@ -132,21 +138,21 @@ const Transactions = () => { @@ -154,12 +160,12 @@ const Transactions = () => { setToDelete(null)} + active={toCancel} + close={() => setToCancel(null)} title="Batalkan Transaksi" >
- Apakah anda yakin membatalkan transaksi {toDelete?.name}? + Apakah anda yakin membatalkan transaksi {toCancel?.name}?
diff --git a/src/lib/transaction/hooks/useTransaction.js b/src/lib/transaction/hooks/useTransaction.js new file mode 100644 index 00000000..f2b493ee --- /dev/null +++ b/src/lib/transaction/hooks/useTransaction.js @@ -0,0 +1,13 @@ +import { useQuery } from "react-query" +import transactionApi from "../api/transactionApi" + +const useTransaction = ({ id }) => { + const fetchTransaction = async () => await transactionApi({ id }) + const { data, isLoading, refetch } = useQuery(`transaction-${id}`, fetchTransaction) + + return { + transaction: { data, isLoading, refetch } + } +} + +export default useTransaction \ No newline at end of file diff --git a/src/lib/transaction/utils/transactions.js b/src/lib/transaction/utils/transactions.js index 166e8a7e..03d4dbd4 100644 --- a/src/lib/transaction/utils/transactions.js +++ b/src/lib/transaction/utils/transactions.js @@ -1,10 +1,14 @@ -const downloadPurchaseOrder = (partnerId, transaction) => { - const url = `${process.env.ODOO_HOST}/api/v1/partner/${partnerId}/sale_order/${transaction.id}/download_po/${transaction.token}` +import { getAuth } from "@/core/utils/auth" + +const downloadPurchaseOrder = (transaction) => { + const auth = getAuth() + const url = `${process.env.ODOO_HOST}/api/v1/partner/${auth.partnerId}/sale_order/${transaction.id}/download_po/${transaction.token}` window.open(url, 'download') } -const downloadQuotation = (partnerId, transaction) => { - const url = `${process.env.ODOO_HOST}/api/v1/partner/${partnerId}/sale_order/${transaction.id}/download/${transaction.token}` +const downloadQuotation = (transaction) => { + const auth = getAuth() + const url = `${process.env.ODOO_HOST}/api/v1/partner/${auth.partnerId}/sale_order/${transaction.id}/download/${transaction.token}` window.open(url, 'download') } diff --git a/src/lib/variant/components/VariantCard.jsx b/src/lib/variant/components/VariantCard.jsx new file mode 100644 index 00000000..6c7ab22f --- /dev/null +++ b/src/lib/variant/components/VariantCard.jsx @@ -0,0 +1,97 @@ +import { useRouter } from "next/router" +import { toast } from "react-hot-toast" + +import Image from "@/core/components/elements/Image/Image" +import Link from "@/core/components/elements/Link/Link" +import { createSlug } from "@/core/utils/slug" +import currencyFormat from "@/core/utils/currencyFormat" +import { updateItemCart } from "@/core/utils/cart" + +const VariantCard = ({ + product, + openOnClick = true, + buyMore = false +}) => { + const router = useRouter() + + const addItemToCart = () => { + toast.success('Berhasil menambahkan ke keranjang', { duration: 1500 }) + updateItemCart({ + productId: product.id, + quantity: 1 + }) + return + } + + const checkoutItem = () => { + router.push(`/shop/checkout?product_id=${product.id}&qty=${product.quantity}`) + } + + const Card = () => ( +
+
+ {product.parent.name} +
+
+

+ {product.parent.name} +

+

+ {product.code || '-'} + {product.attributes.length > 0 ? ` ・ ${product.attributes.join(', ')}` : ''} +

+
+ {product.price.discountPercentage > 0 && ( + <> +

{currencyFormat(product.price.price)}

+ {product.price.discountPercentage}% + + )} +

{currencyFormat(product.price.priceDiscount)}

+
+

+ {currencyFormat(product.price.priceDiscount)} × {product.quantity} Barang +

+

+ {currencyFormat(product.quantity * product.price.priceDiscount)} +

+
+
+ ) + + if (openOnClick) { + return ( + <> + + + + { buyMore && ( +
+ + +
+ ) } + + ) + } + + return +} + +export default VariantCard \ No newline at end of file diff --git a/src/lib/variant/components/VariantGroupCard.jsx b/src/lib/variant/components/VariantGroupCard.jsx new file mode 100644 index 00000000..fd4f9b4d --- /dev/null +++ b/src/lib/variant/components/VariantGroupCard.jsx @@ -0,0 +1,33 @@ +import { useState } from "react" +import VariantCard from "./VariantCard" + +const VariantGroupCard = ({ + variants, + ...props +}) => { + const [ showAll, setShowAll ] = useState(false) + const variantsToShow = showAll ? variants : variants.slice(0, 2) + + return ( + <> + { variantsToShow?.map((variant, index) => ( + + )) } + { variants.length > 2 && ( + + ) } + + ) +} + +export default VariantGroupCard \ No newline at end of file -- cgit v1.2.3 From 3f2ff1475676ba47a841796e39e7d17d627e5356 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 21 Feb 2023 14:46:24 +0700 Subject: fix --- src/lib/cart/components/Cart.jsx | 6 +- src/lib/transaction/api/checkoutPoApi.js | 10 ++ src/lib/transaction/components/Transaction.jsx | 191 +++++++++++++++++++++++- src/lib/transaction/components/Transactions.jsx | 3 +- 4 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 src/lib/transaction/api/checkoutPoApi.js (limited to 'src/lib') diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index bb1f21f6..e7606582 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -141,7 +141,7 @@ const Cart = () => { {product?.name} @@ -182,7 +182,7 @@ const Cart = () => { - updateQuantity(e.target.value, product?.id)} @@ -213,7 +213,7 @@ const Cart = () => {
Total: - { selectedProduct().length > 0 ? currencyFormat(totalPriceBeforeTax - totalDiscountAmount + totalTaxAmount) : ' - ' } +  { selectedProduct().length > 0 ? currencyFormat(totalPriceBeforeTax - totalDiscountAmount + totalTaxAmount) : '-' }
diff --git a/src/lib/transaction/api/checkoutPoApi.js b/src/lib/transaction/api/checkoutPoApi.js new file mode 100644 index 00000000..aed43cff --- /dev/null +++ b/src/lib/transaction/api/checkoutPoApi.js @@ -0,0 +1,10 @@ +import odooApi from "@/core/api/odooApi" +import { getAuth } from "@/core/utils/auth" + +const checkoutPoApi = async ({ id }) => { + const auth = getAuth() + const dataCheckout = await odooApi('POST', `/api/v1/partner/${auth?.partnerId}/sale_order/${id}/checkout`) + return dataCheckout +} + +export default checkoutPoApi \ No newline at end of file diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index c9bdf715..143b24bb 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -3,13 +3,18 @@ import useTransaction from "../hooks/useTransaction" import TransactionStatusBadge from "./TransactionStatusBadge" import Divider from "@/core/components/elements/Divider/Divider" import { useRef, useState } from "react" -import { downloadPurchaseOrder } from "../utils/transactions" +import { downloadPurchaseOrder, downloadQuotation } from "../utils/transactions" import BottomPopup from "@/core/components/elements/Popup/BottomPopup" import uploadPoApi from "../api/uploadPoApi" import { toast } from "react-hot-toast" import getFileBase64 from "@/core/utils/getFileBase64" import currencyFormat from "@/core/utils/currencyFormat" import VariantGroupCard from "@/lib/variant/components/VariantGroupCard" +import { ChevronDownIcon, ChevronRightIcon, ChevronUpIcon } from "@heroicons/react/24/outline" +import Link from "@/core/components/elements/Link/Link" +import Alert from "@/core/components/elements/Alert/Alert" +import checkoutPoApi from "../api/checkoutPoApi" +import cancelTransactionApi from "../api/cancelTransactionApi" const Transaction = ({ id }) => { const { transaction } = useTransaction({ id }) @@ -41,6 +46,37 @@ const Transaction = ({ id }) => { toast.error('Terjadi kesalahan internal, coba lagi nanti atau hubungi kami') } + const [ section, setSection ] = useState({ + customer: false, + invoice: false, + shipping: false + }) + const toggleSection = (name) => { + setSection({ ...section, [name]: !section[name] }) + } + + const [ cancelTransaction, setCancelTransaction ] = useState(false) + const openCancelTransaction = () => setCancelTransaction(true) + const closeCancelTransaction = () => setCancelTransaction(false) + const submitCancelTransaction = async () => { + const isCancelled = await cancelTransactionApi({ transaction: transaction.data }) + if (isCancelled) { + toast.success('Berhasil batalkan transaksi') + transaction.refetch() + } + closeCancelTransaction() + } + + const checkout = async () => { + if (!transaction.data?.purchaseOrderFile) { + toast.error('Mohon upload dokumen PO anda sebelum melanjutkan pesanan') + return + } + await checkoutPoApi({ id }) + toast.success('Berhasil melanjutkan pesanan') + transaction.refetch() + } + return ( <> { transaction.isLoading && ( @@ -105,6 +141,122 @@ const Transaction = ({ id }) => {
+ + toggleSection('customer')} + /> + + { section.customer && } + + + + toggleSection('shipping')} + /> + + { section.shipping && } + + + + toggleSection('invoice')} + /> + + { section.invoice && } + + + +
+

Invoice

+
+ { transaction.data?.invoices?.map((invoice, index) => ( + +
+
+

{ invoice?.name }

+
+ { invoice.amountResidual > 0 ? ( +
Belum Lunas
+ ) : ( +
Lunas
+ ) } +

+ { currencyFormat(invoice.amountTotal) } +

+
+
+ +
+ + )) } + { transaction.data?.invoices?.length === 0 && ( + + Belum ada Invoice + + ) } +
+
+ + + +
+ { transaction.data?.status == 'draft' && ( + + ) } + + { transaction.data?.status != 'draft' && ( + + ) } +
+ + +
+ Apakah anda yakin membatalkan transaksi {transaction.data?.name}? +
+
+ + +
+
{ ) } +const SectionButton = ({ label, active, toggle }) => ( + +) + +const SectionContent = ({ address }) => { + let fullAddress = [] + if (address?.street) fullAddress.push(address.street) + if (address?.sub_district?.name) fullAddress.push(address.sub_district.name) + if (address?.district?.name) fullAddress.push(address.district.name) + if (address?.city?.name) fullAddress.push(address.city.name) + fullAddress = fullAddress.join(', ') + + return ( +
+ + { address.name } + + + { address.email || '-' } + + + { address.mobile || '-' } + + + { fullAddress } + +
+ ) +} + const DescriptionRow = ({ children, label }) => (
{ label } diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index 246a4a2c..25a6076a 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -38,7 +38,6 @@ const Transactions = () => { const submitCancelTransaction = async () => { const isCancelled = await cancelTransactionApi({ - partnerId: auth.partnerId, transaction: toCancel }) if (isCancelled) { @@ -171,7 +170,7 @@ const Transactions = () => { -- cgit v1.2.3 From de7361718def0f6bb32294bb074841ba2c0a3ce6 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 21 Feb 2023 16:25:35 +0700 Subject: fix --- src/lib/invoice/api/invoiceApi.js | 10 + src/lib/invoice/api/invoicesApi.js | 10 + src/lib/invoice/components/Invoice.jsx | 126 ++++++++ src/lib/invoice/components/Invoices.jsx | 157 ++++++++++ src/lib/invoice/hooks/useInvoice.js | 13 + src/lib/invoice/hooks/useInvoices.js | 15 + src/lib/invoice/utils/invoices.js | 14 + src/lib/transaction/components/Transaction.jsx | 377 ++++++++++++------------ src/lib/transaction/components/Transactions.jsx | 133 +++++---- 9 files changed, 598 insertions(+), 257 deletions(-) create mode 100644 src/lib/invoice/api/invoiceApi.js create mode 100644 src/lib/invoice/api/invoicesApi.js create mode 100644 src/lib/invoice/components/Invoice.jsx create mode 100644 src/lib/invoice/components/Invoices.jsx create mode 100644 src/lib/invoice/hooks/useInvoice.js create mode 100644 src/lib/invoice/hooks/useInvoices.js create mode 100644 src/lib/invoice/utils/invoices.js (limited to 'src/lib') diff --git a/src/lib/invoice/api/invoiceApi.js b/src/lib/invoice/api/invoiceApi.js new file mode 100644 index 00000000..f9bacf8e --- /dev/null +++ b/src/lib/invoice/api/invoiceApi.js @@ -0,0 +1,10 @@ +import odooApi from "@/core/api/odooApi" +import { getAuth } from "@/core/utils/auth" + +const invoiceApi = async ({ id }) => { + const auth = getAuth() + const dataInvoice = await odooApi('GET', `/api/v1/partner/${auth.partnerId}/invoice/${id}`) + return dataInvoice +} + +export default invoiceApi \ No newline at end of file diff --git a/src/lib/invoice/api/invoicesApi.js b/src/lib/invoice/api/invoicesApi.js new file mode 100644 index 00000000..4e842f55 --- /dev/null +++ b/src/lib/invoice/api/invoicesApi.js @@ -0,0 +1,10 @@ +import odooApi from "@/core/api/odooApi" +import { getAuth } from "@/core/utils/auth" + +const invoicesApi = async ({ query }) => { + const auth = getAuth() + const dataInvoices = await odooApi('GET', `/api/v1/partner/${auth.partnerId}/invoice?${query}`) + return dataInvoices +} + +export default invoicesApi \ No newline at end of file diff --git a/src/lib/invoice/components/Invoice.jsx b/src/lib/invoice/components/Invoice.jsx new file mode 100644 index 00000000..de6eacca --- /dev/null +++ b/src/lib/invoice/components/Invoice.jsx @@ -0,0 +1,126 @@ +import Spinner from "@/core/components/elements/Spinner/Spinner" +import useInvoice from "../hooks/useInvoice" +import { downloadInvoice, downloadTaxInvoice } from "../utils/invoices" +import Divider from "@/core/components/elements/Divider/Divider" +import VariantGroupCard from "@/lib/variant/components/VariantGroupCard" +import currencyFormat from "@/core/utils/currencyFormat" + +const Invoice = ({ id }) => { + const { invoice } = useInvoice({ id }) + + if (invoice.isLoading) { + return ( +
+ +
+ ) + } + + const address = invoice.data?.customer + let fullAddress = [] + if (address?.street) fullAddress.push(address.street) + if (address?.subDistrict?.name) fullAddress.push(address.subDistrict.name) + if (address?.district?.name) fullAddress.push(address.district.name) + if (address?.city?.name) fullAddress.push(address.city.name) + fullAddress = fullAddress.join(', ') + + return invoice.data?.name && ( + <> +
+ + { invoice.data?.name } + + + { invoice.data?.amountResidual > 0 ? ( + Belum Lunas + ) : ( + Lunas + ) } + + + { invoice.data?.purchaseOrderName || '-' } + + + { invoice.data?.paymentTerm } + + { invoice.data?.amountResidual > 0 && invoice.invoiceDate != invoice.invoiceDateDue && ( + + { invoice.data?.invoiceDateDue } + + ) } + + { invoice.data?.sales } + + + { invoice.data?.invoiceDate } + +
+

Faktur Pembelian

+ +
+
+

Faktur Pajak

+ +
+
+ + + +
+ Detail Penagihan +
+ +
+ + { address?.name } + + + { address?.email || '-' } + + + { address?.mobile || '-' } + + + { fullAddress } + +
+ + + +
Detail Produk
+ +
+ +
+

Total Belanja

+

{ currencyFormat(invoice.data?.amountTotal) }

+
+
+ + ) +} + +const DescriptionRow = ({ children, label }) => ( +
+ { label } + { children } +
+) + +export default Invoice \ No newline at end of file diff --git a/src/lib/invoice/components/Invoices.jsx b/src/lib/invoice/components/Invoices.jsx new file mode 100644 index 00000000..3b1e71e3 --- /dev/null +++ b/src/lib/invoice/components/Invoices.jsx @@ -0,0 +1,157 @@ +import { CheckIcon, ClockIcon, EllipsisVerticalIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline" +import { toQuery } from "lodash-contrib" +import _ from "lodash" +import { useRouter } from "next/router" +import { useState } from "react" +import useInvoices from "../hooks/useInvoices" +import Spinner from "@/core/components/elements/Spinner/Spinner" +import Alert from "@/core/components/elements/Alert/Alert" +import Pagination from "@/core/components/elements/Pagination/Pagination" +import Link from "@/core/components/elements/Link/Link" +import currencyFormat from "@/core/utils/currencyFormat" +import BottomPopup from "@/core/components/elements/Popup/BottomPopup" +import { downloadInvoice, downloadTaxInvoice } from "../utils/invoices" + +const Invoices = () => { + const router = useRouter() + const { + q = '', + page = 1 + } = router.query + + const limit = 10 + + const query = { + name: q, + offset: (page - 1) * limit, + limit + } + const { invoices } = useInvoices({ query }) + + const [ inputQuery, setInputQuery ] = useState(q) + const [ toOthers, setToOthers ] = useState(null) + + const pageCount = Math.ceil(invoices?.data?.saleOrderTotal / limit) + let pageQuery = _.omit(query, ['limit', 'offset']) + pageQuery = _.pickBy(pageQuery, _.identity) + pageQuery = toQuery(pageQuery) + + const handleSubmit = (e) => { + e.preventDefault() + router.push(`/my/invoices?q=${inputQuery}`) + } + + return ( +
+
+ setInputQuery(e.target.value)} + /> + +
+ + { invoices.isLoading && ( +
+ +
+ ) } + + { !invoices.isLoading && invoices.data?.invoices?.length === 0 && ( + + Tidak ada data transaksi + + ) } + + { invoices.data?.invoices?.map((invoice, index) => ( +
+
+ + No. Invoice +

{ invoice.name }

+ +
+ { invoice.amountResidual > 0 ? ( +
Belum Lunas
+ ) : ( +
Lunas
+ ) } + setToOthers(invoice)} /> +
+
+ +
+

+ { invoice.invoiceDate } +

+

+ { invoice.paymentTerm } +

+
+
+
+
+ No. Purchase Order +

{ invoice.purchaseOrderName || '-' }

+
+
+ Total Invoice +

{ currencyFormat(invoice.amountTotal) }

+
+
+ + { invoice.efaktur ? ( +
+ + Faktur Pajak +
+ ) : ( +
+ + Faktur Pajak +
+ ) } +
+ )) } + + + + setToOthers(null)} + > +
+ + +
+
+
+ ) +} + +export default Invoices \ No newline at end of file diff --git a/src/lib/invoice/hooks/useInvoice.js b/src/lib/invoice/hooks/useInvoice.js new file mode 100644 index 00000000..0e612f2f --- /dev/null +++ b/src/lib/invoice/hooks/useInvoice.js @@ -0,0 +1,13 @@ +import { useQuery } from "react-query" +import invoiceApi from "../api/invoiceApi" + +const useInvoice = ({ id }) => { + const fetchInvoice = async () => await invoiceApi({ id }) + const { data, isLoading, refetch } = useQuery(`invoice-${id}`, fetchInvoice) + + return { + invoice: { data, isLoading, refetch } + } +} + +export default useInvoice \ No newline at end of file diff --git a/src/lib/invoice/hooks/useInvoices.js b/src/lib/invoice/hooks/useInvoices.js new file mode 100644 index 00000000..7bcdc952 --- /dev/null +++ b/src/lib/invoice/hooks/useInvoices.js @@ -0,0 +1,15 @@ +import { useQuery } from "react-query" +import invoicesApi from "../api/invoicesApi" +import _ from "lodash-contrib" + +const useInvoices = ({ query }) => { + const queryString = _.toQuery(query) + const fetchInvoices = async () => await invoicesApi({ query: queryString }) + const { data, isLoading, refetch } = useQuery(`invoices-${queryString}`, fetchInvoices) + + return { + invoices: { data, isLoading, refetch } + } +} + +export default useInvoices \ No newline at end of file diff --git a/src/lib/invoice/utils/invoices.js b/src/lib/invoice/utils/invoices.js new file mode 100644 index 00000000..c152191d --- /dev/null +++ b/src/lib/invoice/utils/invoices.js @@ -0,0 +1,14 @@ +const downloadInvoice = (invoice) => { + const url = `${process.env.ODOO_HOST}/api/v1/download/invoice/${invoice.id}/${invoice.token}` + window.open(url, 'download') +} + +const downloadTaxInvoice = (invoice) => { + const url = `${process.env.ODOO_HOST}/api/v1/download/tax-invoice/${invoice.id}/${invoice.token}` + window.open(url, 'download') +} + +export { + downloadInvoice, + downloadTaxInvoice +} \ No newline at end of file diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index 143b24bb..e049a9ac 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -77,216 +77,213 @@ const Transaction = ({ id }) => { transaction.refetch() } - return ( - <> - { transaction.isLoading && ( -
- -
- ) } + if (transaction.isLoading) { + return ( +
+ +
+ ) + } - { transaction.data?.name && ( - <> -
- -
- -
-
- - { transaction.data?.name } - - - { transaction.data?.paymentTerm } - - - { transaction.data?.sales } - - - { transaction.data?.dateOrder } - + return transaction.data?.name && ( + <> +
+ +
+
- - +
+ + { transaction.data?.name } + + + { transaction.data?.paymentTerm } + + + { transaction.data?.sales } + + + { transaction.data?.dateOrder } + +
+ + -
- - { transaction.data?.purchaseOrderName || '-' } - -
-

Dokumen PO

- -
-
+
+ + { transaction.data?.purchaseOrderName || '-' } + +
+

Dokumen PO

+ +
+
- + -
Detail Produk
+
Detail Produk
-
- -
-

Total Belanja

-

{ currencyFormat(transaction.data?.amountTotal) }

-
-
+
+ +
+

Total Belanja

+

{ currencyFormat(transaction.data?.amountTotal) }

+
+
- - - toggleSection('customer')} - /> + + + toggleSection('customer')} + /> - { section.customer && } + { section.customer && } - - - toggleSection('shipping')} - /> + + + toggleSection('shipping')} + /> - { section.shipping && } + { section.shipping && } - - - toggleSection('invoice')} - /> + + + toggleSection('invoice')} + /> - { section.invoice && } + { section.invoice && } - + -
-

Invoice

-
- { transaction.data?.invoices?.map((invoice, index) => ( - -
-
-

{ invoice?.name }

-
- { invoice.amountResidual > 0 ? ( -
Belum Lunas
- ) : ( -
Lunas
- ) } -

- { currencyFormat(invoice.amountTotal) } -

-
-
- +
+

Invoice

+
+ { transaction.data?.invoices?.map((invoice, index) => ( + +
+
+

{ invoice?.name }

+
+ { invoice.amountResidual > 0 ? ( +
Belum Lunas
+ ) : ( +
Lunas
+ ) } +

+ { currencyFormat(invoice.amountTotal) } +

- - )) } - { transaction.data?.invoices?.length === 0 && ( - - Belum ada Invoice - - ) } -
-
- - +
+ +
+ + )) } + { transaction.data?.invoices?.length === 0 && ( + + Belum ada Invoice + + ) } +
+
-
- { transaction.data?.status == 'draft' && ( - - ) } - - { transaction.data?.status != 'draft' && ( - - ) } -
+ - + { transaction.data?.status == 'draft' && ( + + ) } + + { transaction.data?.status != 'draft' && ( + - -
- + Batalkan Transaksi + + ) } +
- +
+ Apakah anda yakin membatalkan transaksi {transaction.data?.name}? +
+
+ - -
-
- - )} + Ya, Batalkan + + +
+
+ +
+ + +
+
+ + +
+
+ + +
+
) } @@ -305,7 +302,7 @@ const SectionButton = ({ label, active, toggle }) => ( const SectionContent = ({ address }) => { let fullAddress = [] if (address?.street) fullAddress.push(address.street) - if (address?.sub_district?.name) fullAddress.push(address.sub_district.name) + if (address?.subDistrict?.name) fullAddress.push(address.sub_district.name) if (address?.district?.name) fullAddress.push(address.district.name) if (address?.city?.name) fullAddress.push(address.city.name) fullAddress = fullAddress.join(', ') diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index 25a6076a..280e8fc5 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -58,79 +58,78 @@ const Transactions = () => { } return ( -
-
-
- setInputQuery(e.target.value)} - /> - -
- { transactions.isLoading && ( -
- -
- ) } +
+
+ setInputQuery(e.target.value)} + /> + +
+ + { transactions.isLoading && ( +
+ +
+ ) } - { !transactions.isLoading && transactions.data?.saleOrders?.length === 0 && ( - - Tidak ada data transaksi - - ) } + { !transactions.isLoading && transactions.data?.saleOrders?.length === 0 && ( + + Tidak ada data transaksi + + ) } - { transactions.data?.saleOrders?.map((saleOrder, index) => ( -
-
- - No. Transaksi -

{ saleOrder.name }

- -
- - setToOthers(saleOrder)} /> + { transactions.data?.saleOrders?.map((saleOrder, index) => ( +
+
+ + No. Transaksi +

{ saleOrder.name }

+ +
+ + setToOthers(saleOrder)} /> +
+
+ +
+
+ No. Purchase Order +

{ saleOrder.purchaseOrderName || '-' }

+
+
+ Total Invoice +

{ saleOrder.invoiceCount } Invoice

- -
-
- No. Purchase Order -

{ saleOrder.purchaseOrderName || '-' }

-
-
- Total Invoice -

{ saleOrder.invoiceCount } Invoice

-
+
+
+ Sales +

{ saleOrder.sales }

-
-
- Sales -

{ saleOrder.sales }

-
-
- Total Harga -

{ currencyFormat(saleOrder.amountTotal) }

-
+
+ Total Harga +

{ currencyFormat(saleOrder.amountTotal) }

- -
- )) } - - -
+
+ +
+ )) } + + setToOthers(null)}>
-- cgit v1.2.3 From 04fe185879061b404fe1baa468bf67d32b3fdd5f Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 21 Feb 2023 16:29:04 +0700 Subject: fix --- src/lib/invoice/components/Invoice.jsx | 2 +- src/lib/invoice/components/Invoices.jsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/lib') diff --git a/src/lib/invoice/components/Invoice.jsx b/src/lib/invoice/components/Invoice.jsx index de6eacca..3e0baaee 100644 --- a/src/lib/invoice/components/Invoice.jsx +++ b/src/lib/invoice/components/Invoice.jsx @@ -55,7 +55,7 @@ const Invoice = ({ id }) => { { invoice.data?.invoiceDate }
-

Faktur Pembelian

+

Invoice

+ + ) +} + +const validationSchema = Yup.object().shape({ + type: Yup.string().required('Harus di-pilih'), + name: Yup.string().min(3, 'Minimal 3 karakter').required('Harus di-isi'), + email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'), + mobile: Yup.string().required('Harus di-isi'), + street: Yup.string().required('Harus di-isi'), + zip: Yup.string().required('Harus di-isi'), + city: Yup.string().required('Harus di-pilih'), +}) + +const defaultValues = { + type: '', + name: '', + email: '', + mobile: '', + street: '', + city: '', + district: '', + subDistrict: '', + zip: '', +} + +const types = [ + { value: 'contact', label: 'Contact Address' }, + { value: 'invoice', label: 'Invoice Address' }, + { value: 'delivery', label: 'Delivery Address' }, + { value: 'other', label: 'Other Address' }, +] + +export default CreateAddress \ No newline at end of file diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx new file mode 100644 index 00000000..b866a1e6 --- /dev/null +++ b/src/lib/address/components/EditAddress.jsx @@ -0,0 +1,256 @@ +import { yupResolver } from "@hookform/resolvers/yup" +import { useRouter } from "next/router" +import { useEffect, useState } from "react" +import * as Yup from "yup" +import cityApi from "../api/cityApi" +import { Controller, useForm } from "react-hook-form" +import districtApi from "../api/districtApi" +import subDistrictApi from "../api/subDistrictApi" +import editAddressApi from "../api/editAddressApi" +import HookFormSelect from "@/core/components/elements/Select/HookFormSelect" +import { toast } from "react-hot-toast" + +const EditAddress = ({ id, defaultValues }) => { + const router = useRouter() + const { + register, + formState: { errors }, + handleSubmit, + watch, + setValue, + getValues, + control, + } = useForm({ + resolver: yupResolver(validationSchema), + defaultValues, + }) + + const [cities, setCities] = useState([]) + const [districts, setDistricts] = useState([]) + const [subDistricts, setSubDistricts] = useState([]) + + useEffect(() => { + const loadCities = async () => { + let dataCities = await cityApi() + dataCities = dataCities.map((city) => ({ + value: city.id, + label: city.name, + })) + setCities(dataCities) + } + loadCities() + }, []) + + const watchCity = watch("city") + useEffect(() => { + setValue("district", "") + if (watchCity) { + const loadDistricts = async () => { + let dataDistricts = await districtApi({ cityId: watchCity }) + dataDistricts = dataDistricts.map((district) => ({ + value: district.id, + label: district.name, + })) + setDistricts(dataDistricts) + let oldDistrict = getValues("oldDistrict") + if (oldDistrict) { + setValue("district", oldDistrict) + setValue("oldDistrict", "") + } + } + loadDistricts() + } + }, [watchCity, setValue, getValues]) + + const watchDistrict = watch("district") + useEffect(() => { + setValue("subDistrict", "") + if (watchDistrict) { + const loadSubDistricts = async () => { + let dataSubDistricts = await subDistrictApi({ + districtId: watchDistrict, + }) + dataSubDistricts = dataSubDistricts.map((district) => ({ + value: district.id, + label: district.name, + })) + setSubDistricts(dataSubDistricts) + let oldSubDistrict = getValues("oldSubDistrict") + + if (oldSubDistrict) { + setValue("subDistrict", oldSubDistrict) + setValue("oldSubDistrict", "") + } + } + loadSubDistricts() + } + }, [watchDistrict, setValue, getValues]) + + const onSubmitHandler = async (values) => { + const data = { + ...values, + city_id: values.city, + district_id: values.district, + sub_district_id: values.subDistrict, + } + + const address = await editAddressApi({ id, data }) + if (address?.id) { + toast.success("Berhasil mengubah alamat") + router.back() + } + } + + return ( +
+
+ + ( + + )} + /> +
+ {errors.type?.message} +
+
+ +
+ + +
+ {errors.name?.message} +
+
+ +
+ + +
+ {errors.email?.message} +
+
+ +
+ + +
+ {errors.mobile?.message} +
+
+ +
+ + +
+ {errors.street?.message} +
+
+ +
+ + +
+ {errors.zip?.message} +
+
+ +
+ + } + /> +
+ {errors.city?.message} +
+
+ +
+ + ( + + )} + /> +
+ +
+ + ( + + )} + /> +
+ + +
+ ) +} + +const validationSchema = Yup.object().shape({ + type: Yup.string().required("Harus di-pilih"), + name: Yup.string().min(3, "Minimal 3 karakter").required("Harus di-isi"), + email: Yup.string() + .email("Format harus seperti johndoe@example.com") + .required("Harus di-isi"), + mobile: Yup.string().required("Harus di-isi"), + street: Yup.string().required("Harus di-isi"), + zip: Yup.string().required("Harus di-isi"), + city: Yup.string().required("Harus di-pilih"), +}) + +const types = [ + { value: "contact", label: "Contact Address" }, + { value: "invoice", label: "Invoice Address" }, + { value: "delivery", label: "Delivery Address" }, + { value: "other", label: "Other Address" }, +] + +export default EditAddress diff --git a/src/lib/wishlist/components/Wishlists.jsx b/src/lib/wishlist/components/Wishlists.jsx index 4bb63933..8cbbb0a2 100644 --- a/src/lib/wishlist/components/Wishlists.jsx +++ b/src/lib/wishlist/components/Wishlists.jsx @@ -33,7 +33,7 @@ const Wishlists = () => {
{wishlists.data?.products.map((product) => ( - + ))}
-- cgit v1.2.3 From f66b12fd1d0b83af0d7230d7b1565fbe00afbe3c Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 22 Feb 2023 11:03:34 +0700 Subject: prettier --- src/lib/address/api/addressApi.js | 4 +- src/lib/address/api/addressesApi.js | 6 +- src/lib/address/api/cityApi.js | 4 +- src/lib/address/api/createAddressApi.js | 4 +- src/lib/address/api/districtApi.js | 4 +- src/lib/address/api/editAddressApi.js | 4 +- src/lib/address/api/subDistrictApi.js | 4 +- src/lib/address/components/Addresses.jsx | 80 +++-- src/lib/address/components/CreateAddress.jsx | 166 ++++----- src/lib/address/components/EditAddress.jsx | 196 ++++------ src/lib/address/hooks/useAddresses.js | 8 +- src/lib/auth/api/loginApi.js | 12 +- src/lib/auth/components/Login.jsx | 93 ++--- src/lib/brand/api/BrandApi.js | 4 +- src/lib/brand/components/Brand.jsx | 66 ++-- src/lib/brand/components/BrandCard.jsx | 20 +- src/lib/brand/hooks/useBrand.js | 6 +- src/lib/cart/api/CartApi.js | 4 +- src/lib/cart/components/Cart.jsx | 216 +++++------ src/lib/cart/hooks/useCart.js | 10 +- src/lib/home/api/categoryHomeApi.js | 4 +- src/lib/home/api/categoryHomeIdApi.js | 4 +- src/lib/home/api/heroBannerApi.js | 4 +- src/lib/home/api/popularProductApi.js | 8 +- src/lib/home/api/preferredBrandApi.js | 4 +- src/lib/home/components/CategoryHome.jsx | 22 +- src/lib/home/components/CategoryHomeId.jsx | 14 +- src/lib/home/components/HeroBanner.jsx | 42 +-- src/lib/home/components/PopularProduct.jsx | 27 +- src/lib/home/components/PreferredBrand.jsx | 32 +- .../components/Skeleton/PopularProductSkeleton.jsx | 6 +- .../components/Skeleton/PreferredBrandSkeleton.jsx | 6 +- src/lib/home/hooks/useCategoryHome.js | 10 +- src/lib/home/hooks/useCategoryHomeId.js | 10 +- src/lib/home/hooks/useHeroBanner.js | 10 +- src/lib/home/hooks/usePopularProduct.js | 6 +- src/lib/home/hooks/usePreferredBrand.js | 6 +- src/lib/invoice/api/invoiceApi.js | 6 +- src/lib/invoice/api/invoicesApi.js | 6 +- src/lib/invoice/components/Invoice.jsx | 165 ++++----- src/lib/invoice/components/Invoices.jsx | 175 ++++----- src/lib/invoice/hooks/useInvoice.js | 6 +- src/lib/invoice/hooks/useInvoices.js | 8 +- src/lib/invoice/utils/invoices.js | 5 +- src/lib/product/api/productApi.js | 4 +- src/lib/product/api/productSearchApi.js | 6 +- src/lib/product/api/productSimilarApi.js | 8 +- src/lib/product/components/Product.jsx | 230 ++++++------ src/lib/product/components/ProductCard.jsx | 80 ++--- src/lib/product/components/ProductFilter.jsx | 107 +++--- src/lib/product/components/ProductSearch.jsx | 86 +++-- src/lib/product/components/ProductSimilar.jsx | 8 +- src/lib/product/components/ProductSlider.jsx | 59 ++-- .../components/Skeleton/ProductSearchSkeleton.jsx | 6 +- src/lib/product/hooks/useProductSearch.js | 8 +- src/lib/product/hooks/useProductSimilar.js | 6 +- src/lib/transaction/api/cancelTransactionApi.js | 11 +- src/lib/transaction/api/checkoutPoApi.js | 11 +- src/lib/transaction/api/transactionApi.js | 6 +- src/lib/transaction/api/transactionsApi.js | 11 +- src/lib/transaction/api/uploadPoApi.js | 12 +- src/lib/transaction/components/Transaction.jsx | 393 ++++++++++----------- .../components/TransactionStatusBadge.jsx | 8 +- src/lib/transaction/components/Transactions.jsx | 192 +++++----- src/lib/transaction/hooks/useTransaction.js | 6 +- src/lib/transaction/hooks/useTransactions.js | 8 +- src/lib/transaction/utils/transactions.js | 7 +- src/lib/variant/components/VariantCard.jsx | 82 +++-- src/lib/variant/components/VariantGroupCard.jsx | 35 +- src/lib/wishlist/api/wishlistsApi.js | 11 +- src/lib/wishlist/components/Wishlists.jsx | 34 +- src/lib/wishlist/hooks/useWishlists.js | 6 +- 72 files changed, 1399 insertions(+), 1539 deletions(-) (limited to 'src/lib') diff --git a/src/lib/address/api/addressApi.js b/src/lib/address/api/addressApi.js index f2fc6c9e..f99d81c0 100644 --- a/src/lib/address/api/addressApi.js +++ b/src/lib/address/api/addressApi.js @@ -1,8 +1,8 @@ -import odooApi from "@/core/api/odooApi" +import odooApi from '@/core/api/odooApi' const addressApi = async ({ id }) => { const dataAddress = await odooApi('GET', `/api/v1/partner/${id}/address`) return dataAddress } -export default addressApi \ No newline at end of file +export default addressApi diff --git a/src/lib/address/api/addressesApi.js b/src/lib/address/api/addressesApi.js index 1edfc077..96f9e9d9 100644 --- a/src/lib/address/api/addressesApi.js +++ b/src/lib/address/api/addressesApi.js @@ -1,5 +1,5 @@ -import odooApi from "@/core/api/odooApi" -import { getAuth } from "@/core/utils/auth" +import odooApi from '@/core/api/odooApi' +import { getAuth } from '@/core/utils/auth' const addressesApi = async () => { const auth = getAuth() @@ -7,4 +7,4 @@ const addressesApi = async () => { return dataAddresses } -export default addressesApi \ No newline at end of file +export default addressesApi diff --git a/src/lib/address/api/cityApi.js b/src/lib/address/api/cityApi.js index 8cf1bedd..7873435b 100644 --- a/src/lib/address/api/cityApi.js +++ b/src/lib/address/api/cityApi.js @@ -1,8 +1,8 @@ -import odooApi from "@/core/api/odooApi" +import odooApi from '@/core/api/odooApi' const cityApi = async () => { const dataCities = await odooApi('GET', '/api/v1/city') return dataCities } -export default cityApi \ No newline at end of file +export default cityApi diff --git a/src/lib/address/api/createAddressApi.js b/src/lib/address/api/createAddressApi.js index 29804d1c..b33b7ae1 100644 --- a/src/lib/address/api/createAddressApi.js +++ b/src/lib/address/api/createAddressApi.js @@ -1,8 +1,8 @@ -import odooApi from "@/core/api/odooApi" +import odooApi from '@/core/api/odooApi' const createAddressApi = async ({ data }) => { const dataAddress = await odooApi('POST', '/api/v1/partner/address', data) return dataAddress } -export default createAddressApi \ No newline at end of file +export default createAddressApi diff --git a/src/lib/address/api/districtApi.js b/src/lib/address/api/districtApi.js index 120757e9..b0bcff16 100644 --- a/src/lib/address/api/districtApi.js +++ b/src/lib/address/api/districtApi.js @@ -1,8 +1,8 @@ -import odooApi from "@/core/api/odooApi" +import odooApi from '@/core/api/odooApi' const districtApi = async ({ cityId }) => { const dataDistricts = await odooApi('GET', `/api/v1/district?city_id=${cityId}`) return dataDistricts } -export default districtApi \ No newline at end of file +export default districtApi diff --git a/src/lib/address/api/editAddressApi.js b/src/lib/address/api/editAddressApi.js index e01f6015..ba383ef0 100644 --- a/src/lib/address/api/editAddressApi.js +++ b/src/lib/address/api/editAddressApi.js @@ -1,8 +1,8 @@ -import odooApi from "@/core/api/odooApi" +import odooApi from '@/core/api/odooApi' const editAddressApi = async ({ id, data }) => { const dataAddress = await odooApi('PUT', `/api/v1/partner/${id}/address`, data) return dataAddress } -export default editAddressApi \ No newline at end of file +export default editAddressApi diff --git a/src/lib/address/api/subDistrictApi.js b/src/lib/address/api/subDistrictApi.js index 64230838..3f834420 100644 --- a/src/lib/address/api/subDistrictApi.js +++ b/src/lib/address/api/subDistrictApi.js @@ -1,8 +1,8 @@ -import odooApi from "@/core/api/odooApi" +import odooApi from '@/core/api/odooApi' const subDistrictApi = async ({ districtId }) => { const dataSubDistricts = await odooApi('GET', `/api/v1/sub_district?district_id=${districtId}`) return dataSubDistricts } -export default subDistrictApi \ No newline at end of file +export default subDistrictApi diff --git a/src/lib/address/components/Addresses.jsx b/src/lib/address/components/Addresses.jsx index 7a82c0da..c4855f8f 100644 --- a/src/lib/address/components/Addresses.jsx +++ b/src/lib/address/components/Addresses.jsx @@ -1,15 +1,13 @@ -import Link from "@/core/components/elements/Link/Link" -import Spinner from "@/core/components/elements/Spinner/Spinner" -import useAuth from "@/core/hooks/useAuth" -import { getItemAddress, updateItemAddress } from "@/core/utils/address" -import { useRouter } from "next/router" -import useAddresses from "../hooks/useAddresses" +import Link from '@/core/components/elements/Link/Link' +import Spinner from '@/core/components/elements/Spinner/Spinner' +import useAuth from '@/core/hooks/useAuth' +import { getItemAddress, updateItemAddress } from '@/core/utils/address' +import { useRouter } from 'next/router' +import useAddresses from '../hooks/useAddresses' const Addresses = () => { const router = useRouter() - const { - select = null - } = router.query + const { select = null } = router.query const auth = useAuth() const { addresses } = useAddresses() const selectedAdress = getItemAddress(select || '') @@ -21,48 +19,60 @@ const Addresses = () => { if (addresses.isLoading) { return ( -
- +
+
) } return ( -
-
- Tambah Alamat +
+
+ Tambah Alamat
-
- { addresses.data?.map((address, index) => { - let type = address.type.charAt(0).toUpperCase() + address.type.slice(1) + ' Address'; +
+ {addresses.data?.map((address, index) => { + let type = address.type.charAt(0).toUpperCase() + address.type.slice(1) + ' Address' return ( -
changeSelectedAddress(address.id)}> -
-
{ type }
- { auth?.partnerId == address.id && ( -
Utama
- ) } +
+
{type}
+ {auth?.partnerId == address.id &&
Utama
}
-

{ address.name }

- { address.mobile && ( -

{ address.mobile }

- ) } -

- { address.street } +

{address.name}

+ {address.mobile &&

{address.mobile}

} +

+ {address.street}

- Ubah Alamat + + Ubah Alamat +
- ); - }) } + ) + })}
) } -export default Addresses \ No newline at end of file +export default Addresses diff --git a/src/lib/address/components/CreateAddress.jsx b/src/lib/address/components/CreateAddress.jsx index 4ba99820..62bb0858 100644 --- a/src/lib/address/components/CreateAddress.jsx +++ b/src/lib/address/components/CreateAddress.jsx @@ -1,34 +1,34 @@ -import HookFormSelect from "@/core/components/elements/Select/HookFormSelect" -import useAuth from "@/core/hooks/useAuth" -import { useRouter } from "next/router" -import { Controller, useForm } from "react-hook-form" -import * as Yup from "yup" -import cityApi from "../api/cityApi" -import districtApi from "../api/districtApi" -import subDistrictApi from "../api/subDistrictApi" -import { useEffect, useState } from "react" -import createAddressApi from "../api/createAddressApi" -import { toast } from "react-hot-toast" -import { yupResolver } from "@hookform/resolvers/yup" +import HookFormSelect from '@/core/components/elements/Select/HookFormSelect' +import useAuth from '@/core/hooks/useAuth' +import { useRouter } from 'next/router' +import { Controller, useForm } from 'react-hook-form' +import * as Yup from 'yup' +import cityApi from '../api/cityApi' +import districtApi from '../api/districtApi' +import subDistrictApi from '../api/subDistrictApi' +import { useEffect, useState } from 'react' +import createAddressApi from '../api/createAddressApi' +import { toast } from 'react-hot-toast' +import { yupResolver } from '@hookform/resolvers/yup' const CreateAddress = () => { const auth = useAuth() const router = useRouter() - const { - register, - formState: { errors }, + const { + register, + formState: { errors }, handleSubmit, watch, setValue, - control, - } = useForm({ + control + } = useForm({ resolver: yupResolver(validationSchema), defaultValues }) - const [ cities, setCities ] = useState([]) - const [ districts, setDistricts ] = useState([]) - const [ subDistricts, setSubDistricts ] = useState([]) + const [cities, setCities] = useState([]) + const [districts, setDistricts] = useState([]) + const [subDistricts, setSubDistricts] = useState([]) useEffect(() => { const loadCities = async () => { @@ -45,12 +45,15 @@ const CreateAddress = () => { if (watchCity) { const loadDistricts = async () => { let dataDistricts = await districtApi({ cityId: watchCity }) - dataDistricts = dataDistricts.map((district) => ({ value: district.id, label: district.name })) + dataDistricts = dataDistricts.map((district) => ({ + value: district.id, + label: district.name + })) setDistricts(dataDistricts) } loadDistricts() } - }, [ watchCity, setValue ]) + }, [watchCity, setValue]) const watchDistrict = watch('district') useEffect(() => { @@ -58,14 +61,17 @@ const CreateAddress = () => { if (watchDistrict) { const loadSubDistricts = async () => { let dataSubDistricts = await subDistrictApi({ districtId: watchDistrict }) - dataSubDistricts = dataSubDistricts.map((district) => ({ value: district.id, label: district.name })) + dataSubDistricts = dataSubDistricts.map((district) => ({ + value: district.id, + label: district.name + })) setSubDistricts(dataSubDistricts) } loadSubDistricts() } - }, [ watchDistrict, setValue ]) + }, [watchDistrict, setValue]) - const onSubmitHandler = async (values) => { + const onSubmitHandler = async (values) => { const data = { ...values, city_id: values.city, @@ -79,119 +85,93 @@ const CreateAddress = () => { toast.success('Berhasil menambahkan alamat') router.back() } - } + } return ( -
+
- + } + render={(props) => } /> -
{ errors.type?.message }
+
{errors.type?.message}
- - -
{ errors.name?.message }
+ + +
{errors.name?.message}
- + -
{ errors.email?.message }
+
{errors.email?.message}
- - -
{ errors.mobile?.message }
+ + +
{errors.mobile?.message}
- + -
{ errors.street?.message }
+
{errors.street?.message}
- - -
{ errors.zip?.message }
+ + +
{errors.zip?.message}
- + } + render={(props) => } /> -
{ errors.city?.message }
+
{errors.city?.message}
- + ( - + render={(props) => ( + )} />
- + ( - + render={(props) => ( + )} />
-
@@ -205,7 +185,7 @@ const validationSchema = Yup.object().shape({ mobile: Yup.string().required('Harus di-isi'), street: Yup.string().required('Harus di-isi'), zip: Yup.string().required('Harus di-isi'), - city: Yup.string().required('Harus di-pilih'), + city: Yup.string().required('Harus di-pilih') }) const defaultValues = { @@ -217,14 +197,14 @@ const defaultValues = { city: '', district: '', subDistrict: '', - zip: '', + zip: '' } const types = [ { value: 'contact', label: 'Contact Address' }, { value: 'invoice', label: 'Invoice Address' }, { value: 'delivery', label: 'Delivery Address' }, - { value: 'other', label: 'Other Address' }, + { value: 'other', label: 'Other Address' } ] -export default CreateAddress \ No newline at end of file +export default CreateAddress diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx index b866a1e6..0cfa013a 100644 --- a/src/lib/address/components/EditAddress.jsx +++ b/src/lib/address/components/EditAddress.jsx @@ -1,14 +1,14 @@ -import { yupResolver } from "@hookform/resolvers/yup" -import { useRouter } from "next/router" -import { useEffect, useState } from "react" -import * as Yup from "yup" -import cityApi from "../api/cityApi" -import { Controller, useForm } from "react-hook-form" -import districtApi from "../api/districtApi" -import subDistrictApi from "../api/subDistrictApi" -import editAddressApi from "../api/editAddressApi" -import HookFormSelect from "@/core/components/elements/Select/HookFormSelect" -import { toast } from "react-hot-toast" +import { yupResolver } from '@hookform/resolvers/yup' +import { useRouter } from 'next/router' +import { useEffect, useState } from 'react' +import * as Yup from 'yup' +import cityApi from '../api/cityApi' +import { Controller, useForm } from 'react-hook-form' +import districtApi from '../api/districtApi' +import subDistrictApi from '../api/subDistrictApi' +import editAddressApi from '../api/editAddressApi' +import HookFormSelect from '@/core/components/elements/Select/HookFormSelect' +import { toast } from 'react-hot-toast' const EditAddress = ({ id, defaultValues }) => { const router = useRouter() @@ -19,10 +19,10 @@ const EditAddress = ({ id, defaultValues }) => { watch, setValue, getValues, - control, + control } = useForm({ resolver: yupResolver(validationSchema), - defaultValues, + defaultValues }) const [cities, setCities] = useState([]) @@ -34,52 +34,52 @@ const EditAddress = ({ id, defaultValues }) => { let dataCities = await cityApi() dataCities = dataCities.map((city) => ({ value: city.id, - label: city.name, + label: city.name })) setCities(dataCities) } loadCities() }, []) - const watchCity = watch("city") + const watchCity = watch('city') useEffect(() => { - setValue("district", "") + setValue('district', '') if (watchCity) { const loadDistricts = async () => { let dataDistricts = await districtApi({ cityId: watchCity }) dataDistricts = dataDistricts.map((district) => ({ value: district.id, - label: district.name, + label: district.name })) setDistricts(dataDistricts) - let oldDistrict = getValues("oldDistrict") + let oldDistrict = getValues('oldDistrict') if (oldDistrict) { - setValue("district", oldDistrict) - setValue("oldDistrict", "") + setValue('district', oldDistrict) + setValue('oldDistrict', '') } } loadDistricts() } }, [watchCity, setValue, getValues]) - const watchDistrict = watch("district") + const watchDistrict = watch('district') useEffect(() => { - setValue("subDistrict", "") + setValue('subDistrict', '') if (watchDistrict) { const loadSubDistricts = async () => { let dataSubDistricts = await subDistrictApi({ - districtId: watchDistrict, + districtId: watchDistrict }) dataSubDistricts = dataSubDistricts.map((district) => ({ value: district.id, - label: district.name, + label: district.name })) setSubDistricts(dataSubDistricts) - let oldSubDistrict = getValues("oldSubDistrict") + let oldSubDistrict = getValues('oldSubDistrict') if (oldSubDistrict) { - setValue("subDistrict", oldSubDistrict) - setValue("oldSubDistrict", "") + setValue('subDistrict', oldSubDistrict) + setValue('oldSubDistrict', '') } } loadSubDistricts() @@ -91,143 +91,101 @@ const EditAddress = ({ id, defaultValues }) => { ...values, city_id: values.city, district_id: values.district, - sub_district_id: values.subDistrict, + sub_district_id: values.subDistrict } const address = await editAddressApi({ id, data }) if (address?.id) { - toast.success("Berhasil mengubah alamat") + toast.success('Berhasil mengubah alamat') router.back() } } return ( -
+
- + ( - - )} + render={(props) => } /> -
- {errors.type?.message} -
+
{errors.type?.message}
- - -
- {errors.name?.message} -
+ + +
{errors.name?.message}
- + -
- {errors.email?.message} -
+
{errors.email?.message}
- - -
- {errors.mobile?.message} -
+ + +
{errors.mobile?.message}
- + -
- {errors.street?.message} -
+
{errors.street?.message}
- - -
- {errors.zip?.message} -
+ + +
{errors.zip?.message}
- + } /> -
- {errors.city?.message} -
+
{errors.city?.message}
- + ( - + )} />
- + ( - + )} />
-
@@ -235,22 +193,20 @@ const EditAddress = ({ id, defaultValues }) => { } const validationSchema = Yup.object().shape({ - type: Yup.string().required("Harus di-pilih"), - name: Yup.string().min(3, "Minimal 3 karakter").required("Harus di-isi"), - email: Yup.string() - .email("Format harus seperti johndoe@example.com") - .required("Harus di-isi"), - mobile: Yup.string().required("Harus di-isi"), - street: Yup.string().required("Harus di-isi"), - zip: Yup.string().required("Harus di-isi"), - city: Yup.string().required("Harus di-pilih"), + type: Yup.string().required('Harus di-pilih'), + name: Yup.string().min(3, 'Minimal 3 karakter').required('Harus di-isi'), + email: Yup.string().email('Format harus seperti johndoe@example.com').required('Harus di-isi'), + mobile: Yup.string().required('Harus di-isi'), + street: Yup.string().required('Harus di-isi'), + zip: Yup.string().required('Harus di-isi'), + city: Yup.string().required('Harus di-pilih') }) const types = [ - { value: "contact", label: "Contact Address" }, - { value: "invoice", label: "Invoice Address" }, - { value: "delivery", label: "Delivery Address" }, - { value: "other", label: "Other Address" }, + { value: 'contact', label: 'Contact Address' }, + { value: 'invoice', label: 'Invoice Address' }, + { value: 'delivery', label: 'Delivery Address' }, + { value: 'other', label: 'Other Address' } ] export default EditAddress diff --git a/src/lib/address/hooks/useAddresses.js b/src/lib/address/hooks/useAddresses.js index 9968d790..629e367c 100644 --- a/src/lib/address/hooks/useAddresses.js +++ b/src/lib/address/hooks/useAddresses.js @@ -1,13 +1,13 @@ -import { useQuery } from "react-query" -import addressesApi from "../api/addressesApi" +import { useQuery } from 'react-query' +import addressesApi from '../api/addressesApi' const useAddresses = () => { const fetchAddresses = async () => await addressesApi() const { data, isLoading } = useQuery('addresses', fetchAddresses) - + return { addresses: { data, isLoading } } } -export default useAddresses \ No newline at end of file +export default useAddresses diff --git a/src/lib/auth/api/loginApi.js b/src/lib/auth/api/loginApi.js index 4782680c..e393309c 100644 --- a/src/lib/auth/api/loginApi.js +++ b/src/lib/auth/api/loginApi.js @@ -1,12 +1,8 @@ -import odooApi from "@/core/api/odooApi" +import odooApi from '@/core/api/odooApi' -const loginApi = async ({email, password}) => { - let result = await odooApi( - 'POST', - '/api/v1/user/login', - {email, password} - ) +const loginApi = async ({ email, password }) => { + let result = await odooApi('POST', '/api/v1/user/login', { email, password }) return result } -export default loginApi \ No newline at end of file +export default loginApi diff --git a/src/lib/auth/components/Login.jsx b/src/lib/auth/components/Login.jsx index acb6e8c3..971188a2 100644 --- a/src/lib/auth/components/Login.jsx +++ b/src/lib/auth/components/Login.jsx @@ -1,18 +1,18 @@ -import Image from "next/image" -import IndoteknikLogo from "@/images/logo.png" -import Link from "@/core/components/elements/Link/Link" -import { useState } from "react" -import loginApi from "../api/loginApi" -import { useRouter } from "next/router" -import Alert from "@/core/components/elements/Alert/Alert" -import { setAuth } from "@/core/utils/auth" +import Image from 'next/image' +import IndoteknikLogo from '@/images/logo.png' +import Link from '@/core/components/elements/Link/Link' +import { useState } from 'react' +import loginApi from '../api/loginApi' +import { useRouter } from 'next/router' +import Alert from '@/core/components/elements/Alert/Alert' +import { setAuth } from '@/core/utils/auth' const Login = () => { const router = useRouter() - const [ email, setEmail ] = useState('') - const [ password, setPassword ] = useState('') - const [ isLoading, setIsLoading ] = useState(false) - const [ alert, setAlert ] = useState(null) + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [isLoading, setIsLoading] = useState(false) + const [alert, setAlert] = useState(null) const handleSubmit = async (e) => { e.preventDefault() @@ -36,8 +36,10 @@ const Login = () => { setAlert({ children: ( <> - Email belum diaktivasi, - aktivasi sekarang + Email belum diaktivasi, + + aktivasi sekarang + ), type: 'info' @@ -47,53 +49,58 @@ const Login = () => { } return ( -
- - Logo Indoteknik +
+ + Logo Indoteknik -

Mulai Belanja Sekarang

-

Masuk ke akun kamu untuk belanja

- - { alert && ( - - { alert.children } +

Mulai Belanja Sekarang

+

Masuk ke akun kamu untuk belanja

+ + {alert && ( + + {alert.children} - ) } + )} -
+
- - Alamat Email + setEmail(e.target.value)} - placeholder="contoh@email.com" + placeholder='contoh@email.com' />
- - Kata Sandi + setPassword(e.target.value)} - placeholder="••••••••••••" + placeholder='••••••••••••' />
-
-

Belum punya akun Indoteknik? Daftar

+

+ Belum punya akun Indoteknik?{' '} + + Daftar + +

) } -export default Login \ No newline at end of file +export default Login diff --git a/src/lib/brand/api/BrandApi.js b/src/lib/brand/api/BrandApi.js index 15634cc4..79801774 100644 --- a/src/lib/brand/api/BrandApi.js +++ b/src/lib/brand/api/BrandApi.js @@ -1,8 +1,8 @@ -import odooApi from "@/core/api/odooApi" +import odooApi from '@/core/api/odooApi' const BrandApi = async ({ id }) => { const dataBrand = await odooApi('GET', `/api/v1/manufacture/${id}`) return dataBrand } -export default BrandApi \ No newline at end of file +export default BrandApi diff --git a/src/lib/brand/components/Brand.jsx b/src/lib/brand/components/Brand.jsx index c1215976..c338c4c4 100644 --- a/src/lib/brand/components/Brand.jsx +++ b/src/lib/brand/components/Brand.jsx @@ -1,13 +1,13 @@ -import useBrand from "../hooks/useBrand" -import Image from "@/core/components/elements/Image/Image" +import useBrand from '../hooks/useBrand' +import Image from '@/core/components/elements/Image/Image' -import { Swiper, SwiperSlide } from "swiper/react" -import { Pagination, Autoplay } from "swiper" -import "swiper/css" -import "swiper/css/pagination" -import "swiper/css/autoplay" -import Divider from "@/core/components/elements/Divider/Divider" -import ImageSkeleton from "@/core/components/elements/Skeleton/ImageSkeleton" +import { Swiper, SwiperSlide } from 'swiper/react' +import { Pagination, Autoplay } from 'swiper' +import 'swiper/css' +import 'swiper/css/pagination' +import 'swiper/css/autoplay' +import Divider from '@/core/components/elements/Divider/Divider' +import ImageSkeleton from '@/core/components/elements/Skeleton/ImageSkeleton' const swiperBanner = { pagination: { dynamicBullets: true }, @@ -20,51 +20,51 @@ const swiperBanner = { const Brand = ({ id }) => { const { brand } = useBrand({ id }) - + return ( <> -
- { brand.isLoading && } - { brand.data && ( +
+ {brand.isLoading && } + {brand.data && ( <> - { brand.data?.banners?.map((banner, index) => ( + {brand.data?.banners?.map((banner, index) => ( - {`Brand - )) } + ))} -
-
Produk dari brand:
- { brand?.data?.logo && ( - {brand?.data?.name} +
Produk dari brand:
+ {brand?.data?.logo && ( + {brand?.data?.name} - ) } - { !brand?.data?.logo && ( -
- { brand?.data?.name } + )} + {!brand?.data?.logo && ( +
+ {brand?.data?.name}
- ) } + )}
- ) } + )}
) } -export default Brand \ No newline at end of file +export default Brand diff --git a/src/lib/brand/components/BrandCard.jsx b/src/lib/brand/components/BrandCard.jsx index 8783010e..0dbdc075 100644 --- a/src/lib/brand/components/BrandCard.jsx +++ b/src/lib/brand/components/BrandCard.jsx @@ -1,20 +1,16 @@ -import Image from "@/core/components/elements/Image/Image" -import Link from "@/core/components/elements/Link/Link" -import { createSlug } from "@/core/utils/slug" +import Image from '@/core/components/elements/Image/Image' +import Link from '@/core/components/elements/Link/Link' +import { createSlug } from '@/core/utils/slug' const BrandCard = ({ brand }) => { return ( - - {brand.name} + {brand.name} ) } -export default BrandCard \ No newline at end of file +export default BrandCard diff --git a/src/lib/brand/hooks/useBrand.js b/src/lib/brand/hooks/useBrand.js index be42a44c..3ba65a97 100644 --- a/src/lib/brand/hooks/useBrand.js +++ b/src/lib/brand/hooks/useBrand.js @@ -1,5 +1,5 @@ -import { useQuery } from "react-query" -import BrandApi from "../api/BrandApi" +import { useQuery } from 'react-query' +import BrandApi from '../api/BrandApi' const useBrand = ({ id }) => { const fetchBrand = async () => await BrandApi({ id }) @@ -10,4 +10,4 @@ const useBrand = ({ id }) => { } } -export default useBrand \ No newline at end of file +export default useBrand diff --git a/src/lib/cart/api/CartApi.js b/src/lib/cart/api/CartApi.js index 9a5b5053..33b61891 100644 --- a/src/lib/cart/api/CartApi.js +++ b/src/lib/cart/api/CartApi.js @@ -1,4 +1,4 @@ -import odooApi from "@/core/api/odooApi" +import odooApi from '@/core/api/odooApi' const CartApi = async ({ variantIds }) => { if (variantIds) { @@ -8,4 +8,4 @@ const CartApi = async ({ variantIds }) => { return null } -export default CartApi \ No newline at end of file +export default CartApi diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index e7606582..df74bed6 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -1,26 +1,26 @@ -import Link from "@/core/components/elements/Link/Link" -import useCart from "../hooks/useCart" -import Image from "@/core/components/elements/Image/Image" -import currencyFormat from "@/core/utils/currencyFormat" -import { useEffect, useState } from "react" -import { deleteItemCart, getItemCart, updateItemCart } from "@/core/utils/cart" -import { CheckIcon, RectangleGroupIcon, TrashIcon } from "@heroicons/react/24/outline" -import { createSlug } from "@/core/utils/slug" -import { useRouter } from "next/router" -import BottomPopup from "@/core/components/elements/Popup/BottomPopup" -import { toast } from "react-hot-toast" -import Spinner from "@/core/components/elements/Spinner/Spinner" +import Link from '@/core/components/elements/Link/Link' +import useCart from '../hooks/useCart' +import Image from '@/core/components/elements/Image/Image' +import currencyFormat from '@/core/utils/currencyFormat' +import { useEffect, useState } from 'react' +import { deleteItemCart, getItemCart, updateItemCart } from '@/core/utils/cart' +import { CheckIcon, RectangleGroupIcon, TrashIcon } from '@heroicons/react/24/outline' +import { createSlug } from '@/core/utils/slug' +import { useRouter } from 'next/router' +import BottomPopup from '@/core/components/elements/Popup/BottomPopup' +import { toast } from 'react-hot-toast' +import Spinner from '@/core/components/elements/Spinner/Spinner' const Cart = () => { const router = useRouter() - const [ products, setProducts ] = useState(null) + const [products, setProducts] = useState(null) const { cart } = useCart({ enabled: !products }) - const [ totalPriceBeforeTax, setTotalPriceBeforeTax ] = useState(0) - const [ totalTaxAmount, setTotalTaxAmount ] = useState(0) - const [ totalDiscountAmount, setTotalDiscountAmount ] = useState(0) + const [totalPriceBeforeTax, setTotalPriceBeforeTax] = useState(0) + const [totalTaxAmount, setTotalTaxAmount] = useState(0) + const [totalDiscountAmount, setTotalDiscountAmount] = useState(0) - const [ deleteConfirmation, setDeleteConfirmation ] = useState(null) + const [deleteConfirmation, setDeleteConfirmation] = useState(null) useEffect(() => { if (cart.data && !products) { @@ -38,7 +38,7 @@ const Cart = () => { }, [cart, products]) useEffect(() => { - if (!products) return + if (!products) return let calculateTotalPriceBeforeTax = 0 let calculateTotalTaxAmount = 0 @@ -50,12 +50,13 @@ const Cart = () => { quantity: product.quantity, selected: product.selected }) - + if (!product.selected) continue - let priceBeforeTax = product.price.price / 1.11 + let priceBeforeTax = product.price.price / 1.11 calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity calculateTotalTaxAmount += (product.price.price - priceBeforeTax) * product.quantity - calculateTotalDiscountAmount += (product.price.price - product.price.priceDiscount) * product.quantity + calculateTotalDiscountAmount += + (product.price.price - product.price.priceDiscount) * product.quantity } setTotalPriceBeforeTax(calculateTotalPriceBeforeTax) setTotalTaxAmount(calculateTotalTaxAmount) @@ -75,7 +76,7 @@ const Cart = () => { quantity += value break case 'MINUS': - if ((quantity - value) < 1) return + if (quantity - value < 1) return quantity -= value break case 'BLUR': @@ -87,7 +88,7 @@ const Cart = () => { break } productsToUpdate[productIndex].quantity = quantity - setProducts([ ...productsToUpdate ]) + setProducts([...productsToUpdate]) } const toggleSelected = (productId) => { @@ -96,7 +97,7 @@ const Cart = () => { let productsToUpdate = products productsToUpdate[productIndex].selected = !productsToUpdate[productIndex].selected - setProducts([ ...productsToUpdate ]) + setProducts([...productsToUpdate]) } const selectedProduct = () => { @@ -111,124 +112,126 @@ const Cart = () => { setProducts([...productsToUpdate]) toast.success('Berhasil menghapus barang dari keranjang') } - + return ( -
-
-

Daftar Produk Belanja

- Cari Produk Lain +
+
+

Daftar Produk Belanja

+ Cari Produk Lain
-
- { cart.isLoading && ( -
- +
+ {cart.isLoading && ( +
+
- ) } - { products?.map((product) => ( -
+ )} + {products?.map((product) => ( +
- - {product?.name} + {product?.name} -
- + - { product?.parent?.name } + {product?.parent?.name} -
- { product?.code } {product?.attributes.length > 0 ? `| ${product?.attributes.join(', ')}` : ''} +
+ {product?.code}{' '} + {product?.attributes.length > 0 ? `| ${product?.attributes.join(', ')}` : ''}
- { product?.price?.discountPercentage > 0 && ( -
-
+ {product?.price?.discountPercentage > 0 && ( +
+
{currencyFormat(product?.price?.price)}
-
- {product?.price?.discountPercentage}% -
+
{product?.price?.discountPercentage}%
- ) } -
- { currencyFormat(product?.price?.priceDiscount) } + )} +
+ {currencyFormat(product?.price?.priceDiscount)}
-
-
- { currencyFormat(product?.price?.priceDiscount * product?.quantity) } +
+
+ {currencyFormat(product?.price?.priceDiscount * product?.quantity)}
-
- - updateQuantity(e.target.value, product?.id)} onBlur={(e) => updateQuantity(e.target.value, product?.id, 'BLUR')} /> - -
- )) } + ))}
-
-
-
- Total: - -  { selectedProduct().length > 0 ? currencyFormat(totalPriceBeforeTax - totalDiscountAmount + totalTaxAmount) : '-' } +
+
+
+ Total: + +   + {selectedProduct().length > 0 + ? currencyFormat(totalPriceBeforeTax - totalDiscountAmount + totalTaxAmount) + : '-'}
-
- - - -
-
-

Faktur Pajak

- + + {invoice.data?.purchaseOrderName || '-'} + + {invoice.data?.paymentTerm} + {invoice.data?.amountResidual > 0 && invoice.invoiceDate != invoice.invoiceDateDue && ( + + {invoice.data?.invoiceDateDue} + + )} + {invoice.data?.sales} + {invoice.data?.invoiceDate} +
+

Invoice

+ +
+
+

Faktur Pajak

+ +
-
- + -
- Detail Penagihan -
- -
- - { address?.name } - - - { address?.email || '-' } - - - { address?.mobile || '-' } - - - { fullAddress } - -
+
Detail Penagihan
- +
+ {address?.name} + {address?.email || '-'} + {address?.mobile || '-'} + {fullAddress} +
+ + -
Detail Produk
+
Detail Produk
-
- -
-

Total Belanja

-

{ currencyFormat(invoice.data?.amountTotal) }

+
+ +
+

Total Belanja

+

{currencyFormat(invoice.data?.amountTotal)}

+
-
- + + ) ) } const DescriptionRow = ({ children, label }) => ( -
- { label } - { children } +
+ {label} + {children}
) -export default Invoice \ No newline at end of file +export default Invoice diff --git a/src/lib/invoice/components/Invoices.jsx b/src/lib/invoice/components/Invoices.jsx index 37944e33..81521785 100644 --- a/src/lib/invoice/components/Invoices.jsx +++ b/src/lib/invoice/components/Invoices.jsx @@ -1,23 +1,25 @@ -import { CheckIcon, ClockIcon, EllipsisVerticalIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline" -import { toQuery } from "lodash-contrib" -import _ from "lodash" -import { useRouter } from "next/router" -import { useState } from "react" -import useInvoices from "../hooks/useInvoices" -import Spinner from "@/core/components/elements/Spinner/Spinner" -import Alert from "@/core/components/elements/Alert/Alert" -import Pagination from "@/core/components/elements/Pagination/Pagination" -import Link from "@/core/components/elements/Link/Link" -import currencyFormat from "@/core/utils/currencyFormat" -import BottomPopup from "@/core/components/elements/Popup/BottomPopup" -import { downloadInvoice, downloadTaxInvoice } from "../utils/invoices" +import { + CheckIcon, + ClockIcon, + EllipsisVerticalIcon, + MagnifyingGlassIcon +} from '@heroicons/react/24/outline' +import { toQuery } from 'lodash-contrib' +import _ from 'lodash' +import { useRouter } from 'next/router' +import { useState } from 'react' +import useInvoices from '../hooks/useInvoices' +import Spinner from '@/core/components/elements/Spinner/Spinner' +import Alert from '@/core/components/elements/Alert/Alert' +import Pagination from '@/core/components/elements/Pagination/Pagination' +import Link from '@/core/components/elements/Link/Link' +import currencyFormat from '@/core/utils/currencyFormat' +import BottomPopup from '@/core/components/elements/Popup/BottomPopup' +import { downloadInvoice, downloadTaxInvoice } from '../utils/invoices' const Invoices = () => { const router = useRouter() - const { - q = '', - page = 1 - } = router.query + const { q = '', page = 1 } = router.query const limit = 10 @@ -28,8 +30,8 @@ const Invoices = () => { } const { invoices } = useInvoices({ query }) - const [ inputQuery, setInputQuery ] = useState(q) - const [ toOthers, setToOthers ] = useState(null) + const [inputQuery, setInputQuery] = useState(q) + const [toOthers, setToOthers] = useState(null) const pageCount = Math.ceil(invoices?.data?.saleOrderTotal / limit) let pageQuery = _.omit(query, ['limit', 'offset']) @@ -39,112 +41,111 @@ const Invoices = () => { const handleSubmit = (e) => { e.preventDefault() router.push(`/my/invoices?q=${inputQuery}`) - } + } return ( -
-
- + + setInputQuery(e.target.value)} /> -
- { invoices.isLoading && ( -
- + {invoices.isLoading && ( +
+
- ) } + )} - { !invoices.isLoading && invoices.data?.invoices?.length === 0 && ( - + {!invoices.isLoading && invoices.data?.invoices?.length === 0 && ( + Tidak ada data invoice - ) } + )} - { invoices.data?.invoices?.map((invoice, index) => ( -
-
+ {invoices.data?.invoices?.map((invoice, index) => ( +
+
- No. Invoice -

{ invoice.name }

+ No. Invoice +

{invoice.name}

-
- { invoice.amountResidual > 0 ? ( -
Belum Lunas
- ) : ( -
Lunas
- ) } - setToOthers(invoice)} /> +
+ {invoice.amountResidual > 0 ? ( +
Belum Lunas
+ ) : ( +
Lunas
+ )} + setToOthers(invoice)} />
-
-

- { invoice.invoiceDate } -

-

- { invoice.paymentTerm } -

+
+

{invoice.invoiceDate}

+

{invoice.paymentTerm}

-
-
+
+
- No. Purchase Order -

{ invoice.purchaseOrderName || '-' }

+ No. Purchase Order +

+ {invoice.purchaseOrderName || '-'} +

-
- Total Invoice -

{ currencyFormat(invoice.amountTotal) }

+
+ Total Invoice +

+ {currencyFormat(invoice.amountTotal)} +

- { invoice.efaktur ? ( -
- + {invoice.efaktur ? ( +
+ Faktur Pajak
- ) : ( -
- + ) : ( +
+ Faktur Pajak
- ) } + )}
- )) } + ))} - setToOthers(null)} - > -
- - @@ -154,4 +155,4 @@ const Invoices = () => { ) } -export default Invoices \ No newline at end of file +export default Invoices diff --git a/src/lib/invoice/hooks/useInvoice.js b/src/lib/invoice/hooks/useInvoice.js index 0e612f2f..2de5e91e 100644 --- a/src/lib/invoice/hooks/useInvoice.js +++ b/src/lib/invoice/hooks/useInvoice.js @@ -1,5 +1,5 @@ -import { useQuery } from "react-query" -import invoiceApi from "../api/invoiceApi" +import { useQuery } from 'react-query' +import invoiceApi from '../api/invoiceApi' const useInvoice = ({ id }) => { const fetchInvoice = async () => await invoiceApi({ id }) @@ -10,4 +10,4 @@ const useInvoice = ({ id }) => { } } -export default useInvoice \ No newline at end of file +export default useInvoice diff --git a/src/lib/invoice/hooks/useInvoices.js b/src/lib/invoice/hooks/useInvoices.js index 7bcdc952..061626e4 100644 --- a/src/lib/invoice/hooks/useInvoices.js +++ b/src/lib/invoice/hooks/useInvoices.js @@ -1,6 +1,6 @@ -import { useQuery } from "react-query" -import invoicesApi from "../api/invoicesApi" -import _ from "lodash-contrib" +import { useQuery } from 'react-query' +import invoicesApi from '../api/invoicesApi' +import _ from 'lodash-contrib' const useInvoices = ({ query }) => { const queryString = _.toQuery(query) @@ -12,4 +12,4 @@ const useInvoices = ({ query }) => { } } -export default useInvoices \ No newline at end of file +export default useInvoices diff --git a/src/lib/invoice/utils/invoices.js b/src/lib/invoice/utils/invoices.js index c152191d..221e53cf 100644 --- a/src/lib/invoice/utils/invoices.js +++ b/src/lib/invoice/utils/invoices.js @@ -8,7 +8,4 @@ const downloadTaxInvoice = (invoice) => { window.open(url, 'download') } -export { - downloadInvoice, - downloadTaxInvoice -} \ No newline at end of file +export { downloadInvoice, downloadTaxInvoice } diff --git a/src/lib/product/api/productApi.js b/src/lib/product/api/productApi.js index a543f086..6fe8901e 100644 --- a/src/lib/product/api/productApi.js +++ b/src/lib/product/api/productApi.js @@ -1,8 +1,8 @@ -import odooApi from "@/core/api/odooApi" +import odooApi from '@/core/api/odooApi' const productApi = async ({ id }) => { const dataProduct = await odooApi('GET', `/api/v1/product/${id}`) return dataProduct } -export default productApi \ No newline at end of file +export default productApi diff --git a/src/lib/product/api/productSearchApi.js b/src/lib/product/api/productSearchApi.js index 86b2914f..b9acd94b 100644 --- a/src/lib/product/api/productSearchApi.js +++ b/src/lib/product/api/productSearchApi.js @@ -1,9 +1,9 @@ -import _ from "lodash-contrib" -import axios from "axios" +import _ from 'lodash-contrib' +import axios from 'axios' const productSearchApi = async ({ query }) => { const dataProductSearch = await axios(`${process.env.SELF_HOST}/api/shop/search?${query}`) return dataProductSearch.data } -export default productSearchApi \ No newline at end of file +export default productSearchApi diff --git a/src/lib/product/api/productSimilarApi.js b/src/lib/product/api/productSimilarApi.js index 1449d9ca..7142fab4 100644 --- a/src/lib/product/api/productSimilarApi.js +++ b/src/lib/product/api/productSimilarApi.js @@ -1,8 +1,10 @@ -import axios from "axios" +import axios from 'axios' const productSimilarApi = async ({ query }) => { - const dataProductSimilar = await axios(`${process.env.SELF_HOST}/api/shop/search?q=${query}&page=1&orderBy=popular`) + const dataProductSimilar = await axios( + `${process.env.SELF_HOST}/api/shop/search?q=${query}&page=1&orderBy=popular` + ) return dataProductSimilar.data.response } -export default productSimilarApi \ No newline at end of file +export default productSimilarApi diff --git a/src/lib/product/components/Product.jsx b/src/lib/product/components/Product.jsx index 92f4e37d..2181c38e 100644 --- a/src/lib/product/components/Product.jsx +++ b/src/lib/product/components/Product.jsx @@ -1,41 +1,40 @@ -import Badge from "@/core/components/elements/Badge/Badge" -import Divider from "@/core/components/elements/Divider/Divider" -import Image from "@/core/components/elements/Image/Image" -import Link from "@/core/components/elements/Link/Link" -import currencyFormat from "@/core/utils/currencyFormat" -import { useEffect, useState } from "react" -import Select from "react-select" -import ProductSimilar from "./ProductSimilar" -import LazyLoad from "react-lazy-load" -import { toast } from "react-hot-toast" -import { updateItemCart } from "@/core/utils/cart" +import Badge from '@/core/components/elements/Badge/Badge' +import Divider from '@/core/components/elements/Divider/Divider' +import Image from '@/core/components/elements/Image/Image' +import Link from '@/core/components/elements/Link/Link' +import currencyFormat from '@/core/utils/currencyFormat' +import { useEffect, useState } from 'react' +import Select from 'react-select' +import ProductSimilar from './ProductSimilar' +import LazyLoad from 'react-lazy-load' +import { toast } from 'react-hot-toast' +import { updateItemCart } from '@/core/utils/cart' const informationTabOptions = [ { value: 'specification', label: 'Spesifikasi' }, { value: 'description', label: 'Deskripsi' }, - { value: 'important', label: 'Info Penting' }, + { value: 'important', label: 'Info Penting' } ] const Product = ({ product }) => { - const [ quantity, setQuantity ] = useState('1') - const [ selectedVariant, setSelectedVariant ] = useState(null) - const [ informationTab, setInformationTab ] = useState(null) + const [quantity, setQuantity] = useState('1') + const [selectedVariant, setSelectedVariant] = useState(null) + const [informationTab, setInformationTab] = useState(null) - const [ activeVariant, setActiveVariant ] = useState({ + const [activeVariant, setActiveVariant] = useState({ id: product.id, code: product.code, name: product.name, price: product.lowestPrice, stock: product.stockTotal, - weight: product.weight, + weight: product.weight }) - + const variantOptions = product.variants?.map((variant) => ({ value: variant.id, - label: - (variant.code ? `[${variant.code}] ` : '') - + - (variant.attributes.length > 0 ? variant.attributes.join(', ') : product.name) + label: + (variant.code ? `[${variant.code}] ` : '') + + (variant.attributes.length > 0 ? variant.attributes.join(', ') : product.name) })) useEffect(() => { @@ -46,9 +45,10 @@ const Product = ({ product }) => { useEffect(() => { if (selectedVariant) { - const variant = product.variants.find(variant => variant.id == selectedVariant.value) - const variantAttributes = variant.attributes.length > 0 ? ' - ' + variant.attributes.join(', ') : '' - + const variant = product.variants.find((variant) => variant.id == selectedVariant.value) + const variantAttributes = + variant.attributes.length > 0 ? ' - ' + variant.attributes.join(', ') : '' + setActiveVariant({ id: variant.id, code: variant.code, @@ -87,76 +87,69 @@ const Product = ({ product }) => { {product.name} -
- { product.manufacture?.name } -

- {activeVariant?.name} -

- { activeVariant?.price?.discountPercentage > 0 && ( -
-
+
+ + {product.manufacture?.name} + +

{activeVariant?.name}

+ {activeVariant?.price?.discountPercentage > 0 && ( +
+
{currencyFormat(activeVariant?.price?.price)}
- - {activeVariant?.price?.discountPercentage}% - + {activeVariant?.price?.discountPercentage}%
- ) } -

- { activeVariant?.price?.priceDiscount > 0 ? currencyFormat(activeVariant?.price?.priceDiscount) : ( - + )} +

+ {activeVariant?.price?.priceDiscount > 0 ? ( + currencyFormat(activeVariant?.price?.priceDiscount) + ) : ( + Hubungi kami untuk dapatkan harga terbaik,  - klik disini + + klik disini + - ) } + )}

-
+
-
+
+
+ setQuantity(e.target.value)} />
- -
@@ -164,78 +157,70 @@ const Product = ({ product }) => { -
-

Informasi Produk

-
- { informationTabOptions.map((option) => ( - +

Informasi Produk

+
+ {informationTabOptions.map((option) => ( + setInformationTab(option.value)} > {option.label} - )) } + ))}
- - + {product?.variantTotal} Varian - + SKU-{product?.id} - + {activeVariant?.code || '-'} - - - { activeVariant?.stock > 0 && ( - -
Ready Stock
-
- { activeVariant?.stock > 5 ? '> 5' : '< 5' } -
+
+ + {activeVariant?.stock > 0 && ( + +
Ready Stock
+
{activeVariant?.stock > 5 ? '> 5' : '< 5'}
- ) } - { activeVariant?.stock == 0 && ( - + )} + {activeVariant?.stock == 0 && ( + Tanya Stok - ) } -
- - { activeVariant?.weight > 0 && ( - { activeVariant?.weight } KG - ) } - { activeVariant?.weight == 0 && ( - + )} + + + {activeVariant?.weight > 0 && {activeVariant?.weight} KG} + {activeVariant?.weight == 0 && ( + Tanya Berat - ) } -
+ )} +
-
-

Kamu Mungkin Juga Suka

+
+

Kamu Mungkin Juga Suka

@@ -247,30 +232,23 @@ const Product = ({ product }) => { const TabButton = ({ children, active, ...props }) => { const activeClassName = active ? 'text-red_r-11 underline underline-offset-4' : 'text-gray_r-11' return ( - ) } const TabContent = ({ children, active, className, ...props }) => ( -
- { children } +
+ {children}
) const SpecificationContent = ({ children, label }) => ( -
- { label } - { children } +
+ {label} + {children}
) -export default Product \ No newline at end of file +export default Product diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index 9eb00ae0..8a2f1d7f 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -1,68 +1,68 @@ -import Image from "@/core/components/elements/Image/Image" -import Link from "@/core/components/elements/Link/Link" -import currencyFormat from "@/core/utils/currencyFormat" -import { createSlug } from "@/core/utils/slug" +import Image from '@/core/components/elements/Image/Image' +import Link from '@/core/components/elements/Link/Link' +import currencyFormat from '@/core/utils/currencyFormat' +import { createSlug } from '@/core/utils/slug' const ProductCard = ({ product, simpleTitle }) => { return ( <> -
- + - {product?.name} - { product.variantTotal > 1 && ( -
{ product.variantTotal } Varian
- ) } + {product.variantTotal > 1 && ( +
+ {product.variantTotal} Varian +
+ )} -
- + {product?.manufacture?.name} - {product?.name} - { product?.lowestPrice?.discountPercentage > 0 && ( -
-
+ {product?.lowestPrice?.discountPercentage > 0 && ( +
+
{currencyFormat(product?.lowestPrice?.price)}
-
- {product?.lowestPrice?.discountPercentage}% -
+
{product?.lowestPrice?.discountPercentage}%
- ) } + )} -
- { product?.lowestPrice?.priceDiscount > 0 ? currencyFormat(product?.lowestPrice?.priceDiscount) : ( - Call for price - ) } +
+ {product?.lowestPrice?.priceDiscount > 0 ? ( + currencyFormat(product?.lowestPrice?.priceDiscount) + ) : ( + Call for price + )}
- { product?.stockTotal > 0 && ( -
-
- Ready Stock -
-
- { product?.stockTotal > 5 ? '> 5' : '< 5' } -
+ {product?.stockTotal > 0 && ( +
+
Ready Stock
+
{product?.stockTotal > 5 ? '> 5' : '< 5'}
- ) } + )}
) } -export default ProductCard \ No newline at end of file +export default ProductCard diff --git a/src/lib/product/components/ProductFilter.jsx b/src/lib/product/components/ProductFilter.jsx index 023b6a8b..d920cfb8 100644 --- a/src/lib/product/components/ProductFilter.jsx +++ b/src/lib/product/components/ProductFilter.jsx @@ -1,31 +1,24 @@ -import BottomPopup from "@/core/components/elements/Popup/BottomPopup" -import { useRouter } from "next/router" -import { useState } from "react" -import _ from "lodash" -import { toQuery } from "lodash-contrib" +import BottomPopup from '@/core/components/elements/Popup/BottomPopup' +import { useRouter } from 'next/router' +import { useState } from 'react' +import _ from 'lodash' +import { toQuery } from 'lodash-contrib' const orderOptions = [ { value: 'price-asc', label: 'Harga Terendah' }, { value: 'price-desc', label: 'Harga Tertinggi' }, { value: 'popular', label: 'Populer' }, - { value: 'stock', label: 'Ready Stock' }, + { value: 'stock', label: 'Ready Stock' } ] -const ProductFilter = ({ - active, - close, - brands, - categories, - prefixUrl, - defaultBrand = null -}) => { +const ProductFilter = ({ active, close, brands, categories, prefixUrl, defaultBrand = null }) => { const router = useRouter() const { query } = router - const [ order, setOrder ] = useState(query?.orderBy) - const [ brand, setBrand ] = useState(query?.brand) - const [ category, setCategory ] = useState(query?.category) - const [ priceFrom, setPriceFrom ] = useState(query?.priceFrom) - const [ priceTo, setPriceTo ] = useState(query?.priceTo) + const [order, setOrder] = useState(query?.orderBy) + const [brand, setBrand] = useState(query?.brand) + const [category, setCategory] = useState(query?.category) + const [priceFrom, setPriceFrom] = useState(query?.priceFrom) + const [priceTo, setPriceTo] = useState(query?.priceTo) const handleSubmit = () => { let params = { @@ -42,85 +35,79 @@ const ProductFilter = ({ } return ( - -
- { !defaultBrand && ( + +
+ {!defaultBrand && (
-
- ) } + )}
-
-
- { orderOptions.map((orderOption) => ( - - )) } + ))}
-
- + setPriceFrom(e.target.value)} /> - setPriceTo(e.target.value)} />
-
@@ -128,4 +115,4 @@ const ProductFilter = ({ ) } -export default ProductFilter \ No newline at end of file +export default ProductFilter diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx index 14df9864..25d0278f 100644 --- a/src/lib/product/components/ProductSearch.jsx +++ b/src/lib/product/components/ProductSearch.jsx @@ -1,21 +1,23 @@ -import { useEffect, useState } from "react" -import useProductSearch from "../hooks/useProductSearch" -import ProductCard from "./ProductCard" -import Pagination from "@/core/components/elements/Pagination/Pagination" -import { toQuery } from "lodash-contrib" -import _ from "lodash" -import ProductSearchSkeleton from "./Skeleton/ProductSearchSkeleton" -import ProductFilter from "./ProductFilter" -import useActive from "@/core/hooks/useActive" +import { useEffect, useState } from 'react' +import useProductSearch from '../hooks/useProductSearch' +import ProductCard from './ProductCard' +import Pagination from '@/core/components/elements/Pagination/Pagination' +import { toQuery } from 'lodash-contrib' +import _ from 'lodash' +import ProductSearchSkeleton from './Skeleton/ProductSearchSkeleton' +import ProductFilter from './ProductFilter' +import useActive from '@/core/hooks/useActive' const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { const { page = 1 } = query if (defaultBrand) query.brand = defaultBrand.toLowerCase() const { productSearch } = useProductSearch({ query }) - const [ products, setProducts ] = useState(null) + const [products, setProducts] = useState(null) const popup = useActive() - const pageCount = Math.ceil(productSearch.data?.response.numFound / productSearch.data?.responseHeader.params.rows) + const pageCount = Math.ceil( + productSearch.data?.response.numFound / productSearch.data?.responseHeader.params.rows + ) const productStart = productSearch.data?.responseHeader.params.start const productRows = productSearch.data?.responseHeader.params.rows const productFound = productSearch.data?.response.numFound @@ -25,11 +27,13 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { return true } }) - const categories = productSearch.data?.facetCounts?.facetFields?.categoryNameStr?.filter((value, index) => { - if (index % 2 === 0) { - return true + const categories = productSearch.data?.facetCounts?.facetFields?.categoryNameStr?.filter( + (value, index) => { + if (index % 2 === 0) { + return true + } } - }) + ) useEffect(() => { if (!products) { @@ -40,47 +44,55 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { if (productSearch.isLoading) { return } - + return ( -
-

Produk

- -
- { productFound > 0 ? ( +
+

Produk

+ +
+ {productFound > 0 ? ( <> Menampilkan  {pageCount > 1 ? ( <> - {productStart + 1}-{ - (productStart + productRows) > productFound ? productFound : productStart + productRows - } + {productStart + 1}- + {productStart + productRows > productFound + ? productFound + : productStart + productRows}  dari  - ) : ''} - { productFound } -  produk { query.q && (<>untuk pencarian { query.q }) } + ) : ( + '' + )} + {productFound} +  produk{' '} + {query.q && ( + <> + untuk pencarian {query.q} + + )} - ) : 'Mungkin yang anda cari'} + ) : ( + 'Mungkin yang anda cari' + )}
- -
- { products && products.map((product) => ( - - )) } +
+ {products && products.map((product) => )}
- { ) } -export default ProductSearch \ No newline at end of file +export default ProductSearch diff --git a/src/lib/product/components/ProductSimilar.jsx b/src/lib/product/components/ProductSimilar.jsx index 89cab536..63a33089 100644 --- a/src/lib/product/components/ProductSimilar.jsx +++ b/src/lib/product/components/ProductSimilar.jsx @@ -1,6 +1,6 @@ -import PopularProductSkeleton from "@/lib/home/components/Skeleton/PopularProductSkeleton" -import useProductSimilar from "../hooks/useProductSimilar" -import ProductSlider from "./ProductSlider" +import PopularProductSkeleton from '@/lib/home/components/Skeleton/PopularProductSkeleton' +import useProductSimilar from '../hooks/useProductSimilar' +import ProductSlider from './ProductSlider' const ProductSimilar = ({ query }) => { const { productSimilar } = useProductSimilar({ query }) @@ -12,4 +12,4 @@ const ProductSimilar = ({ query }) => { return } -export default ProductSimilar \ No newline at end of file +export default ProductSimilar diff --git a/src/lib/product/components/ProductSlider.jsx b/src/lib/product/components/ProductSlider.jsx index 8d677547..aafd3cf1 100644 --- a/src/lib/product/components/ProductSlider.jsx +++ b/src/lib/product/components/ProductSlider.jsx @@ -1,51 +1,48 @@ -import { Swiper, SwiperSlide } from "swiper/react" -import ProductCard from "./ProductCard" -import "swiper/css" -import Image from "@/core/components/elements/Image/Image" -import Link from "@/core/components/elements/Link/Link" -import { useState } from "react" +import { Swiper, SwiperSlide } from 'swiper/react' +import ProductCard from './ProductCard' +import 'swiper/css' +import Image from '@/core/components/elements/Image/Image' +import Link from '@/core/components/elements/Link/Link' +import { useState } from 'react' -const bannerClassName = 'absolute rounded-r top-0 left-0 h-full max-w-[52%] idt-transition border border-gray_r-6' +const bannerClassName = + 'absolute rounded-r top-0 left-0 h-full max-w-[52%] idt-transition border border-gray_r-6' -const ProductSlider = ({ - products, - simpleTitle = false, - bannerMode = false -}) => { - const [ activeIndex, setActiveIndex ] = useState(0) +const ProductSlider = ({ products, simpleTitle = false, bannerMode = false }) => { + const [activeIndex, setActiveIndex] = useState(0) const swiperSliderFirstMove = (swiper) => { setActiveIndex(swiper.activeIndex) } return ( <> - { bannerMode && ( - {products.banner.name} 0 ? 'opacity-0' : 'opacity-100'}`} + {bannerMode && ( + {products.banner.name} 0 ? 'opacity-0' : 'opacity-100'}`} /> - ) } - - { bannerMode && ( + {bannerMode && ( - + - ) } - { products?.products?.map((product, index) => ( + )} + {products?.products?.map((product, index) => ( - )) } + ))} ) } -export default ProductSlider \ No newline at end of file +export default ProductSlider diff --git a/src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx b/src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx index e51a565c..fa1e175d 100644 --- a/src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx +++ b/src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx @@ -1,7 +1,7 @@ -import ProductCardSkeleton from "@/core/components/elements/Skeleton/ProductCardSkeleton" +import ProductCardSkeleton from '@/core/components/elements/Skeleton/ProductCardSkeleton' const ProductSearchSkeleton = () => ( -
+
@@ -11,4 +11,4 @@ const ProductSearchSkeleton = () => (
) -export default ProductSearchSkeleton \ No newline at end of file +export default ProductSearchSkeleton diff --git a/src/lib/product/hooks/useProductSearch.js b/src/lib/product/hooks/useProductSearch.js index d23a8098..0396caec 100644 --- a/src/lib/product/hooks/useProductSearch.js +++ b/src/lib/product/hooks/useProductSearch.js @@ -1,6 +1,6 @@ -import { useQuery } from "react-query" -import productSearchApi from "../api/productSearchApi" -import _ from "lodash-contrib" +import { useQuery } from 'react-query' +import productSearchApi from '../api/productSearchApi' +import _ from 'lodash-contrib' const useProductSearch = ({ query }) => { const queryString = _.toQuery(query) @@ -12,4 +12,4 @@ const useProductSearch = ({ query }) => { } } -export default useProductSearch \ No newline at end of file +export default useProductSearch diff --git a/src/lib/product/hooks/useProductSimilar.js b/src/lib/product/hooks/useProductSimilar.js index 444fec0b..d16e4c58 100644 --- a/src/lib/product/hooks/useProductSimilar.js +++ b/src/lib/product/hooks/useProductSimilar.js @@ -1,5 +1,5 @@ -import productSimilarApi from "../api/productSimilarApi" -import { useQuery } from "react-query" +import productSimilarApi from '../api/productSimilarApi' +import { useQuery } from 'react-query' const useProductSimilar = ({ query }) => { const fetchProductSimilar = async () => await productSimilarApi({ query }) @@ -10,4 +10,4 @@ const useProductSimilar = ({ query }) => { } } -export default useProductSimilar \ No newline at end of file +export default useProductSimilar diff --git a/src/lib/transaction/api/cancelTransactionApi.js b/src/lib/transaction/api/cancelTransactionApi.js index cd1798b5..1bba2bde 100644 --- a/src/lib/transaction/api/cancelTransactionApi.js +++ b/src/lib/transaction/api/cancelTransactionApi.js @@ -1,10 +1,13 @@ -import odooApi from "@/core/api/odooApi" -import { getAuth } from "@/core/utils/auth" +import odooApi from '@/core/api/odooApi' +import { getAuth } from '@/core/utils/auth' const cancelTransactionApi = async ({ transaction }) => { const auth = getAuth() - const dataCancelTransaction = await odooApi('POST', `/api/v1/partner/${auth.partnerId}/sale_order/${transaction.id}/cancel`) + const dataCancelTransaction = await odooApi( + 'POST', + `/api/v1/partner/${auth.partnerId}/sale_order/${transaction.id}/cancel` + ) return dataCancelTransaction } -export default cancelTransactionApi \ No newline at end of file +export default cancelTransactionApi diff --git a/src/lib/transaction/api/checkoutPoApi.js b/src/lib/transaction/api/checkoutPoApi.js index aed43cff..04421368 100644 --- a/src/lib/transaction/api/checkoutPoApi.js +++ b/src/lib/transaction/api/checkoutPoApi.js @@ -1,10 +1,13 @@ -import odooApi from "@/core/api/odooApi" -import { getAuth } from "@/core/utils/auth" +import odooApi from '@/core/api/odooApi' +import { getAuth } from '@/core/utils/auth' const checkoutPoApi = async ({ id }) => { const auth = getAuth() - const dataCheckout = await odooApi('POST', `/api/v1/partner/${auth?.partnerId}/sale_order/${id}/checkout`) + const dataCheckout = await odooApi( + 'POST', + `/api/v1/partner/${auth?.partnerId}/sale_order/${id}/checkout` + ) return dataCheckout } -export default checkoutPoApi \ No newline at end of file +export default checkoutPoApi diff --git a/src/lib/transaction/api/transactionApi.js b/src/lib/transaction/api/transactionApi.js index 7186f847..e7c4c23f 100644 --- a/src/lib/transaction/api/transactionApi.js +++ b/src/lib/transaction/api/transactionApi.js @@ -1,5 +1,5 @@ -import odooApi from "@/core/api/odooApi" -import { getAuth } from "@/core/utils/auth" +import odooApi from '@/core/api/odooApi' +import { getAuth } from '@/core/utils/auth' const transactionApi = async ({ id }) => { const auth = getAuth() @@ -7,4 +7,4 @@ const transactionApi = async ({ id }) => { return dataTransaction } -export default transactionApi \ No newline at end of file +export default transactionApi diff --git a/src/lib/transaction/api/transactionsApi.js b/src/lib/transaction/api/transactionsApi.js index 995b4c5c..f4e36e6f 100644 --- a/src/lib/transaction/api/transactionsApi.js +++ b/src/lib/transaction/api/transactionsApi.js @@ -1,10 +1,13 @@ -import odooApi from "@/core/api/odooApi" -import { getAuth } from "@/core/utils/auth" +import odooApi from '@/core/api/odooApi' +import { getAuth } from '@/core/utils/auth' const transactionsApi = async ({ query }) => { const auth = getAuth() - const dataTransactions = await odooApi('GET', `/api/v1/partner/${auth.partnerId}/sale_order?${query}`) + const dataTransactions = await odooApi( + 'GET', + `/api/v1/partner/${auth.partnerId}/sale_order?${query}` + ) return dataTransactions } -export default transactionsApi \ No newline at end of file +export default transactionsApi diff --git a/src/lib/transaction/api/uploadPoApi.js b/src/lib/transaction/api/uploadPoApi.js index 00ad1d8d..7feeff66 100644 --- a/src/lib/transaction/api/uploadPoApi.js +++ b/src/lib/transaction/api/uploadPoApi.js @@ -1,10 +1,14 @@ -import odooApi from "@/core/api/odooApi" -import { getAuth } from "@/core/utils/auth" +import odooApi from '@/core/api/odooApi' +import { getAuth } from '@/core/utils/auth' const uploadPoApi = async ({ id, data }) => { const auth = getAuth() - const dataUploadPo = await odooApi('POST', `/api/v1/partner/${auth.partnerId}/sale_order/${id}/upload_po`, data) + const dataUploadPo = await odooApi( + 'POST', + `/api/v1/partner/${auth.partnerId}/sale_order/${id}/upload_po`, + data + ) return dataUploadPo } -export default uploadPoApi \ No newline at end of file +export default uploadPoApi diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index 0017afba..2220f3be 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -1,27 +1,27 @@ -import Spinner from "@/core/components/elements/Spinner/Spinner" -import useTransaction from "../hooks/useTransaction" -import TransactionStatusBadge from "./TransactionStatusBadge" -import Divider from "@/core/components/elements/Divider/Divider" -import { useRef, useState } from "react" -import { downloadPurchaseOrder, downloadQuotation } from "../utils/transactions" -import BottomPopup from "@/core/components/elements/Popup/BottomPopup" -import uploadPoApi from "../api/uploadPoApi" -import { toast } from "react-hot-toast" -import getFileBase64 from "@/core/utils/getFileBase64" -import currencyFormat from "@/core/utils/currencyFormat" -import VariantGroupCard from "@/lib/variant/components/VariantGroupCard" -import { ChevronDownIcon, ChevronRightIcon, ChevronUpIcon } from "@heroicons/react/24/outline" -import Link from "@/core/components/elements/Link/Link" -import Alert from "@/core/components/elements/Alert/Alert" -import checkoutPoApi from "../api/checkoutPoApi" -import cancelTransactionApi from "../api/cancelTransactionApi" +import Spinner from '@/core/components/elements/Spinner/Spinner' +import useTransaction from '../hooks/useTransaction' +import TransactionStatusBadge from './TransactionStatusBadge' +import Divider from '@/core/components/elements/Divider/Divider' +import { useRef, useState } from 'react' +import { downloadPurchaseOrder, downloadQuotation } from '../utils/transactions' +import BottomPopup from '@/core/components/elements/Popup/BottomPopup' +import uploadPoApi from '../api/uploadPoApi' +import { toast } from 'react-hot-toast' +import getFileBase64 from '@/core/utils/getFileBase64' +import currencyFormat from '@/core/utils/currencyFormat' +import VariantGroupCard from '@/lib/variant/components/VariantGroupCard' +import { ChevronDownIcon, ChevronRightIcon, ChevronUpIcon } from '@heroicons/react/24/outline' +import Link from '@/core/components/elements/Link/Link' +import Alert from '@/core/components/elements/Alert/Alert' +import checkoutPoApi from '../api/checkoutPoApi' +import cancelTransactionApi from '../api/cancelTransactionApi' const Transaction = ({ id }) => { const { transaction } = useTransaction({ id }) const poNumber = useRef('') const poFile = useRef('') - const [ uploadPo, setUploadPo ] = useState(false) + const [uploadPo, setUploadPo] = useState(false) const openUploadPo = () => setUploadPo(true) const closeUploadPo = () => setUploadPo(false) const submitUploadPo = async () => { @@ -46,7 +46,7 @@ const Transaction = ({ id }) => { toast.error('Terjadi kesalahan internal, coba lagi nanti atau hubungi kami') } - const [ cancelTransaction, setCancelTransaction ] = useState(false) + const [cancelTransaction, setCancelTransaction] = useState(false) const openCancelTransaction = () => setCancelTransaction(true) const closeCancelTransaction = () => setCancelTransaction(false) const submitCancelTransaction = async () => { @@ -70,191 +70,174 @@ const Transaction = ({ id }) => { if (transaction.isLoading) { return ( -
- +
+
) } - return transaction.data?.name && ( - <> -
- -
- -
-
- - { transaction.data?.name } - - - { transaction.data?.paymentTerm } - - - { transaction.data?.sales } - - - { transaction.data?.dateOrder } - -
- - + return ( + transaction.data?.name && ( + <> +
+ +
+ +
+
+ {transaction.data?.name} + + {transaction.data?.paymentTerm} + + {transaction.data?.sales} + {transaction.data?.dateOrder} +
-
- - { transaction.data?.purchaseOrderName || '-' } - -
-

Dokumen PO

- + + +
+ + {transaction.data?.purchaseOrderName || '-'} + +
+

Dokumen PO

+ +
-
- + -
Detail Produk
+
Detail Produk
-
- -
-

Total Belanja

-

{ currencyFormat(transaction.data?.amountTotal) }

+
+ +
+

Total Belanja

+

{currencyFormat(transaction.data?.amountTotal)}

+
-
- - - + - + + + -
-

Invoice

-
- { transaction.data?.invoices?.map((invoice, index) => ( - -
-
-

{ invoice?.name }

-
- { invoice.amountResidual > 0 ? ( -
Belum Lunas
+
+

Invoice

+
+ {transaction.data?.invoices?.map((invoice, index) => ( + +
+
+

{invoice?.name}

+
+ {invoice.amountResidual > 0 ? ( +
Belum Lunas
) : ( -
Lunas
- ) } -

- { currencyFormat(invoice.amountTotal) } -

+
Lunas
+ )} +

+ {currencyFormat(invoice.amountTotal)} +

+
+
- -
- - )) } - { transaction.data?.invoices?.length === 0 && ( - - Belum ada Invoice - - ) } + + ))} + {transaction.data?.invoices?.length === 0 && ( + + Belum ada Invoice + + )} +
-
- + -
- { transaction.data?.status == 'draft' && ( - + )} + - ) } - + )} +
+ + - Download Quotation - - { transaction.data?.status != 'draft' && ( - - ) } -
+
+ Apakah anda yakin membatalkan transaksi{' '} + {transaction.data?.name}? +
+
+ + +
+ - -
- Apakah anda yakin membatalkan transaksi {transaction.data?.name}? -
-
- - -
-
- - -
- - -
-
- - -
-
- - -
-
- + +
+ + +
+
+ + +
+
+ + +
+
+ + ) ) } const SectionAddress = ({ address }) => { - const [ section, setSection ] = useState({ + const [section, setSection] = useState({ customer: false, invoice: false, shipping: false @@ -265,44 +248,40 @@ const SectionAddress = ({ address }) => { return ( <> - toggleSection('customer')} /> - { section.customer && } + {section.customer && } - - toggleSection('shipping')} /> - { section.shipping && } + {section.shipping && } - - toggleSection('invoice')} /> - { section.invoice && } + {section.invoice && } ) } const SectionButton = ({ label, active, toggle }) => ( - ) @@ -315,28 +294,20 @@ const SectionContent = ({ address }) => { fullAddress = fullAddress.join(', ') return ( -
- - { address.name } - - - { address.email || '-' } - - - { address.mobile || '-' } - - - { fullAddress } - +
+ {address.name} + {address.email || '-'} + {address.mobile || '-'} + {fullAddress}
) } const DescriptionRow = ({ children, label }) => ( -
- { label } - { children } +
+ {label} + {children}
) -export default Transaction \ No newline at end of file +export default Transaction diff --git a/src/lib/transaction/components/TransactionStatusBadge.jsx b/src/lib/transaction/components/TransactionStatusBadge.jsx index 28fe714a..7372e4da 100644 --- a/src/lib/transaction/components/TransactionStatusBadge.jsx +++ b/src/lib/transaction/components/TransactionStatusBadge.jsx @@ -35,11 +35,7 @@ const TransactionStatusBadge = ({ status }) => { } badgeProps.className = badgeProps.className.join(' ') - return ( -
- { badgeProps.text } -
- ) + return
{badgeProps.text}
} -export default TransactionStatusBadge \ No newline at end of file +export default TransactionStatusBadge diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index 280e8fc5..f582319d 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -1,27 +1,24 @@ -import { useRouter } from "next/router" -import { useState } from "react" -import { toast } from "react-hot-toast" -import { EllipsisVerticalIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline" - -import { downloadPurchaseOrder, downloadQuotation } from "../utils/transactions" -import useTransactions from "../hooks/useTransactions" -import currencyFormat from "@/core/utils/currencyFormat" -import cancelTransactionApi from "../api/cancelTransactionApi" -import TransactionStatusBadge from "./TransactionStatusBadge" -import Spinner from "@/core/components/elements/Spinner/Spinner" -import Link from "@/core/components/elements/Link/Link" -import BottomPopup from "@/core/components/elements/Popup/BottomPopup" -import Pagination from "@/core/components/elements/Pagination/Pagination" -import { toQuery } from "lodash-contrib" -import _ from "lodash" -import Alert from "@/core/components/elements/Alert/Alert" +import { useRouter } from 'next/router' +import { useState } from 'react' +import { toast } from 'react-hot-toast' +import { EllipsisVerticalIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline' + +import { downloadPurchaseOrder, downloadQuotation } from '../utils/transactions' +import useTransactions from '../hooks/useTransactions' +import currencyFormat from '@/core/utils/currencyFormat' +import cancelTransactionApi from '../api/cancelTransactionApi' +import TransactionStatusBadge from './TransactionStatusBadge' +import Spinner from '@/core/components/elements/Spinner/Spinner' +import Link from '@/core/components/elements/Link/Link' +import BottomPopup from '@/core/components/elements/Popup/BottomPopup' +import Pagination from '@/core/components/elements/Pagination/Pagination' +import { toQuery } from 'lodash-contrib' +import _ from 'lodash' +import Alert from '@/core/components/elements/Alert/Alert' const Transactions = () => { const router = useRouter() - const { - q = '', - page = 1 - } = router.query + const { q = '', page = 1 } = router.query const limit = 10 @@ -32,9 +29,9 @@ const Transactions = () => { } const { transactions } = useTransactions({ query }) - const [ inputQuery, setInputQuery ] = useState(q) - const [ toOthers, setToOthers ] = useState(null) - const [ toCancel, setToCancel ] = useState(null) + const [inputQuery, setInputQuery] = useState(q) + const [toOthers, setToOthers] = useState(null) + const [toCancel, setToCancel] = useState(null) const submitCancelTransaction = async () => { const isCancelled = await cancelTransactionApi({ @@ -55,129 +52,128 @@ const Transactions = () => { const handleSubmit = (e) => { e.preventDefault() router.push(`/my/transactions?q=${inputQuery}`) - } + } return ( -
-
- + + setInputQuery(e.target.value)} /> -
- - { transactions.isLoading && ( -
- + + {transactions.isLoading && ( +
+
- ) } + )} - { !transactions.isLoading && transactions.data?.saleOrders?.length === 0 && ( - + {!transactions.isLoading && transactions.data?.saleOrders?.length === 0 && ( + Tidak ada data transaksi - ) } + )} - { transactions.data?.saleOrders?.map((saleOrder, index) => ( -
-
+ {transactions.data?.saleOrders?.map((saleOrder, index) => ( +
+
- No. Transaksi -

{ saleOrder.name }

+ No. Transaksi +

{saleOrder.name}

-
+
- setToOthers(saleOrder)} /> + setToOthers(saleOrder)} />
-
+
- No. Purchase Order -

{ saleOrder.purchaseOrderName || '-' }

+ No. Purchase Order +

+ {saleOrder.purchaseOrderName || '-'} +

-
- Total Invoice -

{ saleOrder.invoiceCount } Invoice

+
+ Total Invoice +

{saleOrder.invoiceCount} Invoice

-
+
- Sales -

{ saleOrder.sales }

+ Sales +

{saleOrder.sales}

-
- Total Harga -

{ currencyFormat(saleOrder.amountTotal) }

+
+ Total Harga +

+ {currencyFormat(saleOrder.amountTotal)} +

- )) } - + ))} + - setToOthers(null)}> -
- - -
- setToCancel(null)} - title="Batalkan Transaksi" - > -
- Apakah anda yakin membatalkan transaksi {toCancel?.name}? + setToCancel(null)} title='Batalkan Transaksi'> +
+ Apakah anda yakin membatalkan transaksi{' '} + {toCancel?.name}?
-
- -
@@ -186,4 +182,4 @@ const Transactions = () => { ) } -export default Transactions \ No newline at end of file +export default Transactions diff --git a/src/lib/transaction/hooks/useTransaction.js b/src/lib/transaction/hooks/useTransaction.js index f2b493ee..6dda0573 100644 --- a/src/lib/transaction/hooks/useTransaction.js +++ b/src/lib/transaction/hooks/useTransaction.js @@ -1,5 +1,5 @@ -import { useQuery } from "react-query" -import transactionApi from "../api/transactionApi" +import { useQuery } from 'react-query' +import transactionApi from '../api/transactionApi' const useTransaction = ({ id }) => { const fetchTransaction = async () => await transactionApi({ id }) @@ -10,4 +10,4 @@ const useTransaction = ({ id }) => { } } -export default useTransaction \ No newline at end of file +export default useTransaction diff --git a/src/lib/transaction/hooks/useTransactions.js b/src/lib/transaction/hooks/useTransactions.js index 3f67fd3e..5b40a05a 100644 --- a/src/lib/transaction/hooks/useTransactions.js +++ b/src/lib/transaction/hooks/useTransactions.js @@ -1,6 +1,6 @@ -import { useQuery } from "react-query" -import transactionsApi from "../api/transactionsApi" -import _ from "lodash-contrib" +import { useQuery } from 'react-query' +import transactionsApi from '../api/transactionsApi' +import _ from 'lodash-contrib' const useTransactions = ({ query }) => { const queryString = _.toQuery(query) @@ -12,4 +12,4 @@ const useTransactions = ({ query }) => { } } -export default useTransactions \ No newline at end of file +export default useTransactions diff --git a/src/lib/transaction/utils/transactions.js b/src/lib/transaction/utils/transactions.js index 03d4dbd4..4c7522be 100644 --- a/src/lib/transaction/utils/transactions.js +++ b/src/lib/transaction/utils/transactions.js @@ -1,4 +1,4 @@ -import { getAuth } from "@/core/utils/auth" +import { getAuth } from '@/core/utils/auth' const downloadPurchaseOrder = (transaction) => { const auth = getAuth() @@ -12,7 +12,4 @@ const downloadQuotation = (transaction) => { window.open(url, 'download') } -export { - downloadPurchaseOrder, - downloadQuotation -} \ No newline at end of file +export { downloadPurchaseOrder, downloadQuotation } diff --git a/src/lib/variant/components/VariantCard.jsx b/src/lib/variant/components/VariantCard.jsx index 6c7ab22f..6e7ea871 100644 --- a/src/lib/variant/components/VariantCard.jsx +++ b/src/lib/variant/components/VariantCard.jsx @@ -1,17 +1,13 @@ -import { useRouter } from "next/router" -import { toast } from "react-hot-toast" +import { useRouter } from 'next/router' +import { toast } from 'react-hot-toast' -import Image from "@/core/components/elements/Image/Image" -import Link from "@/core/components/elements/Link/Link" -import { createSlug } from "@/core/utils/slug" -import currencyFormat from "@/core/utils/currencyFormat" -import { updateItemCart } from "@/core/utils/cart" +import Image from '@/core/components/elements/Image/Image' +import Link from '@/core/components/elements/Link/Link' +import { createSlug } from '@/core/utils/slug' +import currencyFormat from '@/core/utils/currencyFormat' +import { updateItemCart } from '@/core/utils/cart' -const VariantCard = ({ - product, - openOnClick = true, - buyMore = false -}) => { +const VariantCard = ({ product, openOnClick = true, buyMore = false }) => { const router = useRouter() const addItemToCart = () => { @@ -22,41 +18,43 @@ const VariantCard = ({ }) return } - + const checkoutItem = () => { router.push(`/shop/checkout?product_id=${product.id}&qty=${product.quantity}`) } const Card = () => ( -
-
+
+
{product.parent.name}
-
-

- {product.parent.name} -

-

+

+

{product.parent.name}

+

{product.code || '-'} {product.attributes.length > 0 ? ` ・ ${product.attributes.join(', ')}` : ''}

-
+
{product.price.discountPercentage > 0 && ( <> -

{currencyFormat(product.price.price)}

- {product.price.discountPercentage}% +

+ {currencyFormat(product.price.price)} +

+ {product.price.discountPercentage}% )} -

{currencyFormat(product.price.priceDiscount)}

+

+ {currencyFormat(product.price.priceDiscount)} +

-

+

{currencyFormat(product.price.priceDiscount)} × {product.quantity} Barang

-

+

{currencyFormat(product.quantity * product.price.priceDiscount)}

@@ -69,29 +67,29 @@ const VariantCard = ({ - { buyMore && ( -
- -
- ) } + )} ) } - return + return } -export default VariantCard \ No newline at end of file +export default VariantCard diff --git a/src/lib/variant/components/VariantGroupCard.jsx b/src/lib/variant/components/VariantGroupCard.jsx index fd4f9b4d..8cb1eec4 100644 --- a/src/lib/variant/components/VariantGroupCard.jsx +++ b/src/lib/variant/components/VariantGroupCard.jsx @@ -1,33 +1,26 @@ -import { useState } from "react" -import VariantCard from "./VariantCard" +import { useState } from 'react' +import VariantCard from './VariantCard' -const VariantGroupCard = ({ - variants, - ...props -}) => { - const [ showAll, setShowAll ] = useState(false) +const VariantGroupCard = ({ variants, ...props }) => { + const [showAll, setShowAll] = useState(false) const variantsToShow = showAll ? variants : variants.slice(0, 2) return ( <> - { variantsToShow?.map((variant, index) => ( - - )) } - { variants.length > 2 && ( - - ) } + )} ) } -export default VariantGroupCard \ No newline at end of file +export default VariantGroupCard diff --git a/src/lib/wishlist/api/wishlistsApi.js b/src/lib/wishlist/api/wishlistsApi.js index 49ef56ee..dfcce028 100644 --- a/src/lib/wishlist/api/wishlistsApi.js +++ b/src/lib/wishlist/api/wishlistsApi.js @@ -1,10 +1,13 @@ -import odooApi from "@/core/api/odooApi" -import { getAuth } from "@/core/utils/auth" +import odooApi from '@/core/api/odooApi' +import { getAuth } from '@/core/utils/auth' const wishlistsApi = async ({ limit, offset }) => { const auth = getAuth() - const dataWishlists = await odooApi('GET', `/api/v1/user/${auth.id}/wishlist?limit=${limit}&offset=${offset}`) + const dataWishlists = await odooApi( + 'GET', + `/api/v1/user/${auth.id}/wishlist?limit=${limit}&offset=${offset}` + ) return dataWishlists } -export default wishlistsApi \ No newline at end of file +export default wishlistsApi diff --git a/src/lib/wishlist/components/Wishlists.jsx b/src/lib/wishlist/components/Wishlists.jsx index 8cbbb0a2..71ac095e 100644 --- a/src/lib/wishlist/components/Wishlists.jsx +++ b/src/lib/wishlist/components/Wishlists.jsx @@ -1,15 +1,13 @@ -import Alert from "@/core/components/elements/Alert/Alert" -import Pagination from "@/core/components/elements/Pagination/Pagination" -import Spinner from "@/core/components/elements/Spinner/Spinner" -import ProductCard from "@/lib/product/components/ProductCard" -import { useRouter } from "next/router" -import useWishlists from "../hooks/useWishlists" +import Alert from '@/core/components/elements/Alert/Alert' +import Pagination from '@/core/components/elements/Pagination/Pagination' +import Spinner from '@/core/components/elements/Spinner/Spinner' +import ProductCard from '@/lib/product/components/ProductCard' +import { useRouter } from 'next/router' +import useWishlists from '../hooks/useWishlists' const Wishlists = () => { const router = useRouter() - const { - page = 1 - } = router.query + const { page = 1 } = router.query const limit = 30 const { wishlists } = useWishlists({ page, limit }) @@ -17,31 +15,31 @@ const Wishlists = () => { if (wishlists.isLoading) { return ( -
- +
+
) } return ( -
- { wishlists.data?.products?.length == 0 && ( +
+ {wishlists.data?.products?.length == 0 && ( Wishlist anda masih kosong - ) } - -
+ )} + +
{wishlists.data?.products.map((product) => ( ))}
-
+
) } -export default Wishlists \ No newline at end of file +export default Wishlists diff --git a/src/lib/wishlist/hooks/useWishlists.js b/src/lib/wishlist/hooks/useWishlists.js index a219ab69..169fdf46 100644 --- a/src/lib/wishlist/hooks/useWishlists.js +++ b/src/lib/wishlist/hooks/useWishlists.js @@ -1,5 +1,5 @@ -import { useQuery } from "react-query" -import wishlistsApi from "../api/wishlistsApi" +import { useQuery } from 'react-query' +import wishlistsApi from '../api/wishlistsApi' const useWishlists = ({ page, limit }) => { const offset = (page - 1) * limit @@ -11,4 +11,4 @@ const useWishlists = ({ page, limit }) => { } } -export default useWishlists \ No newline at end of file +export default useWishlists -- cgit v1.2.3 From 757b69f4d814ec4890c209f7a9fdf3d9940157d9 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 22 Feb 2023 12:03:05 +0700 Subject: fix --- src/lib/checkout/components/Checkout.jsx | 272 +++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 src/lib/checkout/components/Checkout.jsx (limited to 'src/lib') diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx new file mode 100644 index 00000000..0a3949c3 --- /dev/null +++ b/src/lib/checkout/components/Checkout.jsx @@ -0,0 +1,272 @@ +import Alert from '@/core/components/elements/Alert/Alert' +import Divider from '@/core/components/elements/Divider/Divider' +import Link from '@/core/components/elements/Link/Link' +import useAuth from '@/core/hooks/useAuth' +import { getItemAddress } from '@/core/utils/address' +import addressesApi from '@/lib/address/api/addressesApi' +import CartApi from '@/lib/cart/api/CartApi' +import VariantCard from '@/lib/variant/components/VariantCard' +import { ExclamationCircleIcon } from '@heroicons/react/24/outline' +import { useEffect, useRef, useState } from 'react' +import _ from 'lodash' +import { getCart, getItemCart } from '@/core/utils/cart' +import currencyFormat from '@/core/utils/currencyFormat' +import { toast } from 'react-hot-toast' + +const Checkout = () => { + const auth = useAuth() + const [selectedAddress, setSelectedAddress] = useState({ + shipping: null, + invoicing: null + }) + const [addresses, setAddresses] = useState(null) + + useEffect(() => { + if (!auth) return + + const getAddresses = async () => { + const dataAddresses = await addressesApi() + setAddresses(dataAddresses) + } + + getAddresses() + }, [auth]) + + useEffect(() => { + if (!addresses) return + + const matchAddress = (key) => { + const addressToMatch = getItemAddress(key) + const foundAddress = addresses.filter((address) => address.id == addressToMatch) + if (foundAddress.length > 0) { + return foundAddress[0] + } + return addresses[0] + } + + setSelectedAddress({ + shipping: matchAddress('shipping'), + invoicing: matchAddress('invoicing') + }) + }, [addresses]) + + const [products, setProducts] = useState(null) + const [totalAmount, setTotalAmount] = useState(0) + const [totalDiscountAmount, setTotalDiscountAmount] = useState(0) + + useEffect(() => { + const loadProducts = async () => { + const cart = getCart() + const variantIds = _.filter(cart, (o) => o.selected == true) + .map((o) => o.productId) + .join(',') + const dataProducts = await CartApi({ variantIds }) + const dataProductsQuantity = _.map(dataProducts, (o) => ({ + ...o, + quantity: getItemCart({ productId: o.id }).quantity + })) + setProducts(dataProductsQuantity) + } + loadProducts() + }, []) + + useEffect(() => { + if (products) { + let calculateTotalAmount = 0 + let calculateTotalDiscountAmount = 0 + products.forEach((product) => { + calculateTotalAmount += product.price.price * product.quantity + calculateTotalDiscountAmount += + (product.price.price - product.price.priceDiscount) * product.quantity + }) + setTotalAmount(calculateTotalAmount) + setTotalDiscountAmount(calculateTotalDiscountAmount) + } + }, [products]) + + const [selectedPayment, setSelectedPayment] = useState(null) + + const poNumber = useRef('') + const poFile = useRef('') + + const [isLoading, setIsLoading] = useState(false) + + const checkout = async () => { + if (!selectedPayment) { + toast.error('Pilih metode pembayaran', { position: 'bottom-center' }) + return + } + } + + return ( + <> +
+ +
+ +
+ + Jika mengalami kesulitan dalam melakukan pembelian di website Indoteknik. Hubungi kami + disini + +
+
+ + + + + + + +
+ {products?.map((product) => ( + + ))} +
+ + + +
+
+
Ringkasan Pesanan
+
{products?.length} Barang
+
+
+
+
+
Total Belanja
+
{currencyFormat(totalAmount)}
+
+
+
Total Diskon
+
- {currencyFormat(totalDiscountAmount)}
+
+
+
Subtotal
+
{currencyFormat(totalAmount - totalDiscountAmount)}
+
+
+
PPN 11% (Incl.)
+
{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}
+
+
+
+
+
Grand Total
+
+ {currencyFormat(totalAmount - totalDiscountAmount)} +
+
+

*) Belum termasuk biaya pengiriman

+

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

+
+ + + + + + + +
+
+ Metode Pembayaran (Wajib dipilih) +
+
+ {payments.map((payment, index) => ( + + ))} +
+
+ + + +
+
Purchase Order
+ +
+
+ + +
+
+ + +
+
+

Ukuran dokumen PO Maksimal 5MB

+
+ + + +
+ +
+ + ) +} + +const payments = [ + { name: 'BCA', number: '8870-4000-81' }, + { name: 'MANDIRI', number: '155-0067-6869-75' } +] + +const SectionAddress = ({ address, label, url }) => ( +
+
+
{label}
+ + Pilih Alamat Lain + +
+ + {address && ( +
+
+ {address.type.charAt(0).toUpperCase() + address.type.slice(1) + ' Address'} +
+

{address.name}

+

{address.mobile}

+

+ {address.street}, {address?.city?.name} +

+
+ )} +
+) + +export default Checkout -- cgit v1.2.3 From 7265295454801c1d921385a4b67fb3780b46771e Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 22 Feb 2023 14:00:00 +0700 Subject: fix --- src/lib/address/components/Addresses.jsx | 12 ++------- src/lib/cart/components/Cart.jsx | 8 ++++++ src/lib/checkout/api/checkoutApi.js | 14 ++++++++++ src/lib/checkout/components/Checkout.jsx | 36 +++++++++++++++++++++++--- src/lib/checkout/components/FinishCheckout.jsx | 30 +++++++++++++++++++++ src/lib/product/components/ProductCard.jsx | 2 +- src/lib/transaction/components/Transaction.jsx | 2 +- 7 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 src/lib/checkout/api/checkoutApi.js create mode 100644 src/lib/checkout/components/FinishCheckout.jsx (limited to 'src/lib') diff --git a/src/lib/address/components/Addresses.jsx b/src/lib/address/components/Addresses.jsx index c4855f8f..3ac06b6c 100644 --- a/src/lib/address/components/Addresses.jsx +++ b/src/lib/address/components/Addresses.jsx @@ -40,7 +40,7 @@ const Addresses = () => { className={ 'p-4 rounded-md border ' + (selectedAdress && selectedAdress == address.id - ? 'border-gray_r-7 bg-gray_r-2' + ? 'border-gray_r-7 bg-gray_r-4' : 'border-gray_r-7') } > @@ -51,15 +51,7 @@ const Addresses = () => {

{address.name}

{address.mobile &&

{address.mobile}

} -

- {address.street} -

+

{address.street}

{ const router = useRouter() @@ -126,6 +127,13 @@ const Cart = () => {
)} + + {!cart.isLoading && !products && ( + + Keranjang belanja anda masih kosong + + )} + {products?.map((product) => (
diff --git a/src/lib/checkout/components/FinishCheckout.jsx b/src/lib/checkout/components/FinishCheckout.jsx new file mode 100644 index 00000000..f0aaba4e --- /dev/null +++ b/src/lib/checkout/components/FinishCheckout.jsx @@ -0,0 +1,30 @@ +import Link from "@/core/components/elements/Link/Link" +import useTransaction from "@/lib/transaction/hooks/useTransaction" + +const FinishCheckout = ({ id }) => { + const { transaction } = useTransaction({ id }) + + return ( +
+
+
+

Terima Kasih atas Pembelian Anda

+

+ Rincian belanja sudah kami kirimkan ke email anda. Mohon dicek kembali. jika tidak + menerima email, anda dapat menghubungi kami disini. +

+

{transaction.data?.name}

+

No. Transaksi

+
+ + Lihat detail pembelian Anda disini + +
+
+ ) +} + +export default FinishCheckout \ No newline at end of file diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index 8a2f1d7f..0fe75c56 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -6,7 +6,7 @@ import { createSlug } from '@/core/utils/slug' const ProductCard = ({ product, simpleTitle }) => { return ( <> -
+
( const SectionContent = ({ address }) => { let fullAddress = [] if (address?.street) fullAddress.push(address.street) - if (address?.subDistrict?.name) fullAddress.push(address.sub_district.name) + if (address?.subDistrict?.name) fullAddress.push(address.subDistrict.name) if (address?.district?.name) fullAddress.push(address.district.name) if (address?.city?.name) fullAddress.push(address.city.name) fullAddress = fullAddress.join(', ') -- cgit v1.2.3 From 6e9b5666556fb09bb7ab92e2c62c294431751403 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 22 Feb 2023 14:02:28 +0700 Subject: fix --- src/lib/product/components/ProductCard.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/lib') diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index 0fe75c56..dd221e4f 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -6,7 +6,7 @@ import { createSlug } from '@/core/utils/slug' const ProductCard = ({ product, simpleTitle }) => { return ( <> -
+
{
)} -
+
Date: Wed, 22 Feb 2023 14:42:23 +0700 Subject: fix --- src/lib/product/api/productApi.js | 1 + src/lib/product/components/ProductSlider.jsx | 31 ++++++++++++++++++---------- 2 files changed, 21 insertions(+), 11 deletions(-) (limited to 'src/lib') diff --git a/src/lib/product/api/productApi.js b/src/lib/product/api/productApi.js index 6fe8901e..4fe4cd7d 100644 --- a/src/lib/product/api/productApi.js +++ b/src/lib/product/api/productApi.js @@ -1,6 +1,7 @@ import odooApi from '@/core/api/odooApi' const productApi = async ({ id }) => { + if (!id) return const dataProduct = await odooApi('GET', `/api/v1/product/${id}`) return dataProduct } diff --git a/src/lib/product/components/ProductSlider.jsx b/src/lib/product/components/ProductSlider.jsx index aafd3cf1..eeea7acc 100644 --- a/src/lib/product/components/ProductSlider.jsx +++ b/src/lib/product/components/ProductSlider.jsx @@ -1,34 +1,43 @@ import { Swiper, SwiperSlide } from 'swiper/react' +import { FreeMode } from 'swiper' import ProductCard from './ProductCard' import 'swiper/css' +import 'swiper/css/free-mode' import Image from '@/core/components/elements/Image/Image' import Link from '@/core/components/elements/Link/Link' -import { useState } from 'react' +import { useRef, useState } from 'react' const bannerClassName = 'absolute rounded-r top-0 left-0 h-full max-w-[52%] idt-transition border border-gray_r-6' const ProductSlider = ({ products, simpleTitle = false, bannerMode = false }) => { - const [activeIndex, setActiveIndex] = useState(0) - const swiperSliderFirstMove = (swiper) => { - setActiveIndex(swiper.activeIndex) + const bannerRef = useRef('') + + const onSliderMove = (swiper) => { + if (!bannerMode) return + const calculateOpacity = (132 + swiper.translate) / 100 + bannerRef.current.style = `opacity: ${calculateOpacity > 0 ? calculateOpacity : 0}` } return ( <> {bannerMode && ( - {products.banner.name} 0 ? 'opacity-0' : 'opacity-100'}`} - /> +
+ {products.banner.name} +
)} {bannerMode && ( -- cgit v1.2.3 From 7cbd033633ebc549ce06cff278b197c4f186f9dd Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 22 Feb 2023 14:54:18 +0700 Subject: fix --- src/lib/product/components/ProductSlider.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/lib') diff --git a/src/lib/product/components/ProductSlider.jsx b/src/lib/product/components/ProductSlider.jsx index eeea7acc..0dab2f6b 100644 --- a/src/lib/product/components/ProductSlider.jsx +++ b/src/lib/product/components/ProductSlider.jsx @@ -2,10 +2,9 @@ import { Swiper, SwiperSlide } from 'swiper/react' import { FreeMode } from 'swiper' import ProductCard from './ProductCard' import 'swiper/css' -import 'swiper/css/free-mode' import Image from '@/core/components/elements/Image/Image' import Link from '@/core/components/elements/Link/Link' -import { useRef, useState } from 'react' +import { useRef } from 'react' const bannerClassName = 'absolute rounded-r top-0 left-0 h-full max-w-[52%] idt-transition border border-gray_r-6' @@ -13,7 +12,7 @@ const bannerClassName = const ProductSlider = ({ products, simpleTitle = false, bannerMode = false }) => { const bannerRef = useRef('') - const onSliderMove = (swiper) => { + const changeBannerOpacity = (swiper) => { if (!bannerMode) return const calculateOpacity = (132 + swiper.translate) / 100 bannerRef.current.style = `opacity: ${calculateOpacity > 0 ? calculateOpacity : 0}` @@ -35,7 +34,9 @@ const ProductSlider = ({ products, simpleTitle = false, bannerMode = false }) => freeMode={{ enabled: true, sticky: false }} slidesPerView={2.2} spaceBetween={8} - onSliderMove={onSliderMove} + onSliderMove={changeBannerOpacity} + onSlideChangeTransitionEnd={changeBannerOpacity} + onSlideChangeTransitionStart={changeBannerOpacity} prefix='product' modules={[FreeMode]} > -- cgit v1.2.3 From ebda24f072741d11da7dd5e406eb5bce069dbf6a Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 22 Feb 2023 15:45:05 +0700 Subject: fix --- src/lib/auth/components/Login.jsx | 5 +- src/lib/auth/components/Register.jsx | 95 ++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/lib/auth/components/Register.jsx (limited to 'src/lib') diff --git a/src/lib/auth/components/Login.jsx b/src/lib/auth/components/Login.jsx index 971188a2..92d38c11 100644 --- a/src/lib/auth/components/Login.jsx +++ b/src/lib/auth/components/Login.jsx @@ -93,12 +93,13 @@ const Login = () => { {!isLoading ? 'Masuk' : 'Loading...'} -

+ +

Belum punya akun Indoteknik?{' '} Daftar -

+
) } diff --git a/src/lib/auth/components/Register.jsx b/src/lib/auth/components/Register.jsx new file mode 100644 index 00000000..4b8a8053 --- /dev/null +++ b/src/lib/auth/components/Register.jsx @@ -0,0 +1,95 @@ +import Image from 'next/image' +import Link from '@/core/components/elements/Link/Link' +import IndoteknikLogo from '@/images/logo.png' +import { useState } from 'react' + +const Register = () => { + const [fullname, setFullname] = useState('') + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [companyName, setCompanyName] = useState('') + const [isLoading, setIsLoading] = useState('') + + const handleSubmit = (e) => { + e.preventDefault() + } + + return ( +
+ + Logo Indoteknik + + +

Daftar Akun Indoteknik

+

+ Buat akun sekarang lebih mudah dan terverifikasi +

+ +
+
+ + setCompanyName(e.target.value.toUpperCase())} + placeholder='cth: INDOTEKNIK DOTCOM GEMILANG' + autoCapitalize + /> +
+ +
+ + setFullname(e.target.value)} + placeholder='John Doe' + /> +
+
+ + setEmail(e.target.value)} + placeholder='contoh@email.com' + /> +
+
+ + setPassword(e.target.value)} + placeholder='••••••••••••' + /> +
+ + +
+ +
+ Sudah punya akun Indoteknik?{' '} + + Masuk + +
+
+ ) +} + +export default Register -- cgit v1.2.3 From 4360ec478bee28c1edae5e614065c31cb9da64c1 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 22 Feb 2023 15:46:23 +0700 Subject: fix --- src/lib/auth/components/Register.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/auth/components/Register.jsx b/src/lib/auth/components/Register.jsx index 4b8a8053..1f7a55d5 100644 --- a/src/lib/auth/components/Register.jsx +++ b/src/lib/auth/components/Register.jsx @@ -35,7 +35,7 @@ const Register = () => { value={companyName} onChange={(e) => setCompanyName(e.target.value.toUpperCase())} placeholder='cth: INDOTEKNIK DOTCOM GEMILANG' - autoCapitalize + autoCapitalize='true' />
-- cgit v1.2.3 From 71a4dd291e9fc1d1bb72b57a22f21d1c03b92d8f Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 22 Feb 2023 15:48:38 +0700 Subject: fix --- src/lib/auth/components/Login.jsx | 4 ++-- src/lib/auth/components/Register.jsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/lib') diff --git a/src/lib/auth/components/Login.jsx b/src/lib/auth/components/Login.jsx index 92d38c11..01b2a571 100644 --- a/src/lib/auth/components/Login.jsx +++ b/src/lib/auth/components/Login.jsx @@ -87,13 +87,13 @@ const Login = () => {
- +
Belum punya akun Indoteknik?{' '} diff --git a/src/lib/auth/components/Register.jsx b/src/lib/auth/components/Register.jsx index 1f7a55d5..e3e3c4fa 100644 --- a/src/lib/auth/components/Register.jsx +++ b/src/lib/auth/components/Register.jsx @@ -75,7 +75,7 @@ const Register = () => { -- cgit v1.2.3 From 321e0c09be4b26d72b470407217262d10c88089d Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 22 Feb 2023 22:57:48 +0700 Subject: fix --- src/lib/auth/api/editPersonalProfileApi.js | 10 +++ src/lib/auth/components/CompanyProfile.jsx | 97 +++++++++++++++++++++++ src/lib/auth/components/PersonalProfile.jsx | 115 ++++++++++++++++++++++++++++ src/lib/cart/components/Cart.jsx | 70 ++++++++--------- 4 files changed, 257 insertions(+), 35 deletions(-) create mode 100644 src/lib/auth/api/editPersonalProfileApi.js create mode 100644 src/lib/auth/components/CompanyProfile.jsx create mode 100644 src/lib/auth/components/PersonalProfile.jsx (limited to 'src/lib') diff --git a/src/lib/auth/api/editPersonalProfileApi.js b/src/lib/auth/api/editPersonalProfileApi.js new file mode 100644 index 00000000..af2ad4b2 --- /dev/null +++ b/src/lib/auth/api/editPersonalProfileApi.js @@ -0,0 +1,10 @@ +import odooApi from "@/core/api/odooApi" +import { getAuth } from "@/core/utils/auth" + +const editPersonalProfileApi = async ({ data }) => { + const auth = getAuth() + const dataProfile = await odooApi('PUT', `/api/v1/user/${auth.id}`, data) + return dataProfile +} + +export default editPersonalProfileApi \ No newline at end of file diff --git a/src/lib/auth/components/CompanyProfile.jsx b/src/lib/auth/components/CompanyProfile.jsx new file mode 100644 index 00000000..d66a0209 --- /dev/null +++ b/src/lib/auth/components/CompanyProfile.jsx @@ -0,0 +1,97 @@ +import useAuth from '@/core/hooks/useAuth' +import addressApi from '@/lib/address/api/addressApi' +import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline' +import { useEffect, useState } from 'react' +import { useForm } from 'react-hook-form' + +const PersonalProfile = () => { + const auth = useAuth() + const [isOpen, setIsOpen] = useState(false) + const toggle = () => setIsOpen(!isOpen) + const { register, setValue } = useForm({ + defaultValues: { + email: '', + name: '', + mobile: '', + password: '' + } + }) + + useEffect(() => { + const loadProfile = async () => { + const dataProfile = await addressApi({ id: auth.partnerId }) + setValue('email', dataProfile?.email) + setValue('name', dataProfile?.name) + setValue('mobile', dataProfile?.mobile) + } + if (auth) loadProfile() + }, [auth, setValue]) + + return ( + <> + + + {isOpen && ( + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + + )} + + ) +} + +export default PersonalProfile diff --git a/src/lib/auth/components/PersonalProfile.jsx b/src/lib/auth/components/PersonalProfile.jsx new file mode 100644 index 00000000..29628be9 --- /dev/null +++ b/src/lib/auth/components/PersonalProfile.jsx @@ -0,0 +1,115 @@ +import useAuth from '@/core/hooks/useAuth' +import { setAuth } from '@/core/utils/auth' +import addressApi from '@/lib/address/api/addressApi' +import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline' +import { useEffect, useState } from 'react' +import { useForm } from 'react-hook-form' +import { toast } from 'react-hot-toast' +import editPersonalProfileApi from '../api/editPersonalProfileApi' + +const PersonalProfile = () => { + const auth = useAuth() + const [isOpen, setIsOpen] = useState(false) + const toggle = () => setIsOpen(!isOpen) + const { register, setValue, handleSubmit } = useForm({ + defaultValues: { + email: '', + name: '', + mobile: '', + password: '' + } + }) + + useEffect(() => { + const loadProfile = async () => { + const dataProfile = await addressApi({ id: auth.partnerId }) + setValue('email', dataProfile?.email) + setValue('name', dataProfile?.name) + setValue('mobile', dataProfile?.mobile) + } + if (auth) loadProfile() + }, [auth, setValue]) + + const onSubmitHandler = async (values) => { + let data = values + if (!values.password) delete data.password + const isUpdated = await editPersonalProfileApi({ data }) + console.log(isUpdated) + if (isUpdated?.user) { + setAuth(isUpdated.user) + setValue('password', '') + setIsOpen(false) + toast.success('Berhasil mengubah profil', { duration: 1500 }) + return + } + toast.error('Terjadi kesalahan internal') + } + + return ( + <> + + + {isOpen && ( +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ )} + + ) +} + +export default PersonalProfile diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index 2d94ac0b..af2bec78 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -103,7 +103,7 @@ const Cart = () => { const selectedProduct = () => { if (!products) return [] - return products.filter((product) => product.selected == true) + return products?.filter((product) => product.selected == true) } const deleteProduct = (productId) => { @@ -115,27 +115,27 @@ const Cart = () => { } return ( -
+

Daftar Produk Belanja

Cari Produk Lain
-
+
{cart.isLoading && (
)} - {!cart.isLoading && !products && ( - + {!cart.isLoading && (!products || products?.length == 0) && ( + Keranjang belanja anda masih kosong )} {products?.map((product) => ( -
+
))} -
-
-
-
- Total: - -   - {selectedProduct().length > 0 - ? currencyFormat(totalPriceBeforeTax - totalDiscountAmount + totalTaxAmount) - : '-'} - +
+
+
+ Total: + +   + {selectedProduct().length > 0 + ? currencyFormat(totalPriceBeforeTax - totalDiscountAmount + totalTaxAmount) + : '-'} + +
+
+
+ +
-
-
- -
-- cgit v1.2.3 From 89d99c0386750af7a29678344fe0ed8fb6435d4f Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 22 Feb 2023 22:58:00 +0700 Subject: fix --- src/lib/auth/components/PersonalProfile.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/auth/components/PersonalProfile.jsx b/src/lib/auth/components/PersonalProfile.jsx index 29628be9..46253ef1 100644 --- a/src/lib/auth/components/PersonalProfile.jsx +++ b/src/lib/auth/components/PersonalProfile.jsx @@ -65,7 +65,10 @@ const PersonalProfile = () => { {isOpen && ( -
+
Date: Wed, 22 Feb 2023 23:05:10 +0700 Subject: fix --- src/lib/auth/components/PersonalProfile.jsx | 2 +- src/lib/cart/components/Cart.jsx | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src/lib') diff --git a/src/lib/auth/components/PersonalProfile.jsx b/src/lib/auth/components/PersonalProfile.jsx index 46253ef1..0b387f2e 100644 --- a/src/lib/auth/components/PersonalProfile.jsx +++ b/src/lib/auth/components/PersonalProfile.jsx @@ -90,7 +90,7 @@ const PersonalProfile = () => {
diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index af2bec78..d0408ddf 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -129,9 +129,11 @@ const Cart = () => { )} {!cart.isLoading && (!products || products?.length == 0) && ( - - Keranjang belanja anda masih kosong - +
+ + Keranjang belanja anda masih kosong + +
)} {products?.map((product) => ( @@ -154,7 +156,7 @@ const Cart = () => { className='object-contain object-center border border-gray_r-6 h-40 w-full rounded-md' /> -
+
Date: Wed, 22 Feb 2023 23:34:50 +0700 Subject: fix --- src/lib/product/components/Product.jsx | 81 ++++++++++++++++++++--- src/lib/product/components/ProductCard.jsx | 20 ++++-- src/lib/wishlist/api/createOrDeleteWishlistApi.js | 10 +++ src/lib/wishlist/api/wishlistApi.js | 14 ++++ src/lib/wishlist/hooks/useWishlist.js | 13 ++++ 5 files changed, 122 insertions(+), 16 deletions(-) create mode 100644 src/lib/wishlist/api/createOrDeleteWishlistApi.js create mode 100644 src/lib/wishlist/api/wishlistApi.js create mode 100644 src/lib/wishlist/hooks/useWishlist.js (limited to 'src/lib') diff --git a/src/lib/product/components/Product.jsx b/src/lib/product/components/Product.jsx index 2181c38e..9e33316c 100644 --- a/src/lib/product/components/Product.jsx +++ b/src/lib/product/components/Product.jsx @@ -9,6 +9,11 @@ import ProductSimilar from './ProductSimilar' import LazyLoad from 'react-lazy-load' import { toast } from 'react-hot-toast' import { updateItemCart } from '@/core/utils/cart' +import useWishlist from '@/lib/wishlist/hooks/useWishlist' +import { HeartIcon } from '@heroicons/react/24/outline' +import useAuth from '@/core/hooks/useAuth' +import { useRouter } from 'next/router' +import createOrDeleteWishlistApi from '@/lib/wishlist/api/createOrDeleteWishlistApi' const informationTabOptions = [ { value: 'specification', label: 'Spesifikasi' }, @@ -17,6 +22,9 @@ const informationTabOptions = [ ] const Product = ({ product }) => { + const auth = useAuth() + const router = useRouter() + const { wishlist } = useWishlist({ productId: product?.id }) const [quantity, setQuantity] = useState('1') const [selectedVariant, setSelectedVariant] = useState(null) const [informationTab, setInformationTab] = useState(null) @@ -82,6 +90,21 @@ const Product = ({ product }) => { toast.success('Berhasil menambahkan ke keranjang') } + const toggleWishlist = async () => { + if (!auth) { + router.push('/login') + return + } + const data = { product_id: product.id } + await createOrDeleteWishlistApi({ data }) + if (wishlist.data.productTotal > 0) { + toast.success('Berhasil menghapus dari wishlist') + } else { + toast.success('Berhasil menambahkan ke wishlist') + } + wishlist.refetch() + } + return ( <> { />
- - {product.manufacture?.name} - +
+ {product.manufacture?.name ? ( + {product.manufacture?.name} + ) : ( +
-
+ )} + +

{activeVariant?.name}

{activeVariant?.price?.discountPercentage > 0 && (
@@ -109,7 +147,10 @@ const Product = ({ product }) => { ) : ( Hubungi kami untuk dapatkan harga terbaik,  - + klik disini @@ -146,10 +187,17 @@ const Product = ({ product }) => { onChange={(e) => setQuantity(e.target.value)} />
- -
@@ -193,7 +241,10 @@ const Product = ({ product }) => { )} {activeVariant?.stock == 0 && ( - + Tanya Stok )} @@ -201,7 +252,10 @@ const Product = ({ product }) => { {activeVariant?.weight > 0 && {activeVariant?.weight} KG} {activeVariant?.weight == 0 && ( - + Tanya Berat )} @@ -232,14 +286,21 @@ const Product = ({ product }) => { const TabButton = ({ children, active, ...props }) => { const activeClassName = active ? 'text-red_r-11 underline underline-offset-4' : 'text-gray_r-11' return ( - ) } const TabContent = ({ children, active, className, ...props }) => ( -
+
{children}
) diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index dd221e4f..6b88a3bd 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -23,12 +23,20 @@ const ProductCard = ({ product, simpleTitle }) => { )}
- - {product?.manufacture?.name} - + {product?.manufacture?.name ? ( + + {product.manufacture.name} + + ) : ( +
-
+ )} { + const auth = getAuth() + const dataWishlist = await odooApi('POST', `/api/v1/user/${auth.id}/wishlist/create-or-delete`, data) + return dataWishlist +} + +export default createOrDeleteWishlistApi \ No newline at end of file diff --git a/src/lib/wishlist/api/wishlistApi.js b/src/lib/wishlist/api/wishlistApi.js new file mode 100644 index 00000000..a8906dd4 --- /dev/null +++ b/src/lib/wishlist/api/wishlistApi.js @@ -0,0 +1,14 @@ +import odooApi from '@/core/api/odooApi' +import { getAuth } from '@/core/utils/auth' + +const wishlistApi = async ({ productId }) => { + const auth = getAuth() + if (!auth) return { productTotal: 0, products: [] } + const dataWishlist = await odooApi( + 'GET', + `/api/v1/user/${auth.id}/wishlist?product_id=${productId}` + ) + return dataWishlist +} + +export default wishlistApi diff --git a/src/lib/wishlist/hooks/useWishlist.js b/src/lib/wishlist/hooks/useWishlist.js new file mode 100644 index 00000000..1eb17d53 --- /dev/null +++ b/src/lib/wishlist/hooks/useWishlist.js @@ -0,0 +1,13 @@ +import { useQuery } from "react-query" +import wishlistApi from "../api/wishlistApi" + +const useWishlist = ({ productId }) => { + const fetchWishlist = async () => await wishlistApi({ productId }) + const { data, isLoading, refetch } = useQuery(`wishlist-${productId}`, fetchWishlist) + + return { + wishlist: { data, isLoading, refetch } + } +} + +export default useWishlist -- cgit v1.2.3 From ac3fdf7be9982e65d8f83a20bc487f8dd62e3bfc Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 22 Feb 2023 23:36:47 +0700 Subject: fix --- src/lib/address/components/CreateAddress.jsx | 58 +++++++++++++++++---- src/lib/address/components/EditAddress.jsx | 58 +++++++++++++++++---- src/lib/auth/api/editPersonalProfileApi.js | 6 +-- src/lib/auth/components/Login.jsx | 27 ++++++++-- src/lib/auth/components/Register.jsx | 17 +++++-- src/lib/brand/components/BrandCard.jsx | 6 ++- src/lib/cart/components/Cart.jsx | 10 +++- src/lib/checkout/components/Checkout.jsx | 33 +++++++++--- src/lib/checkout/components/FinishCheckout.jsx | 38 +++++++------- src/lib/home/components/HeroBanner.jsx | 6 ++- src/lib/home/components/PopularProduct.jsx | 7 ++- src/lib/home/components/PreferredBrand.jsx | 6 ++- src/lib/invoice/components/Invoice.jsx | 5 +- src/lib/invoice/components/Invoices.jsx | 31 +++++++++--- src/lib/product/components/ProductFilter.jsx | 22 ++++++-- src/lib/product/components/ProductSearch.jsx | 13 ++++- src/lib/product/components/ProductSlider.jsx | 10 +++- src/lib/transaction/components/Transaction.jsx | 61 +++++++++++++++++++---- src/lib/transaction/components/Transactions.jsx | 49 ++++++++++++++---- src/lib/variant/components/VariantGroupCard.jsx | 6 ++- src/lib/wishlist/api/createOrDeleteWishlistApi.js | 12 +++-- src/lib/wishlist/components/Wishlists.jsx | 16 ++++-- src/lib/wishlist/hooks/useWishlist.js | 4 +- 23 files changed, 396 insertions(+), 105 deletions(-) (limited to 'src/lib') diff --git a/src/lib/address/components/CreateAddress.jsx b/src/lib/address/components/CreateAddress.jsx index 62bb0858..849b4c01 100644 --- a/src/lib/address/components/CreateAddress.jsx +++ b/src/lib/address/components/CreateAddress.jsx @@ -88,20 +88,34 @@ const CreateAddress = () => { } return ( - +
} + render={(props) => ( + + )} />
{errors.type?.message}
- +
{errors.name?.message}
@@ -118,7 +132,12 @@ const CreateAddress = () => {
- +
{errors.mobile?.message}
@@ -135,7 +154,12 @@ const CreateAddress = () => {
- +
{errors.zip?.message}
@@ -144,7 +168,12 @@ const CreateAddress = () => { } + render={(props) => ( + + )} />
{errors.city?.message}
@@ -155,7 +184,11 @@ const CreateAddress = () => { name='district' control={control} render={(props) => ( - + )} />
@@ -166,12 +199,19 @@ const CreateAddress = () => { name='subDistrict' control={control} render={(props) => ( - + )} />
- diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx index 0cfa013a..a832edbc 100644 --- a/src/lib/address/components/EditAddress.jsx +++ b/src/lib/address/components/EditAddress.jsx @@ -102,20 +102,34 @@ const EditAddress = ({ id, defaultValues }) => { } return ( -
+
} + render={(props) => ( + + )} />
{errors.type?.message}
- +
{errors.name?.message}
@@ -132,7 +146,12 @@ const EditAddress = ({ id, defaultValues }) => {
- +
{errors.mobile?.message}
@@ -149,7 +168,12 @@ const EditAddress = ({ id, defaultValues }) => {
- +
{errors.zip?.message}
@@ -158,7 +182,12 @@ const EditAddress = ({ id, defaultValues }) => { } + render={(props) => ( + + )} />
{errors.city?.message}
@@ -169,7 +198,11 @@ const EditAddress = ({ id, defaultValues }) => { name='district' control={control} render={(props) => ( - + )} />
@@ -180,12 +213,19 @@ const EditAddress = ({ id, defaultValues }) => { name='subDistrict' control={control} render={(props) => ( - + )} />
- diff --git a/src/lib/auth/api/editPersonalProfileApi.js b/src/lib/auth/api/editPersonalProfileApi.js index af2ad4b2..39cd44c1 100644 --- a/src/lib/auth/api/editPersonalProfileApi.js +++ b/src/lib/auth/api/editPersonalProfileApi.js @@ -1,5 +1,5 @@ -import odooApi from "@/core/api/odooApi" -import { getAuth } from "@/core/utils/auth" +import odooApi from '@/core/api/odooApi' +import { getAuth } from '@/core/utils/auth' const editPersonalProfileApi = async ({ data }) => { const auth = getAuth() @@ -7,4 +7,4 @@ const editPersonalProfileApi = async ({ data }) => { return dataProfile } -export default editPersonalProfileApi \ No newline at end of file +export default editPersonalProfileApi diff --git a/src/lib/auth/components/Login.jsx b/src/lib/auth/components/Login.jsx index 01b2a571..b4e94e0a 100644 --- a/src/lib/auth/components/Login.jsx +++ b/src/lib/auth/components/Login.jsx @@ -37,7 +37,10 @@ const Login = () => { children: ( <> Email belum diaktivasi, - + aktivasi sekarang @@ -51,18 +54,29 @@ const Login = () => { return (
- Logo Indoteknik + Logo Indoteknik

Mulai Belanja Sekarang

Masuk ke akun kamu untuk belanja

{alert && ( - + {alert.children} )} -
+
{
Belum punya akun Indoteknik?{' '} - + Daftar
diff --git a/src/lib/auth/components/Register.jsx b/src/lib/auth/components/Register.jsx index df08541d..135972d3 100644 --- a/src/lib/auth/components/Register.jsx +++ b/src/lib/auth/components/Register.jsx @@ -26,7 +26,12 @@ const Register = () => { return (
- Logo Indoteknik + Logo Indoteknik

Daftar Akun Indoteknik

@@ -34,7 +39,10 @@ const Register = () => { Buat akun sekarang lebih mudah dan terverifikasi

- +