summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHATEC\SPVDEV001 <tri.susilo@altama.co.id>2023-09-01 09:36:52 +0700
committerHATEC\SPVDEV001 <tri.susilo@altama.co.id>2023-09-01 09:36:52 +0700
commitdfdeedf7141c9191952bdb3005e2e91d2a495044 (patch)
tree2abb9a8090306791a76069baff6c91c5e151d3b5
parent57540da51be35b2d0a90f1d64b097fc4da608a25 (diff)
parent7356bcc0d1b7bac8d05ac315fdcf2a46b3996e91 (diff)
Merge branch 'master' into Feature/google_sign_up
# Conflicts: # src/core/components/elements/Navbar/NavbarDesktop.jsx
-rw-r--r--public/images/GAMBAR-BG-FLASH-SALE.jpgbin0 -> 21334 bytes
-rw-r--r--public/images/ICON_FLASH_SALE.svg35
-rw-r--r--public/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg34
-rw-r--r--public/robots.txt5
-rw-r--r--src/api/productApi.js2
-rw-r--r--src/components/skeleton/BannerSkeleton.jsx1
-rw-r--r--src/core/components/elements/CountDown/CountDown2.jsx50
-rw-r--r--src/core/components/elements/Navbar/NavbarDesktop.jsx9
-rw-r--r--src/core/components/elements/Navbar/Search.jsx30
-rw-r--r--src/core/components/elements/Navbar/TopBanner.jsx5
-rw-r--r--src/core/components/elements/Sidebar/Sidebar.jsx2
-rw-r--r--src/core/components/elements/Skeleton/TopBannerSkeleton.jsx19
-rw-r--r--src/core/components/layouts/BasicLayout.jsx1
-rw-r--r--src/lib/checkout/api/getVoucher.js18
-rw-r--r--src/lib/checkout/components/Checkout.jsx165
-rw-r--r--src/lib/flashSale/components/FlashSale.jsx13
-rw-r--r--src/lib/flashSale/skeleton/FlashSaleSkeleton.jsx36
-rw-r--r--src/lib/home/api/popularProductApi.js2
-rw-r--r--src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx22
-rw-r--r--src/lib/product/api/productSimilarApi.js25
-rw-r--r--src/lib/product/components/Product/ProductDesktop.jsx95
-rw-r--r--src/lib/product/components/Product/ProductMobile.jsx128
-rw-r--r--src/lib/product/components/ProductCard.jsx42
-rw-r--r--src/lib/product/components/ProductSimilar.jsx3
-rw-r--r--src/lib/product/components/ProductSlider.jsx2
-rw-r--r--src/lib/product/hooks/useProductSimilar.js5
-rw-r--r--src/lib/transaction/api/airwayBillApi.js13
-rw-r--r--src/lib/transaction/components/Transaction.jsx180
-rw-r--r--src/lib/transaction/components/Transactions.jsx2
-rw-r--r--src/lib/transaction/hooks/useAirwayBill.js11
-rw-r--r--src/pages/_app.jsx1
-rw-r--r--src/pages/api/shop/search.js3
-rw-r--r--src/pages/index.jsx36
33 files changed, 766 insertions, 229 deletions
diff --git a/public/images/GAMBAR-BG-FLASH-SALE.jpg b/public/images/GAMBAR-BG-FLASH-SALE.jpg
new file mode 100644
index 00000000..db26136a
--- /dev/null
+++ b/public/images/GAMBAR-BG-FLASH-SALE.jpg
Binary files differ
diff --git a/public/images/ICON_FLASH_SALE.svg b/public/images/ICON_FLASH_SALE.svg
new file mode 100644
index 00000000..d6d90b18
--- /dev/null
+++ b/public/images/ICON_FLASH_SALE.svg
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 230 230" style="enable-background:new 0 0 230 230;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#F0C83B;}
+ .st1{fill:#FEFEFE;}
+</style>
+<g>
+ <path class="st0" d="M142.9,104.6c-14,17.9-27.7,35.4-41.4,52.9c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.3,4.2-20.6,6.4-30.9
+ c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.4,0-17.1,0h-2.8c1.6-3.7,3.1-7.1,4.6-10.5
+ c4.6-10.3,9.3-20.6,13.8-30.9c0.8-1.8,1.7-2.4,3.6-2.3c7.1,0.1,14.3,0.1,21.5,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.3-8.3,16.8-12.5,25.1
+ c-1,2-0.6,2.4,1.5,2.3C128.2,104.6,135.2,104.6,142.9,104.6z"/>
+ <path class="st0" d="M169.1,192.3c-1-1.2-0.9-1.7,0.3-2.6c2.4-1.9,4.9-3.8,7.1-5.9c26-25.7,34.9-56.6,26.6-92.1
+ c-9.6-40.9-46.5-69.9-89.7-69.9c-4.8,0.1-11.4,0.7-17.8,2.2c-48.4,11-79.8,59.2-69.6,110c4.3,21.7,15.3,39.7,32.4,53.8
+ c1.4,1.2,1.7,1.9,0.4,3.4c-4.4,5.4-8.7,11-13,16.4c-1.3,1.7-2.3,3.6-1.8,5.9c0.6,2.9,2.2,4.8,5.1,5.6c3.1,0.8,5.4-0.4,7.3-2.7
+ c4.5-5.7,9.1-11.4,13.5-17.3c1.3-1.7,2.1-2,4-1c26.6,13.2,53.5,13.6,80.4,0.9c1.5-0.7,2.3-0.4,3.2,0.8c4.5,5.7,9.1,11.3,13.6,16.9
+ c2.9,3.6,7.1,4.4,10.2,1.9c3.3-2.7,3.5-6.8,0.5-10.5C177.6,202.8,173.4,197.5,169.1,192.3z M121.8,194.1
+ c-23.4,2-43.6-5.5-60.3-21.8c-13.5-13.1-21.2-29.4-23.3-48.2c-0.4-3.1-0.4-6.2-0.5-9.3c0.2-22.1,7.8-41.2,23.3-56.8
+ c13-13.1,28.9-20.6,47.5-22.3c38.4-3.5,73.8,23.2,81.8,61.7c6.2,29.7-1.8,55.6-23.8,76.6C154.2,186,139.1,192.6,121.8,194.1z"/>
+ <path class="st0" d="M23.9,58.9c-5.3,0-8.4-5.5-5.8-10.3c4.6-8.4,10.6-15.8,17.6-22.3c6.2-5.7,12.9-10.6,20.3-14.6
+ c4.6-2.5,9.6,0.1,10.3,5.1c0.4,2.9-1,5.4-4,7.1c-9.2,5.2-17.4,11.6-24.2,19.8c-3,3.6-5.6,7.3-7.9,11.4
+ C28.8,57.5,26.8,58.9,23.9,58.9z"/>
+ <path class="st0" d="M169.9,10c1.2-0.2,2.4,0.3,3.6,1c15.6,8.4,28.4,19.8,37.7,35c1,1.7,1.9,3.4,1.6,5.5c-0.4,2.9-2.5,5.2-5.1,5.9
+ c-2.9,0.7-5.9-0.3-7.4-2.9c-8.1-13.8-19.7-24-33.6-31.6c-3-1.6-4.2-4.8-3.3-7.8C164.1,12,166.6,10.1,169.9,10z"/>
+ <path class="st1" d="M142.9,104.6c-14,17.9-27.7,35.4-41.4,52.9c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.3,4.2-20.6,6.4-30.9
+ c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.4,0-17.1,0h-2.8c1.6-3.7,3.1-7.1,4.6-10.5
+ c4.6-10.3,9.3-20.6,13.8-30.9c0.8-1.8,1.7-2.4,3.6-2.3c7.1,0.1,14.3,0.1,21.5,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.3-8.3,16.8-12.5,25.1
+ c-1,2-0.6,2.4,1.5,2.3C128.2,104.6,135.2,104.6,142.9,104.6z"/>
+ <path class="st0" d="M142.9,104.6c-14,17.9-27.7,35.4-41.4,52.9c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.3,4.2-20.6,6.4-30.9
+ c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.4,0-17.1,0h-2.8c1.6-3.7,3.1-7.1,4.6-10.5
+ c4.6-10.3,9.3-20.6,13.8-30.9c0.8-1.8,1.7-2.4,3.6-2.3c7.1,0.1,14.3,0.1,21.5,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.3-8.3,16.8-12.5,25.1
+ c-1,2-0.6,2.4,1.5,2.3C128.2,104.6,135.2,104.6,142.9,104.6z"/>
+</g>
+</svg>
diff --git a/public/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg b/public/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg
new file mode 100644
index 00000000..2a8b40b8
--- /dev/null
+++ b/public/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 199.8 213" style="enable-background:new 0 0 199.8 213;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#F0C83B;}
+ .st1{fill:#FEFEFE;}
+</style>
+<g>
+ <path class="st0" d="M128,96c-14.1,18-27.9,35.7-41.7,53.3c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.4,4.2-20.8,6.5-31.2
+ c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.5,0-17.2,0h-2.8c1.6-3.7,3.1-7.2,4.6-10.6
+ c4.6-10.4,9.4-20.8,13.9-31.2c0.8-1.8,1.7-2.4,3.6-2.3c7.2,0.1,14.4,0.1,21.7,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.4-8.4,16.9-12.6,25.3
+ c-1,2-0.6,2.4,1.5,2.3C113.2,96,120.3,96,128,96z"/>
+ <path class="st0" d="M154.5,184.4c-1-1.2-0.9-1.7,0.3-2.6c2.4-1.9,4.9-3.8,7.2-5.9c26.2-25.9,35.2-57.1,26.8-92.9
+ c-9.7-41.2-46.9-70.5-90.5-70.5c-4.8,0.1-11.5,0.7-17.9,2.2C31.5,25.8-0.1,74.4,10.2,125.6c4.3,21.9,15.4,40,32.7,54.3
+ c1.4,1.2,1.7,1.9,0.4,3.4c-4.4,5.4-8.8,11.1-13.1,16.5c-1.3,1.7-2.3,3.6-1.8,5.9c0.6,2.9,2.2,4.8,5.1,5.6c3.1,0.8,5.4-0.4,7.4-2.7
+ c4.5-5.7,9.2-11.5,13.6-17.4c1.3-1.7,2.1-2,4-1c26.8,13.3,53.9,13.7,81.1,0.9c1.5-0.7,2.3-0.4,3.2,0.8c4.5,5.7,9.2,11.4,13.7,17
+ c2.9,3.6,7.2,4.4,10.3,1.9c3.3-2.7,3.5-6.9,0.5-10.6C163,195,158.8,189.7,154.5,184.4z M106.8,186.2c-23.6,2-44-5.5-60.8-22
+ c-13.6-13.2-21.4-29.6-23.5-48.6c-0.4-3.1-0.4-6.3-0.5-9.4C22.2,84,29.8,64.7,45.5,49c13.1-13.2,29.1-20.8,47.9-22.5
+ c38.7-3.5,74.4,23.4,82.5,62.2c6.3,29.9-1.8,56.1-24,77.2C139.4,178.1,124.2,184.7,106.8,186.2z"/>
+ <path class="st0" d="M8,49.9c-5.3,0-8.5-5.5-5.8-10.4C6.8,31.1,12.9,23.6,19.9,17c6.3-5.7,13-10.7,20.5-14.7
+ c4.6-2.5,9.7,0.1,10.4,5.1c0.4,2.9-1,5.4-4,7.2c-9.3,5.2-17.5,11.7-24.4,20c-3,3.6-5.6,7.4-8,11.5C13,48.5,11,49.9,8,49.9z"/>
+ <path class="st0" d="M155.3,0.6c1.2-0.2,2.4,0.3,3.6,1c15.7,8.5,28.6,20,38,35.3c1,1.7,1.9,3.4,1.6,5.5c-0.4,2.9-2.5,5.2-5.1,5.9
+ c-2.9,0.7-5.9-0.3-7.5-2.9c-8.2-13.9-19.9-24.2-33.9-31.9c-3-1.6-4.2-4.8-3.3-7.9C149.4,2.6,151.9,0.7,155.3,0.6z"/>
+ <path class="st1" d="M128,96c-14.1,18-27.9,35.7-41.7,53.3c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.4,4.2-20.8,6.5-31.2
+ c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.5,0-17.2,0h-2.8c1.6-3.7,3.1-7.2,4.6-10.6
+ c4.6-10.4,9.4-20.8,13.9-31.2c0.8-1.8,1.7-2.4,3.6-2.3c7.2,0.1,14.4,0.1,21.7,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.4-8.4,16.9-12.6,25.3
+ c-1,2-0.6,2.4,1.5,2.3C113.2,96,120.3,96,128,96z"/>
+ <path class="st0" d="M128,96c-14.1,18-27.9,35.7-41.7,53.3c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.4,4.2-20.8,6.5-31.2
+ c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.5,0-17.2,0h-2.8c1.6-3.7,3.1-7.2,4.6-10.6
+ c4.6-10.4,9.4-20.8,13.9-31.2c0.8-1.8,1.7-2.4,3.6-2.3c7.2,0.1,14.4,0.1,21.7,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.4-8.4,16.9-12.6,25.3
+ c-1,2-0.6,2.4,1.5,2.3C113.2,96,120.3,96,128,96z"/>
+</g>
+</svg>
diff --git a/public/robots.txt b/public/robots.txt
index 4aca1276..205ec9f3 100644
--- a/public/robots.txt
+++ b/public/robots.txt
@@ -8,13 +8,14 @@ Disallow: /shop/product/pdf/*
Disallow: /shop/product/wishlist/*
Disallow: /shop/product/https://*
Disallow: /v1/*
-Disallow: /shop/search?product_name=*
-Disallow: /shop/search?q=*
+Disallow: /shop/search?*=*
Disallow: /shop/cart/
Disallow: /shop/checkout/
Disallow: /my/*
Disallow: /shop/search/*
Disallow: /promo/*
+Disallow: /shop/brands/*?*
+Disallow: /shop/category/*?*
User-agent: Adsbot-Google
Allow: /my/*
diff --git a/src/api/productApi.js b/src/api/productApi.js
index cca052f7..009d95ef 100644
--- a/src/api/productApi.js
+++ b/src/api/productApi.js
@@ -3,7 +3,7 @@ import axios from 'axios'
export const popularProductApi = () => {
return async () => {
const dataPopularProducts = await axios(
- `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular`
+ `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular-weekly&priceFrom=1`
)
return dataPopularProducts.data.response
}
diff --git a/src/components/skeleton/BannerSkeleton.jsx b/src/components/skeleton/BannerSkeleton.jsx
index 7cb3952d..da86aef9 100644
--- a/src/components/skeleton/BannerSkeleton.jsx
+++ b/src/components/skeleton/BannerSkeleton.jsx
@@ -1,7 +1,6 @@
import useDevice from '@/core/hooks/useDevice'
import classNames from 'classnames'
import Skeleton from 'react-loading-skeleton'
-import 'react-loading-skeleton/dist/skeleton.css'
const HeroBannerSkeleton = () => {
const { isDesktop, isMobile } = useDevice()
diff --git a/src/core/components/elements/CountDown/CountDown2.jsx b/src/core/components/elements/CountDown/CountDown2.jsx
index 61503d17..e85a22cb 100644
--- a/src/core/components/elements/CountDown/CountDown2.jsx
+++ b/src/core/components/elements/CountDown/CountDown2.jsx
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'
const CountDown2 = ({ initialTime }) => {
- const hours = Math.floor(initialTime / 3600)
+ /*const hours = Math.floor(initialTime / 3600)
const minutes = Math.floor((initialTime % 3600) / 60)
const seconds = initialTime % 60
@@ -25,24 +25,58 @@ const CountDown2 = ({ initialTime }) => {
}
}, 1000)
return () => clearInterval(timer)
+ }, [timeLeft])*/
+
+ const days = Math.floor(initialTime / 86400)
+ const hours = Math.floor((initialTime % 86400) / 3600)
+ const minutes = Math.floor((initialTime % 3600) / 60)
+ const seconds = initialTime % 60
+
+ const [timeLeft, setTimeLeft] = useState({
+ day: days,
+ hour: hours,
+ minute: minutes,
+ second: seconds
+ })
+
+ useEffect(() => {
+ const timer = setInterval(() => {
+ const totalSeconds =
+ timeLeft.day * 86400 + timeLeft.hour * 3600 + timeLeft.minute * 60 + timeLeft.second
+ const secondsLeft = totalSeconds - 1
+ if (secondsLeft < 0) {
+ clearInterval(timer)
+ } else {
+ const days = Math.floor(secondsLeft / 86400)
+ const hours = Math.floor((secondsLeft % 86400) / 3600)
+ const minutes = Math.floor((secondsLeft % 3600) / 60)
+ const seconds = secondsLeft % 60
+ setTimeLeft({ day: days, hour: hours, minute: minutes, second: seconds })
+ }
+ }, 1000)
+ return () => clearInterval(timer)
}, [timeLeft])
+
return (
- <div className='flex justify-between gap-x-2'>
+ <div className='flex justify-between gap-x-2 mt-18'>
<div className='flex flex-col items-center'>
- <span className='bg-red-200 border border-red-500 text-black font-sm w-10 h-8 flex items-center justify-center rounded'>
- {timeLeft.hour.toString().padStart(2, '0')}
+ <span className='bg-yellow-400 border border-yellow-400 text-black font-sm w-10 h-7 flex items-center justify-center rounded-lg'>
+ {timeLeft.day.toString().padStart(2, '0')}
</span>
+ <span className='text-xs text-white'>Hari</span>
</div>
<div className='flex flex-col items-center'>
- <span className='bg-red-200 border border-red-500 text-black font-sm w-10 h-8 flex items-center justify-center rounded'>
- {timeLeft.minute.toString().padStart(2, '0')}
+ <span className='bg-yellow-400 border border-yellow-400 text-black font-sm w-10 h-7 flex items-center justify-center rounded-lg'>
+ {timeLeft.hour.toString().padStart(2, '0')}
</span>
+ <span className='text-xs text-white'>Jam</span>
</div>
<div className='flex flex-col items-center'>
- <span className='bg-red-200 border border-red-500 text-black font-sm w-10 h-8 flex items-center justify-center rounded'>
- {timeLeft.second.toString().padStart(2, '0')}
+ <span className='bg-yellow-400 border border-yellow-400 text-black font-sm w-10 h-7 flex items-center justify-center rounded-lg'>
+ {timeLeft.minute.toString().padStart(2, '0')}
</span>
+ <span className='text-xs text-white'>Menit</span>
</div>
</div>
)
diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx
index 17aedeb6..6fdea644 100644
--- a/src/core/components/elements/Navbar/NavbarDesktop.jsx
+++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx
@@ -14,7 +14,6 @@ import { useContext, useEffect, useState } from 'react'
import useAuth from '@/core/hooks/useAuth'
import NavbarUserDropdown from './NavbarUserDropdown'
import { getCountCart } from '@/core/utils/cart'
-import TopBanner from './TopBanner'
import whatsappUrl from '@/core/utils/whatsappUrl'
import { useRouter } from 'next/router'
import { getAuth, setAuth } from '@/core/utils/auth'
@@ -22,8 +21,12 @@ import { createSlug, getIdFromSlug } from '@/core/utils/slug'
import productApi from '@/lib/product/api/productApi'
import { useSession } from 'next-auth/react'
import { AuthContext } from '@/pages/_app'
+import { TopBannerSkeleton } from '../Skeleton/TopBannerSkeleton'
const Search = dynamic(() => import('./Search'))
+const TopBanner = dynamic(() => import('./TopBanner'), {
+ loading: () => <TopBannerSkeleton />
+})
const NavbarDesktop = () => {
const [isOpenCategory, setIsOpenCategory] = useState(false)
@@ -165,8 +168,10 @@ const NavbarDesktop = () => {
Ready Stock
</Link>
<Link
- href='/blog'
+ href='https://blog.indoteknik.com/'
className='p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 hover:bg-gray_r-3 idt-transition'
+ target='_blank'
+ rel='noreferrer noopener'
>
Blog Indoteknik
</Link>
diff --git a/src/core/components/elements/Navbar/Search.jsx b/src/core/components/elements/Navbar/Search.jsx
index 47a9c235..f4a8ab3a 100644
--- a/src/core/components/elements/Navbar/Search.jsx
+++ b/src/core/components/elements/Navbar/Search.jsx
@@ -64,21 +64,21 @@ const Search = () => {
<MagnifyingGlassIcon className='w-6' />
</button>
- {suggestions.length > 0 && (
- <>
- <div className='absolute w-full top-[50px] rounded-b bg-gray_r-1 border border-gray_r-6 divide-y divide-gray_r-6 z-50'>
- {suggestions.map((suggestion, index) => (
- <Link
- href={`/shop/search?q=${suggestion.term}`}
- key={index}
- className='px-3 py-3 !text-gray_r-12 font-normal'
- >
- {suggestion.term}
- </Link>
- ))}
- </div>
- </>
- )}
+ <div
+ className={`absolute w-full top-[50px] rounded-b bg-gray_r-1 border border-gray_r-6 divide-y divide-gray_r-6 z-50 ${
+ suggestions.length > 0 ? 'block' : 'hidden'
+ }`}
+ >
+ {suggestions.map((suggestion, index) => (
+ <Link
+ href={`/shop/search?q=${suggestion.term}`}
+ key={index}
+ className='px-3 py-3 !text-gray_r-12 font-normal'
+ >
+ {suggestion.term}
+ </Link>
+ ))}
+ </div>
</form>
</>
)
diff --git a/src/core/components/elements/Navbar/TopBanner.jsx b/src/core/components/elements/Navbar/TopBanner.jsx
index 9efd0a8d..dca2e930 100644
--- a/src/core/components/elements/Navbar/TopBanner.jsx
+++ b/src/core/components/elements/Navbar/TopBanner.jsx
@@ -2,11 +2,16 @@ import odooApi from '@/core/api/odooApi'
import { useQuery } from 'react-query'
import Image from 'next/image'
import Link from '../Link/Link'
+import { TopBannerSkeleton } from '../Skeleton/TopBannerSkeleton'
const TopBanner = () => {
const fetchTopBanner = async () => await odooApi('GET', '/api/v1/banner?type=top-banner')
const topBanner = useQuery('topBanner', fetchTopBanner)
+ if (topBanner.isLoading) {
+ return <TopBannerSkeleton />
+ }
+
return (
topBanner.isFetched &&
topBanner.data?.length > 0 && (
diff --git a/src/core/components/elements/Sidebar/Sidebar.jsx b/src/core/components/elements/Sidebar/Sidebar.jsx
index 7ea8f7c4..f0bcb7b7 100644
--- a/src/core/components/elements/Sidebar/Sidebar.jsx
+++ b/src/core/components/elements/Sidebar/Sidebar.jsx
@@ -120,7 +120,7 @@ const Sidebar = ({ active, close }) => {
<SidebarLink className={itemClassName} href='/shop/brands'>
Semua Brand
</SidebarLink>
- <SidebarLink className={itemClassName} href='/blog'>
+ <SidebarLink className={itemClassName} href='https://blog.indoteknik.com/' target="_blank" rel="noreferrer noopener">
Blog Indoteknik
</SidebarLink>
<SidebarLink className={itemClassName} href='/video'>
diff --git a/src/core/components/elements/Skeleton/TopBannerSkeleton.jsx b/src/core/components/elements/Skeleton/TopBannerSkeleton.jsx
new file mode 100644
index 00000000..f7d2e748
--- /dev/null
+++ b/src/core/components/elements/Skeleton/TopBannerSkeleton.jsx
@@ -0,0 +1,19 @@
+import useDevice from '@/core/hooks/useDevice'
+import classNames from 'classnames'
+import Skeleton from 'react-loading-skeleton'
+
+const TopBannerSkeleton = () => {
+ const { isDesktop, isMobile } = useDevice()
+
+ const deviceClassName = {
+ 'h-10': isDesktop,
+ 'h-2.5': isMobile
+ }
+ const combinedClassName = classNames(deviceClassName)
+
+ return (
+ <Skeleton className={combinedClassName} count={1} containerClassName='w-full h-full block' />
+ )
+}
+
+export { TopBannerSkeleton }
diff --git a/src/core/components/layouts/BasicLayout.jsx b/src/core/components/layouts/BasicLayout.jsx
index 272440c2..e737101a 100644
--- a/src/core/components/layouts/BasicLayout.jsx
+++ b/src/core/components/layouts/BasicLayout.jsx
@@ -38,7 +38,6 @@ const BasicLayout = ({ children }) => {
const { slug } = router.query
const getProduct = async () => {
let product = await productApi({ id: getIdFromSlug(slug), headers: { Token: authToken } })
- console.log('ini product', product)
setPayloadWa({
name: product[0]?.name,
manufacture: product[0]?.manufacture.name,
diff --git a/src/lib/checkout/api/getVoucher.js b/src/lib/checkout/api/getVoucher.js
index 57d8acf5..07cf376e 100644
--- a/src/lib/checkout/api/getVoucher.js
+++ b/src/lib/checkout/api/getVoucher.js
@@ -1,11 +1,21 @@
import odooApi from '@/core/api/odooApi'
-export const getVoucher = async (id) => {
- const dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher`)
+export const getVoucher = async (id, source) => {
+ let dataVoucher = null
+ if(source){
+ dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher?source=${source}`)
+ }else {
+ dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher`)
+ }
return dataVoucher
}
-export const findVoucher = async (code, id) => {
- const dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher?code=${code}`)
+export const findVoucher = async (code, id, source) => {
+ let dataVoucher = null
+ if(source){
+ dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher?code=${code}&source=${source}`)
+ }else{
+ dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher?code=${code}`)
+ }
return dataVoucher
}
diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx
index 53ac63e1..afb94c10 100644
--- a/src/lib/checkout/components/Checkout.jsx
+++ b/src/lib/checkout/components/Checkout.jsx
@@ -107,7 +107,7 @@ const Checkout = () => {
const voucher = async () => {
if (!listVouchers) {
try {
- let dataVoucher = await getVoucher(auth?.id)
+ let dataVoucher = await getVoucher(auth?.id, query)
SetListVoucher(dataVoucher)
} finally {
setLoadingVoucher(false)
@@ -115,7 +115,7 @@ const Checkout = () => {
}
}
const VoucherCode = async (code) => {
- let dataVoucher = await findVoucher(code, auth.id)
+ let dataVoucher = await findVoucher(code, auth.id, query)
if (dataVoucher.length <= 0) {
SetFindVoucher(1)
return
@@ -301,7 +301,7 @@ const Checkout = () => {
voucher: activeVoucher,
type: 'sale_order'
}
- if(query){
+ if (query) {
data.source = 'buy'
}
if (poNumber.current.value) data.po_number = poNumber.current.value
@@ -427,7 +427,7 @@ const Checkout = () => {
<hr className='mt-10 my-4 border-gray_r-10' />
<div className=''>
- {!loadingVoucher && listVouchers.length === 0 ? (
+ {!loadingVoucher && listVouchers?.length === 0 ? (
<div className='flex items-center justify-center mt-4 mb-4'>
<div className='text-center'>
<h1 className='font-bold mb-4'>Tidak ada voucher tersedia</h1>
@@ -490,7 +490,78 @@ const Checkout = () => {
<div className='absolute w-full h-full bg-gray_r-3/40 top-0 left-0 z-50' />
)}
- <div className={`border border-solid mb-5 w-full hover:cursor-pointer p-4 `}>
+ <div className={`border border-solid mb-5 w-full hover:cursor-pointer p-2 pl-4 pr-4 `}>
+ {item.canApply && (
+ <div
+ class='p-2 mb-4 text-sm text-green-800 rounded-lg bg-green-50 dark:text-green-400'
+ role='alert'
+ >
+ <p>
+ Potensi potongan sebesar{' '}
+ <span className='text-green font-bold'>
+ {currencyFormat(item.discountVoucher)}
+ </span>
+ </p>
+ </div>
+ )}
+ {!item.canApply && item.applyStatus === 'MPA-HF' && (
+ <div
+ class='p-2 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400'
+ role='alert'
+ >
+ <p className='text-sm'>Voucher tidak bisa digabung dengan promo lainya</p>
+ </div>
+ )}
+ {!item.canApply && item.applyStatus === 'UM' && (
+ <div
+ class='p-2 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400'
+ role='alert'
+ >
+ <p className='text-sm'>
+ Tambah produk{' '}
+ <span className='text-red-500 font-bold '>{item.manufactureNames}</span> senilai{' '}
+ <span className='text-red-500 font-bold'>
+ {currencyFormat(item.minPurchaseAmount)}
+ </span>{' '}
+ untuk pakai promo ini
+ </p>
+ </div>
+ )}
+
+ {!item.canApply &&
+ item.applyStatus === 'MPA' &&
+ item.manufactureNames != '' && (
+ <div
+ class='p-2 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400'
+ role='alert'
+ >
+ <p className='text-sm'>
+ Tambah produk{' '}
+ <span className='text-red-500 font-bold'>{item.manufactureNames}</span> senilai{' '}
+ <span className='text-red-500 font-bold'>
+ {currencyFormat(item.differenceToApply)}
+ </span>{' '}
+ untuk pakai promo ini
+ </p>
+ </div>
+ )}
+
+ {!item.canApply &&
+ item.applyStatus === 'MPA' &&
+ item.manufactureNames === '' && (
+ <div
+ class='p-2 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400'
+ role='alert'
+ >
+ <p className='text-sm'>
+ Tambah{' '}
+ <span className='text-red-500 font-bold'>
+ {currencyFormat(item.differenceToApply)}
+ </span>{' '}
+ untuk pakai promo ini{' '}
+ </p>
+ </div>
+ )}
<div className={`flex gap-x-3`}>
<div className='hidden md:w-[250px] md:block'>
<Image src={item.image} alt={item.name} className={`object-cover`} />
@@ -516,7 +587,7 @@ const Checkout = () => {
</label>
</div>
</div>
- <hr className='mt-3 my-4 border-gray_r-8' />
+ <hr className='mt-2 my-2 border-gray_r-8' />
<div className='flex justify-between items-center'>
<p className='text-justify text-sm md:text-xs'>
Kode Voucher :{' '}
@@ -528,51 +599,30 @@ const Checkout = () => {
)}
</p>
</div>
+ <div className='flex items-center mt-3'>
+ <svg
+ aria-hidden='true'
+ fill='none'
+ stroke='currentColor'
+ stroke-width='1.5'
+ viewBox='0 0 24 24'
+ className='w-5 text-black'
+ >
+ <path
+ d='M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z'
+ stroke-linecap='round'
+ stroke-linejoin='round'
+ ></path>
+ </svg>
+ <span className='text-left ml-3 text-sm '>
+ Berakhir dalam{' '}
+ <span className='text-red-600'>{item.remainingTime}</span> lagi{' '}
+ </span>
+ </div>
</div>
</div>
<div className='mt-3'>
<p className='text-justify text-sm '>
- {!item.canApply &&
- item.applyStatus === 'MPA' &&
- item.manufactureNames != '' && (
- <p>
- Tambah produk{' '}
- <span className='text-red-500'>{item.manufactureNames}</span> senilai{' '}
- <span className='text-red-500'>
- {currencyFormat(item.differenceToApply)}
- </span>{' '}
- untuk pakai promo ini
- </p>
- )}
- {!item.canApply &&
- item.applyStatus === 'MPA' &&
- item.manufactureNames === '' && (
- <p>
- Tambah{' '}
- <span className='text-red-500'>
- {currencyFormat(item.differenceToApply)}
- </span>{' '}
- untuk pakai promo ini{' '}
- </p>
- )}
- {!item.canApply && item.applyStatus === 'UM' && (
- <p>
- Tambah produk{' '}
- <span className='text-red-500'>{item.manufactureNames}</span> senilai{' '}
- <span className='text-red-500'>
- {currencyFormat(item.minPurchaseAmount)}
- </span>{' '}
- untuk pakai promo ini
- </p>
- )}
- {item.canApply && (
- <p>
- Potensi potongan sebesar{' '}
- <span className='text-red-500'>
- {currencyFormat(item.discountVoucher)}
- </span>
- </p>
- )}
{/* {item.canApply === false
? 'Tambah ' +
currencyFormat(item.differenceToApply) +
@@ -580,27 +630,6 @@ const Checkout = () => {
: 'Potensi potongan sebesar ' +
currencyFormat(hitungDiscountVoucher(item.code))} */}
</p>
- <hr className='mt-2 my-4 border-gray_r-8' />
- <div className='flex items-center'>
- <svg
- aria-hidden='true'
- fill='none'
- stroke='currentColor'
- stroke-width='1.5'
- viewBox='0 0 24 24'
- className='w-5 text-black'
- >
- <path
- d='M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z'
- stroke-linecap='round'
- stroke-linejoin='round'
- ></path>
- </svg>
- <span className='text-left ml-3 text-sm '>
- Berakhir dalam <span className='text-red-600'>{item.remainingTime}</span>{' '}
- lagi{' '}
- </span>
- </div>
</div>
</div>
</div>
diff --git a/src/lib/flashSale/components/FlashSale.jsx b/src/lib/flashSale/components/FlashSale.jsx
index e4a4a25c..87545d8d 100644
--- a/src/lib/flashSale/components/FlashSale.jsx
+++ b/src/lib/flashSale/components/FlashSale.jsx
@@ -1,21 +1,28 @@
import { useEffect, useState } from 'react'
import flashSaleApi from '../api/flashSaleApi'
-import Image from '@/core/components/elements/Image/Image'
+import Image from 'next/image'
import CountDown from '@/core/components/elements/CountDown/CountDown'
import productSearchApi from '@/lib/product/api/productSearchApi'
import ProductSlider from '@/lib/product/components/ProductSlider'
+import { FlashSaleSkeleton } from '../skeleton/FlashSaleSkeleton'
const FlashSale = () => {
const [flashSales, setFlashSales] = useState(null)
+ const [isLoading, setIsLoading] = useState(true)
useEffect(() => {
const loadFlashSales = async () => {
const dataFlashSales = await flashSaleApi()
setFlashSales(dataFlashSales)
+ setIsLoading(false)
}
loadFlashSales()
}, [])
+ if (isLoading) {
+ return <FlashSaleSkeleton />
+ }
+
return (
flashSales?.length > 0 && (
<div className='px-4 sm:px-0 grid grid-cols-1 gap-y-8'>
@@ -30,11 +37,15 @@ const FlashSale = () => {
<Image
src={flashSale.banner}
alt={flashSale.name}
+ width={1080}
+ height={192}
className='w-full rounded mb-4 hidden sm:block'
/>
<Image
src={flashSale.bannerMobile}
alt={flashSale.name}
+ width={256}
+ height={48}
className='w-full rounded mb-4 block sm:hidden'
/>
<FlashSaleProduct flashSaleId={flashSale.pricelistId} />
diff --git a/src/lib/flashSale/skeleton/FlashSaleSkeleton.jsx b/src/lib/flashSale/skeleton/FlashSaleSkeleton.jsx
new file mode 100644
index 00000000..e9a200d9
--- /dev/null
+++ b/src/lib/flashSale/skeleton/FlashSaleSkeleton.jsx
@@ -0,0 +1,36 @@
+import useDevice from '@/core/hooks/useDevice'
+import PopularProductSkeleton from '@/lib/home/components/Skeleton/PopularProductSkeleton'
+import Skeleton from 'react-loading-skeleton'
+
+const FlashSaleSkeleton = () => {
+ return (
+ <div className='px-4 md:px-0'>
+ <TitleSkeleton />
+ <div className='my-4'>
+ <BannerSkeleton />
+ </div>
+ <PopularProductSkeleton />
+ </div>
+ )
+}
+
+const TitleSkeleton = () => {
+ return (
+ <div className='w-full md:w-[36%] flex gap-x-4'>
+ <Skeleton containerClassName='block w-1/2' height={24} />
+ <div className='w-1/2 flex gap-x-1'>
+ <Skeleton height={40} containerClassName='w-full' />
+ <Skeleton height={40} containerClassName='w-full' />
+ <Skeleton height={40} containerClassName='w-full' />
+ <Skeleton height={40} containerClassName='w-full' />
+ </div>
+ </div>
+ )
+}
+
+const BannerSkeleton = () => {
+ const { isDesktop } = useDevice()
+ return <Skeleton duration={1.2} height={isDesktop ? 192 : 48} containerClassName='w-full' />
+}
+
+export { FlashSaleSkeleton, TitleSkeleton, BannerSkeleton }
diff --git a/src/lib/home/api/popularProductApi.js b/src/lib/home/api/popularProductApi.js
index 37e4390e..4bb5e580 100644
--- a/src/lib/home/api/popularProductApi.js
+++ b/src/lib/home/api/popularProductApi.js
@@ -2,7 +2,7 @@ import axios from 'axios'
const popularProductApi = async () => {
const dataPopularProducts = await axios(
- `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular`
+ `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular-weekly`
)
return dataPopularProducts.data.response
}
diff --git a/src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx b/src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx
index 00589342..bd783053 100644
--- a/src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx
+++ b/src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx
@@ -1,12 +1,16 @@
-import BrandSkeleton from '@/core/components/elements/Skeleton/BrandSkeleton'
+import useDevice from '@/core/hooks/useDevice'
+import Skeleton from 'react-loading-skeleton'
-const PreferredBrandSkeleton = () => (
- <div className='grid grid-cols-4 gap-x-3'>
- <BrandSkeleton />
- <BrandSkeleton />
- <BrandSkeleton />
- <BrandSkeleton />
- </div>
-)
+const PreferredBrandSkeleton = () => {
+ const { isDesktop } = useDevice()
+
+ return (
+ <div className='grid grid-cols-4 md:grid-cols-8 gap-x-3'>
+ {Array.from({ length: isDesktop ? 8 : 4 }, (_, index) => (
+ <Skeleton count={1} height={isDesktop ? 84 : 56} key={index} />
+ ))}
+ </div>
+ )
+}
export default PreferredBrandSkeleton
diff --git a/src/lib/product/api/productSimilarApi.js b/src/lib/product/api/productSimilarApi.js
index 93c7f22c..c1bccd59 100644
--- a/src/lib/product/api/productSimilarApi.js
+++ b/src/lib/product/api/productSimilarApi.js
@@ -1,9 +1,30 @@
+import odooApi from '@/core/api/odooApi'
import axios from 'axios'
+import productSearchApi from './productSearchApi'
-const productSimilarApi = async ({ query }) => {
+const productSimilarApi = async ({ query, source }) => {
+ let dataflashSale = null
+ const flashSale = await odooApi('GET', '/api/v1/flashsale/header')
+ if (flashSale && flashSale.length > 0) {
+ const dataFlash = await productSearchApi({
+ query: `fq=flashsale_id_i:${flashSale[0].pricelistId}&fq=flashsale_price_f:[1 TO *]&limit=${
+ source === 'bottom' ? '4' : '1'
+ }`,
+ operation: 'AND'
+ })
+ if (source === 'bottom') {
+ dataflashSale = dataFlash.response.products.slice('2', '4')
+ } else {
+ dataflashSale = dataFlash.response.products
+ }
+ }
const dataProductSimilar = await axios(
- `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=${query}&page=1&orderBy=popular&operation=OR`
+ `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=${query}&page=1&orderBy=popular-weekly&operation=OR`
)
+ dataProductSimilar.data.response.products = [
+ ...dataflashSale,
+ ...dataProductSimilar.data.response.products,
+ ];
return dataProductSimilar.data.response
}
diff --git a/src/lib/product/components/Product/ProductDesktop.jsx b/src/lib/product/components/Product/ProductDesktop.jsx
index 7e1d0d3b..3b9296a8 100644
--- a/src/lib/product/components/Product/ProductDesktop.jsx
+++ b/src/lib/product/components/Product/ProductDesktop.jsx
@@ -18,6 +18,9 @@ import odooApi from '@/core/api/odooApi'
import { Button, Spinner } from 'flowbite-react'
import PromotionType from '@/lib/promotinProgram/components/PromotionType'
import useAuth from '@/core/hooks/useAuth'
+import ImageNext from 'next/image'
+import CountDown2 from '@/core/components/elements/CountDown/CountDown2'
+import CountDown from '@/core/components/elements/CountDown/CountDown'
const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
const router = useRouter()
@@ -33,6 +36,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
const [promotionType, setPromotionType] = useState(false)
const [promotionActiveId, setPromotionActiveId] = useState(null)
const [selectVariantPromoActive, setSelectVariantPromoActive] = useState(null)
+ const [backgorundFlashSale, setBackgorundFlashSale] = useState(null)
const getLowestPrice = useCallback(() => {
const prices = product.variants.map((variant) => variant.price)
@@ -47,6 +51,14 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
setLowestPrice(lowest)
}, [getLowestPrice])
+ useEffect(() => {
+ const getBackgound = async () => {
+ const get = await odooApi('GET', '/api/v1/banner?type=flash-sale-background-banner')
+ setBackgorundFlashSale(get[0].image)
+ }
+ getBackgound()
+ }, [])
+
const [informationTab, setInformationTab] = useState(informationTabOptions[0].value)
const variantQuantityRefs = useRef([])
@@ -54,7 +66,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
const setVariantQuantityRef = (variantId) => (element) => {
if (element) {
let variantIndex = product.variants.findIndex((varian) => varian.id == variantId)
- product.variants[variantIndex].quantity = element.value
+ product.variants[variantIndex].quantity = element?.value
}
variantQuantityRefs.current[variantId] = element
}
@@ -96,7 +108,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
router.push(`/login?next=/shop/product/${slug}`)
return
}
-
+
const quantity = variantQuantityRefs.current[variantId].value
if (!validQuantity(quantity)) return
@@ -106,6 +118,15 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
setAddCartAlert(true)
}
+ const handleQuantityChange = (variantId) => (event) => {
+ const { value } = event.target
+ const variantIndex = product.variants.findIndex((variant) => variant.id === variantId)
+ if (variantIndex !== -1) {
+ product.variants[variantIndex].quantity = parseInt(value, 10) // Pastikan untuk mengubah ke tipe number jika diperlukan
+ // Lakukan sesuatu jika nilai quantity diubah
+ }
+ }
+
const handleBuy = (variant) => {
const quantity = variantQuantityRefs.current[variant].value
if (!validQuantity(quantity)) return
@@ -142,7 +163,8 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
useEffect(() => {
const loadProductSimilarInBrand = async () => {
const productSimilarQuery = [product?.name, `fq=-product_id_i:${product.id}`].join('&')
- const dataProductSimilar = await productSimilarApi({ query: productSimilarQuery })
+ const source = 'right'
+ const dataProductSimilar = await productSimilarApi({ query: productSimilarQuery, source })
setProductSimilarInBrand(dataProductSimilar.products)
}
if (!productSimilarInBrand) loadProductSimilarInBrand()
@@ -171,11 +193,52 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
<div className='flex'>
<div className='w-full flex flex-wrap'>
<div className='w-5/12'>
- <Image
- src={product.image}
- alt={product.name}
- className='h-[430px] object-contain object-center w-full border border-gray_r-4'
- />
+ <div className='relative mb-2'>
+ {product?.flashSale?.remainingTime > 0 && (
+ <div className={`absolute bottom-0 w-full`}>
+ <div className='absolute bottom-0 w-full h-full'>
+ <ImageNext src={backgorundFlashSale || '/images/GAMBAR-BG-FLASH-SALE.jpg'} width={1000} height={100} />
+ </div>
+ <div className='relative'>
+ <div className='flex gap-x-2 items-center p-2'>
+ <div className='bg-yellow-400 rounded-full p-1 h-9 w-20 flex items-center justify-center '>
+ <span className='text-lg font-bold'>
+ {product.lowestPrice.discountPercentage}%
+ </span>
+ </div>
+ <div
+ className={`bg-red-600 border border-solid border-yellow-400 rounded-full h-9 p-2 flex w-[50%] items-center justify-center gap-x-4`}
+ >
+ <ImageNext
+ src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg'
+ width={17}
+ height={10}
+ />
+ <span className='text-white text-lg font-semibold'>
+ {product.flashSale.tag || 'FLASH SALE'}
+ </span>
+ </div>
+ <div>
+ <CountDown2 initialTime={product.flashSale.remainingTime} />
+ </div>
+ </div>
+ </div>
+ </div>
+ )}
+ <Image
+ src={product.image}
+ alt={product.name}
+ className='h-[430px] object-contain object-center w-full border border-gray_r-4'
+ />
+ </div>
+ <div>
+ <p className='text-justify text-xs leading-5'>
+ <span className='font-semibold '>Keterangan : </span>Gambar atau foto berperan
+ sebagai ilustrasi produk. Kadang tidak sesuai dengan kondisi terbaru dengan
+ berbagai perubahan dan perbaikan. Hubungi tim sales kami untuk informasi yang
+ lebih baik perihal gambar di 021-2933 8828.
+ </p>
+ </div>
</div>
<div className='w-7/12 px-4'>
@@ -375,6 +438,18 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
<div className='text-gray_r-11 line-through text-caption-1'>
{currencyFormat(lowestPrice?.price)}
</div>
+ {product.flashSale.remainingTime > 0 && (
+ <div className='bg-red-600 rounded-full mb-1 p-2 pl-3 pr-3 flex w-fit items-center gap-x-1'>
+ <ImageNext
+ src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg'
+ width={15}
+ height={10}
+ />
+ <span className='text-white text-xs font-semibold'>
+ {product.flashSale.tag || 'FLASH SALE'}
+ </span>
+ </div>
+ )}
</div>
)}
<h3 className='text-danger-500 font-semibold mt-1 text-title-md'>
@@ -412,7 +487,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
type='number'
className='form-input w-16 py-2 text-center bg-gray_r-1'
ref={setVariantQuantityRef(product.variants[0].id)}
- onChange={setVariantQuantityRef(product.variants[0].id)}
+ onChange={handleQuantityChange(product.variants[0].id)}
defaultValue={1}
/>
<button
@@ -579,7 +654,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
type='number'
className='form-input w-16 py-2 text-center bg-gray_r-1'
ref={setVariantQuantityRef(variant.id)}
- onChange={setVariantQuantityRef(variant.id)}
+ onChange={handleQuantityChange(variant.id)}
defaultValue={1}
/>
</td>
diff --git a/src/lib/product/components/Product/ProductMobile.jsx b/src/lib/product/components/Product/ProductMobile.jsx
index 2edd1a5f..d25d0861 100644
--- a/src/lib/product/components/Product/ProductMobile.jsx
+++ b/src/lib/product/components/Product/ProductMobile.jsx
@@ -18,6 +18,8 @@ import PromotionType from '@/lib/promotinProgram/components/PromotionType'
import { gtagAddToCart } from '@/core/utils/googleTag'
import odooApi from '@/core/api/odooApi'
import { Button, Spinner } from 'flowbite-react'
+import ImageNext from 'next/image'
+import CountDown2 from '@/core/components/elements/CountDown/CountDown2'
const ProductMobile = ({ product, wishlist, toggleWishlist }) => {
const router = useRouter()
@@ -30,6 +32,7 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => {
const [isLoadingSLA, setIsLoadingSLA] = useState(true)
const [promotionType, setPromotionType] = useState(false)
const [promotionActiveId, setPromotionActiveId] = useState(null)
+ const [backgorundFlashSale, setBackgorundFlashSale] = useState(null)
const getLowestPrice = () => {
const prices = product.variants.map((variant) => variant.price)
@@ -39,6 +42,16 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => {
return lowest
}
+ useEffect(() => {
+ const getBackgound = async () => {
+ const get = await odooApi('GET', '/api/v1/banner?type=flash-sale-background-banner')
+ if (get.length > 0) {
+ setBackgorundFlashSale(get[0].image)
+ }
+ }
+ getBackgound()
+ }, [])
+
const [activeVariant, setActiveVariant] = useState({
id: null,
code: product.code,
@@ -69,11 +82,11 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => {
product.variants = variantData
setIsLoadingSLA(false)
- if(product.variants.length === 1){
+ if (product.variants.length === 1) {
setActiveVariant({
id: product.variants[0].id,
code: product.variants[0].code,
- name: product.variants[0].parent.name ,
+ name: product.variants[0].parent.name,
price: product.variants[0].price,
stock: product.variants[0].stock,
weight: product.variants[0].weight,
@@ -143,7 +156,7 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => {
quantity,
programLineId: promotionActiveId,
selected: true,
- source : 'buy'
+ source: 'buy'
})
router.push(`/shop/checkout?source=buy`)
}
@@ -156,11 +169,48 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => {
return (
<MobileView>
- <Image
- src={product.image}
- alt={product.name}
- className='h-72 object-contain object-center w-full border-b border-gray_r-4'
- />
+ <div className='relative'>
+ {product?.flashSale?.remainingTime > 0 && (
+ <div className={`absolute bottom-0 w-full`}>
+ <div className='absolute bottom-0 w-full'>
+ <ImageNext
+ src={backgorundFlashSale || '/images/GAMBAR-BG-FLASH-SALE.jpg'}
+ width={1000}
+ height={100}
+ />
+ </div>
+ <div className='relative'>
+ <div className='flex gap-x-2 items-center p-2'>
+ <div className='bg-yellow-400 rounded-full p-1 h-9 w-20 flex items-center justify-center '>
+ <span className='text-lg font-bold'>
+ {product.lowestPrice.discountPercentage}%
+ </span>
+ </div>
+ <div
+ className={`bg-red-600 border border-solid border-yellow-400 rounded-full h-9 p-2 flex w-[50%] items-center justify-center gap-x-4`}
+ >
+ <ImageNext
+ src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg'
+ width={17}
+ height={10}
+ />
+ <span className='text-white text-lg font-semibold'>
+ {product.flashSale.tag || 'FLASH SALE'}
+ </span>
+ </div>
+ <div>
+ <CountDown2 initialTime={product.flashSale.remainingTime} />
+ </div>
+ </div>
+ </div>
+ </div>
+ )}
+ <Image
+ src={product.image}
+ alt={product.name}
+ className='h-72 object-contain object-center w-full border-b border-gray_r-4'
+ />
+ </div>
<div className='p-4'>
<div className='flex items-end mb-2'>
@@ -294,40 +344,44 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => {
<Spinner aria-label='Alternate spinner button example' />
<span className='pl-3'>Loading...</span>
</Button>
- ) : selectedVariant ? activeVariant?.sla?.slaDate != '-' ? (
- <button
- type='button'
- title={`Masa Persiapan Barang ${activeVariant?.sla?.slaDate}`}
- className={`flex gap-x-1 items-center p-2 h-8 rounded-lg w-full ${
- activeVariant?.sla?.slaDate === 'indent' ? 'bg-indigo-900' : 'btn-light'
- }`}
- >
- <div
- className={`flex-1 text-sm ${
- activeVariant?.sla?.slaDate === 'indent' ? 'text-white' : ''
+ ) : selectedVariant ? (
+ activeVariant?.sla?.slaDate != '-' ? (
+ <button
+ type='button'
+ title={`Masa Persiapan Barang ${activeVariant?.sla?.slaDate}`}
+ className={`flex gap-x-1 items-center p-2 h-8 rounded-lg w-full ${
+ activeVariant?.sla?.slaDate === 'indent' ? 'bg-indigo-900' : 'btn-light'
}`}
>
- {activeVariant?.sla?.slaDate}
- </div>
- <div className='flex-end'>
- <svg
- aria-hidden='true'
- fill='none'
- stroke='currentColor'
- stroke-width='1.5'
- className={`w-7 h-7 text-sm ${
+ <div
+ className={`flex-1 text-sm ${
activeVariant?.sla?.slaDate === 'indent' ? 'text-white' : ''
}`}
>
- <path
- d='M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z'
- stroke-linecap='round'
- stroke-linejoin='round'
- ></path>
- </svg>
- </div>
- </button>
- ):('-') : (
+ {activeVariant?.sla?.slaDate}
+ </div>
+ <div className='flex-end'>
+ <svg
+ aria-hidden='true'
+ fill='none'
+ stroke='currentColor'
+ stroke-width='1.5'
+ className={`w-7 h-7 text-sm ${
+ activeVariant?.sla?.slaDate === 'indent' ? 'text-white' : ''
+ }`}
+ >
+ <path
+ d='M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z'
+ stroke-linecap='round'
+ stroke-linejoin='round'
+ ></path>
+ </svg>
+ </div>
+ </button>
+ ) : (
+ '-'
+ )
+ ) : (
'-'
)}
</span>
diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx
index a8964310..2c849bd6 100644
--- a/src/lib/product/components/ProductCard.jsx
+++ b/src/lib/product/components/ProductCard.jsx
@@ -3,8 +3,12 @@ import Link from '@/core/components/elements/Link/Link'
import currencyFormat from '@/core/utils/currencyFormat'
import { createSlug } from '@/core/utils/slug'
import whatsappUrl from '@/core/utils/whatsappUrl'
+import ImageNext from 'next/image'
+import { useRouter } from 'next/router'
const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
+ const router = useRouter()
+
const callForPriceWhatsapp = whatsappUrl('product', {
name: product.name,
url: createSlug('/shop/product/', product.name, product.id, true)
@@ -12,7 +16,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
if (variant == 'vertical') {
return (
- <div className='rounded shadow-sm border border-gray_r-4 bg-white h-[350px]'>
+ <div className='rounded shadow-sm border border-gray_r-4 bg-white h-[300px] md:h-[350px]'>
<Link
href={createSlug('/shop/product/', product?.name, product?.id)}
className='border-b border-gray_r-4 relative'
@@ -22,6 +26,32 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
alt={product?.name}
className='w-full object-contain object-center h-36 sm:h-48'
/>
+ {router.pathname != '/' && product?.flashSale?.id > 0 && (
+ <div className='absolute bottom-0 w-full grid'>
+ <div className='absolute bottom-0 w-full h-full'>
+ <ImageNext src='/images/GAMBAR-BG-FLASH-SALE.jpg' className='h-full' width={1000} height={100} />
+ </div>
+ <div className='relative'>
+ <div className='flex gap-x-1 items-center p-2 justify-center'>
+ <div className='bg-yellow-400 rounded-full p-1 h-6 w-19 flex items-center justify-center '>
+ <span className='text-sm font-bold text-black'>
+ {product?.lowestPrice.discountPercentage}%
+ </span>
+ </div>
+ <div className='bg-red-600 border border-solid border-yellow-400 p-2 rounded-full h-6 flex w-fit items-center justify-center gap-x-2'>
+ <ImageNext
+ src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg'
+ width={13}
+ height={5}
+ />
+ <span className='text-white text-[11px] font-semibold'>
+ {product?.flashSale?.tag != "false" || product?.flashSale?.tag != product?.flashSale?.tag ? product?.flashSale?.tag : 'FLASH SALE'}
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ )}
{product.variantTotal > 1 && (
<div className='absolute badge-gray bottom-1.5 left-1.5'>
{product.variantTotal} Varian
@@ -101,6 +131,16 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
</Link>
</div>
<div className='w-8/12 p-2'>
+ {product.flashSale.id > 0 && (
+ <div className='bg-red-600 rounded-full mb-1 p-2 pl-3 pr-3 flex w-fit items-center gap-x-1'>
+ <ImageNext
+ src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg'
+ width={15}
+ height={10}
+ />
+ <span className='text-white text-xs font-semibold'>{product?.flashSale?.tag != "false" || product?.flashSale?.tag != product?.flashSale?.tag ? product?.flashSale?.tag : 'FLASH SALE'}</span>
+ </div>
+ )}
{product?.manufacture?.name ? (
<Link
href={createSlug(
diff --git a/src/lib/product/components/ProductSimilar.jsx b/src/lib/product/components/ProductSimilar.jsx
index 63a33089..1b82c2e5 100644
--- a/src/lib/product/components/ProductSimilar.jsx
+++ b/src/lib/product/components/ProductSimilar.jsx
@@ -3,7 +3,8 @@ import useProductSimilar from '../hooks/useProductSimilar'
import ProductSlider from './ProductSlider'
const ProductSimilar = ({ query }) => {
- const { productSimilar } = useProductSimilar({ query })
+ const source = "bottom"
+ const { productSimilar } = useProductSimilar({ query, source })
if (productSimilar.isLoading) {
return <PopularProductSkeleton />
diff --git a/src/lib/product/components/ProductSlider.jsx b/src/lib/product/components/ProductSlider.jsx
index b511eea5..dedbd6ab 100644
--- a/src/lib/product/components/ProductSlider.jsx
+++ b/src/lib/product/components/ProductSlider.jsx
@@ -66,7 +66,7 @@ const ProductSlider = ({ products, simpleTitle = false, bannerMode = false }) =>
</MobileView>
<DesktopView>
- <Swiper slidesPerView={5.6} spaceBetween={16} {...swiperProps}>
+ <Swiper slidesPerView={6.7} spaceBetween={16} {...swiperProps}>
{swiperContent}
</Swiper>
</DesktopView>
diff --git a/src/lib/product/hooks/useProductSimilar.js b/src/lib/product/hooks/useProductSimilar.js
index d16e4c58..712d07ad 100644
--- a/src/lib/product/hooks/useProductSimilar.js
+++ b/src/lib/product/hooks/useProductSimilar.js
@@ -1,10 +1,9 @@
import productSimilarApi from '../api/productSimilarApi'
import { useQuery } from 'react-query'
-const useProductSimilar = ({ query }) => {
- const fetchProductSimilar = async () => await productSimilarApi({ query })
+const useProductSimilar = ({ query, source }) => {
+ const fetchProductSimilar = async () => await productSimilarApi({ query, source })
const { data, isLoading } = useQuery(`productSimilar-${query}`, fetchProductSimilar)
-
return {
productSimilar: { data, isLoading }
}
diff --git a/src/lib/transaction/api/airwayBillApi.js b/src/lib/transaction/api/airwayBillApi.js
new file mode 100644
index 00000000..1ef579c7
--- /dev/null
+++ b/src/lib/transaction/api/airwayBillApi.js
@@ -0,0 +1,13 @@
+import odooApi from '@/core/api/odooApi'
+import { getAuth } from '@/core/utils/auth'
+
+const airwayBillApi = async ({ orderId }) => {
+ const auth = getAuth()
+ const dataAirwayBill = await odooApi(
+ 'GET',
+ `/api/v1/partner/${auth.partnerId}/sale-order/${orderId}/awb`
+ )
+ return dataAirwayBill
+}
+
+export default airwayBillApi
diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx
index dca85f0c..74f3dbd5 100644
--- a/src/lib/transaction/components/Transaction.jsx
+++ b/src/lib/transaction/components/Transaction.jsx
@@ -12,7 +12,6 @@ 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 MobileView from '@/core/components/views/MobileView'
@@ -21,9 +20,13 @@ import Menu from '@/lib/auth/components/Menu'
import Image from '@/core/components/elements/Image/Image'
import { createSlug } from '@/core/utils/slug'
import toTitleCase from '@/core/utils/toTitleCase'
+import useAirwayBill from '../hooks/useAirwayBill'
const Transaction = ({ id }) => {
const { transaction } = useTransaction({ id })
+ const { queryAirwayBill } = useAirwayBill({ orderId: id })
+
+ const [airwayBillPopup, setAirwayBillPopup] = useState(null)
const poNumber = useRef(null)
const poFile = useRef(null)
@@ -173,6 +176,35 @@ const Transaction = ({ id }) => {
<Divider />
+ <div className='p-4'>
+ <div className='font-medium'>Pengiriman</div>
+ <div className='flex flex-col gap-y-3 mt-4'>
+ {queryAirwayBill.data?.airways?.map((airway) => (
+ <button
+ className='shadow rounded-md p-4 text-gray_r-12 font-normal flex justify-between items-center text-left'
+ key={airway?.waybillNumber}
+ onClick={() => setAirwayBillPopup(airway?.waybillNumber)}
+ >
+ <div>
+ <span className='text-sm text-gray_r-11'>No Resi</span>
+ <p className='mt-1 font-medium'>{airway?.waybillNumber}</p>
+ </div>
+ <div className='flex gap-x-2'>
+ <div className='text-sm text-gray_r-11 badge-green'>
+ {airway?.deliveryStatus?.status}
+ </div>
+ <ChevronRightIcon className='w-5 stroke-2' />
+ </div>
+ </button>
+ ))}
+ </div>
+ {!queryAirwayBill?.data?.airways && (
+ <div className='badge-red text-sm px-2'>Belum ada pengiriman</div>
+ )}
+ </div>
+
+ <Divider />
+
<div className='p-4 flex flex-col gap-y-4'>
<DescriptionRow label='Purchase Order'>
{transaction.data?.purchaseOrderName || '-'}
@@ -229,9 +261,7 @@ const Transaction = ({ id }) => {
</Link>
))}
{transaction.data?.invoices?.length === 0 && (
- <Alert type='info' className='text-center'>
- Belum ada Invoice
- </Alert>
+ <div className='badge-red text-sm px-2'>Belum ada invoice</div>
)}
</div>
</div>
@@ -268,20 +298,36 @@ const Transaction = ({ id }) => {
<div className='w-3/12 pr-4'>
<Menu />
</div>
- <div className='w-9/12 p-4 bg-white border border-gray_r-6 rounded'>
+ <div className='w-9/12 p-4 py-6 bg-white border border-gray_r-6 rounded'>
<h1 className='text-title-sm font-semibold mb-6'>Detail Transaksi</h1>
<div className='flex items-center gap-x-2 mb-3'>
<span className='text-h-sm font-medium'>{transaction?.data?.name}</span>
<TransactionStatusBadge status={transaction?.data?.status} />
</div>
- <button
- type='button'
- className='btn-solid-red px-3 py-2'
- onClick={() => downloadQuotation(transaction.data)}
- >
- Download
- </button>
+ <div className='flex gap-x-4'>
+ <button
+ type='button'
+ className='btn-solid-red px-3 py-2 mr-auto'
+ onClick={() => downloadQuotation(transaction.data)}
+ >
+ Download
+ </button>
+ {transaction.data?.status == 'draft' && (
+ <button className='btn-yellow' onClick={checkout}>
+ Lanjutkan Transaksi
+ </button>
+ )}
+ {transaction.data?.status != 'draft' && (
+ <button
+ className='btn-light'
+ disabled={transaction.data?.status != 'waiting'}
+ onClick={openCancelTransaction}
+ >
+ Batalkan Transaksi
+ </button>
+ )}
+ </div>
<div className='grid grid-cols-2 gap-x-6 mt-6'>
<div className='grid grid-cols-2 gap-y-4'>
@@ -313,7 +359,7 @@ const Transaction = ({ id }) => {
</div>
</div>
- <div className='text-h-sm font-semibold mt-6 mb-4'>Informasi Pelanggan</div>
+ <div className='text-h-sm font-semibold mt-10 mb-4'>Informasi Pelanggan</div>
<div className='grid grid-cols-2 gap-x-4'>
<div className='border border-gray_r-6 rounded p-3'>
<div className='font-medium mb-4'>Detail Pelanggan</div>
@@ -321,7 +367,32 @@ const Transaction = ({ id }) => {
</div>
</div>
- <div className='text-h-sm font-semibold mt-6 mb-4'>Rincian Pembelian</div>
+ <div className='text-h-sm font-semibold mt-10 mb-4'>Pengiriman</div>
+ <div className='grid grid-cols-3 gap-4'>
+ {queryAirwayBill.data?.airways?.map((airway) => (
+ <button
+ className='shadow rounded-md p-4 text-gray_r-12 font-normal flex justify-between items-center text-left'
+ key={airway?.waybillNumber}
+ onClick={() => setAirwayBillPopup(airway?.waybillNumber)}
+ >
+ <div>
+ <span className='text-sm text-gray_r-11'>No Resi</span>
+ <p className='mt-1 font-medium'>{airway?.waybillNumber}</p>
+ </div>
+ <div className='flex gap-x-2'>
+ <div className='text-sm text-gray_r-11 badge-green'>
+ {airway?.deliveryStatus?.status}
+ </div>
+ <ChevronRightIcon className='w-5 stroke-2' />
+ </div>
+ </button>
+ ))}
+ </div>
+ {!queryAirwayBill.data?.airways && (
+ <div className='badge-red text-sm'>Belum ada pengiriman</div>
+ )}
+
+ <div className='text-h-sm font-semibold mt-10 mb-4'>Rincian Pembelian</div>
<table className='table-data'>
<thead>
<tr>
@@ -401,7 +472,7 @@ const Transaction = ({ id }) => {
{currencyFormat(transaction.data?.amountTax)}
</div>
- <div className='text-right'>Biaya Pengiriman</div>
+ <div className='text-right whitespace-nowrap'>Biaya Pengiriman</div>
<div className='text-right font-medium'>
{currencyFormat(transaction.data?.deliveryAmount)}
</div>
@@ -413,8 +484,8 @@ const Transaction = ({ id }) => {
</div>
</div>
- <div className='text-h-sm font-semibold mt-6 mb-4'>Invoice</div>
- <div className='grid grid-cols-4 gap-4'>
+ <div className='text-h-sm font-semibold mt-10 mb-4'>Invoice</div>
+ <div className='grid grid-cols-3 gap-4'>
{transaction.data?.invoices?.map((invoice, index) => (
<Link href={`/my/invoices/${invoice.id}`} key={index}>
<div className='shadow rounded-md p-4 text-gray_r-12 font-normal flex justify-between'>
@@ -437,30 +508,63 @@ const Transaction = ({ id }) => {
))}
</div>
{transaction.data?.invoices?.length === 0 && (
- <Alert type='info' className='text-center'>
- Belum ada Invoice
- </Alert>
+ <div className='badge-red text-sm'>Belum ada invoice</div>
)}
-
- <div className='mt-6'>
- {transaction.data?.status == 'draft' && (
- <button className='btn-yellow' onClick={checkout}>
- Lanjutkan Transaksi
- </button>
- )}
- {transaction.data?.status != 'draft' && (
- <button
- className='btn-light'
- disabled={transaction.data?.status != 'waiting'}
- onClick={openCancelTransaction}
- >
- Batalkan Transaksi
- </button>
- )}
- </div>
</div>
</div>
</DesktopView>
+
+ {queryAirwayBill.data?.airways?.map((airway) => (
+ <BottomPopup
+ key={airway.waybillNumber}
+ title='Detail Pengiriman'
+ active={airwayBillPopup == airway.waybillNumber}
+ close={() => setAirwayBillPopup(null)}
+ >
+ <div className='flex flex-col gap-y-4 my-4'>
+ <div className='flex justify-between'>
+ <div className='text-gray_r-11'>No Pengiriman</div>
+ <div>{airway?.deliveryOrder?.name}</div>
+ </div>
+ <div className='flex justify-between'>
+ <div className='text-gray_r-11'>Kurir</div>
+ <div>{airway?.deliveryOrder?.carrier}</div>
+ </div>
+ <div className='flex justify-between'>
+ <div className='text-gray_r-11'>No Resi</div>
+ <div>{airway?.waybillNumber}</div>
+ </div>
+ </div>
+
+ <div className='pt-4'>
+ <div className='font-semibold text-body-1 mb-4'>Status Pengiriman</div>
+ <ol class='relative border-l border-gray_r-7'>
+ {airway?.manifests?.map((manifest, index) => (
+ <li class='mb-6 ml-4' key={index}>
+ <div
+ class={`absolute w-3 h-3 rounded-full mt-1.5 -left-1.5 border ${
+ index == 0 ? 'bg-red-600 border-red-600' : 'bg-gray_r-7 border-white'
+ }`}
+ />
+ <time class='text-sm leading-none text-gray-400'>
+ {manifest.datetime}
+ </time>
+ <p
+ class={`leading-6 font-medium text-body-2 mt-2 ${
+ index == 0 ? 'text-red-600' : 'text-gray_r-11'
+ }`}
+ >
+ {manifest.description}
+ </p>
+ </li>
+ ))}
+ {(!airway?.manifests || airway?.manifests?.length == 0) && (
+ <div className='badge-red text-sm'>Belum ada pengiriman</div>
+ )}
+ </ol>
+ </div>
+ </BottomPopup>
+ ))}
</>
)
)
@@ -536,7 +640,7 @@ const SectionContent = ({ address }) => {
const DescriptionRow = ({ children, label }) => (
<div className='grid grid-cols-2'>
<span className='text-gray_r-11'>{label}</span>
- <span className='text-right break-all leading-6'>{children}</span>
+ <span className='text-right leading-6'>{children}</span>
</div>
)
diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx
index 642881a6..be63effd 100644
--- a/src/lib/transaction/components/Transactions.jsx
+++ b/src/lib/transaction/components/Transactions.jsx
@@ -252,7 +252,7 @@ const Transactions = ({ context = '' }) => {
{transactions.data?.saleOrders?.map((saleOrder) => (
<tr key={saleOrder.id}>
<td>
- <Link href={`${router.pathname}/${saleOrder.id}`}>{saleOrder.name}</Link>
+ <Link className='whitespace-nowrap' href={`${router.pathname}/${saleOrder.id}`}>{saleOrder.name}</Link>
</td>
<td>{saleOrder.purchaseOrderName || '-'}</td>
<td>{saleOrder.dateOrder || '-'}</td>
diff --git a/src/lib/transaction/hooks/useAirwayBill.js b/src/lib/transaction/hooks/useAirwayBill.js
new file mode 100644
index 00000000..c711ab77
--- /dev/null
+++ b/src/lib/transaction/hooks/useAirwayBill.js
@@ -0,0 +1,11 @@
+import { useQuery } from 'react-query'
+import airwayBillApi from '../api/airwayBillApi'
+
+const useAirwayBill = ({ orderId }) => {
+ const fetchAirwayBill = async () => await airwayBillApi({ orderId })
+ const queryAirwayBill = useQuery(`airwayBill-${orderId}`, fetchAirwayBill)
+
+ return { queryAirwayBill }
+}
+
+export default useAirwayBill
diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx
index 86cbe963..7f23b94b 100644
--- a/src/pages/_app.jsx
+++ b/src/pages/_app.jsx
@@ -1,4 +1,5 @@
import '../styles/globals.css'
+import 'react-loading-skeleton/dist/skeleton.css'
import NextProgress from 'next-progress'
import { useRouter, Router } from 'next/router'
import { AnimatePresence, motion } from 'framer-motion'
diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js
index 937c6d4c..d465d94b 100644
--- a/src/pages/api/shop/search.js
+++ b/src/pages/api/shop/search.js
@@ -135,7 +135,8 @@ const productResponseMap = (products, pricelist) => {
categories: [],
flashSale: {
id: product?.flashsale_id_i,
- name: product?.product?.flashsale_name_s
+ name: product?.product?.flashsale_name_s,
+ tag : product?.flashsale_tag_s || 'FLASH SALE'
}
}
diff --git a/src/pages/index.jsx b/src/pages/index.jsx
index 12d2ab46..47a0a493 100644
--- a/src/pages/index.jsx
+++ b/src/pages/index.jsx
@@ -7,6 +7,8 @@ import DelayRender from '@/core/components/elements/DelayRender/DelayRender'
import { HeroBannerSkeleton } from '@/components/skeleton/BannerSkeleton'
import { PopularProductSkeleton } from '@/components/skeleton/PopularProductSkeleton'
import PromotinProgram from '@/lib/promotinProgram/components/HomePage'
+import PreferredBrandSkeleton from '@/lib/home/components/Skeleton/PreferredBrandSkeleton'
+import { FlashSaleSkeleton } from '@/lib/flashSale/skeleton/FlashSaleSkeleton'
const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout'))
const HeroBanner = dynamic(() => import('@/components/ui/HeroBanner'), {
@@ -19,9 +21,13 @@ const PopularProduct = dynamic(() => import('@/components/ui/PopularProduct'), {
loading: () => <PopularProductSkeleton />
})
-const PreferredBrand = dynamic(() => import('@/lib/home/components/PreferredBrand'))
+const PreferredBrand = dynamic(() => import('@/lib/home/components/PreferredBrand'), {
+ loading: () => <PreferredBrandSkeleton />
+})
-const FlashSale = dynamic(() => import('@/lib/flashSale/components/FlashSale'))
+const FlashSale = dynamic(() => import('@/lib/flashSale/components/FlashSale'), {
+ loading: () => <FlashSaleSkeleton />
+})
const BannerSection = dynamic(() => import('@/lib/home/components/BannerSection'))
const CategoryHomeId = dynamic(() => import('@/lib/home/components/CategoryHomeId'))
const CustomerReviews = dynamic(() => import('@/lib/review/components/CustomerReviews'))
@@ -66,22 +72,12 @@ export default function Home() {
</div>
<div className='my-16 flex flex-col gap-y-16'>
- <DelayRender renderAfter={400}>
- <PreferredBrand />
- </DelayRender>
- <DelayRender renderAfter={600}>
- <FlashSale />
- </DelayRender>
- <DelayRender renderAfter={600}>
- <PromotinProgram />
- </DelayRender>
- <DelayRender renderAfter={1000}>
- <CategoryHomeId />
- <BannerSection />
- </DelayRender>
- <DelayRender renderAfter={1200}>
- <CustomerReviews />
- </DelayRender>
+ <PreferredBrand />
+ <FlashSale />
+ <PromotinProgram />
+ <CategoryHomeId />
+ <BannerSection />
+ <CustomerReviews />
</div>
</div>
</DesktopView>
@@ -98,8 +94,8 @@ export default function Home() {
<FlashSale />
</DelayRender>
<DelayRender renderAfter={600}>
- <PromotinProgram />
- </DelayRender>
+ <PromotinProgram />
+ </DelayRender>
<DelayRender renderAfter={800}>
<PopularProduct />
</DelayRender>