diff options
Diffstat (limited to 'src')
32 files changed, 1742 insertions, 920 deletions
diff --git a/src/core/components/elements/Navbar/NavbarUserDropdown.jsx b/src/core/components/elements/Navbar/NavbarUserDropdown.jsx index b735c13f..1d2429a7 100644 --- a/src/core/components/elements/Navbar/NavbarUserDropdown.jsx +++ b/src/core/components/elements/Navbar/NavbarUserDropdown.jsx @@ -1,17 +1,25 @@ import { deleteAuth } from '@/core/utils/auth' import Link from '../Link/Link' +import { useRouter } from 'next/router' const NavbarUserDropdown = () => { + const router = useRouter() + + const logout = () => { + deleteAuth() + router.push('/login') + } + return ( <div className='navbar-user-dropdown-wrapper'> <div className='navbar-user-dropdown'> - <Link href='/'>Daftar Transaksi</Link> - <Link href='/'>Invoice & Faktur Pajak</Link> - <Link href='/'>Wishlist</Link> - <Link href='/'>Pengaturan Akun</Link> - <Link href='/login' onClick={deleteAuth}> + <Link href='/my/transactions'>Daftar Transaksi</Link> + <Link href='/my/invoices'>Invoice & Faktur Pajak</Link> + <Link href='/my/wishlist'>Wishlist</Link> + <Link href='/my/address'>Daftar Alamat</Link> + <button type='button' onClick={logout}> Keluar Akun - </Link> + </button> </div> </div> ) diff --git a/src/core/components/elements/Popup/BottomPopup.jsx b/src/core/components/elements/Popup/BottomPopup.jsx index 95c75473..c244330c 100644 --- a/src/core/components/elements/Popup/BottomPopup.jsx +++ b/src/core/components/elements/Popup/BottomPopup.jsx @@ -49,8 +49,8 @@ const BottomPopup = ({ children, active = false, title, close }) => { <DesktopView> <motion.div initial={{ bottom: '35%', opacity: 0 }} - animate={{ bottom: '50%', opacity: 1 }} - exit={{ bottom: '35%', opacity: 0 }} + animate={{ bottom: '30%', opacity: 1 }} + exit={{ bottom: '25%', opacity: 0 }} transition={transition} className='fixed left-1/2 -translate-x-1/2 w-2/5 border border-gray_r-6 rounded-xl z-[60] p-4 pt-0 bg-white' > diff --git a/src/core/components/elements/Skeleton/PriceSkeleton.jsx b/src/core/components/elements/Skeleton/PriceSkeleton.jsx new file mode 100644 index 00000000..0dc4c70f --- /dev/null +++ b/src/core/components/elements/Skeleton/PriceSkeleton.jsx @@ -0,0 +1,9 @@ +const PriceSkeleton = () => ( + <div role='status' className='max-w-sm rounded animate-pulse my-2'> + <div className='h-2.5 bg-gray_r-6 rounded-full w-3/12 mb-1'></div> + <div className='h-2.5 bg-gray_r-6 rounded-full w-6/12 mb-1'></div> + <span className='sr-only'>Loading...</span> + </div> +) + +export default PriceSkeleton diff --git a/src/core/hooks/useDevice.js b/src/core/hooks/useDevice.js index 387330b0..a8584692 100644 --- a/src/core/hooks/useDevice.js +++ b/src/core/hooks/useDevice.js @@ -9,11 +9,9 @@ const useDevice = () => { if (window.innerWidth < 768) { setIsMobile(true) setIsDesktop(false) - console.count('m: true | d: false') } else { setIsMobile(false) setIsDesktop(true) - console.count('m: false | d: true') } } handleResize() diff --git a/src/lib/address/components/Addresses.jsx b/src/lib/address/components/Addresses.jsx index 3ac06b6c..a2adecb1 100644 --- a/src/lib/address/components/Addresses.jsx +++ b/src/lib/address/components/Addresses.jsx @@ -4,13 +4,15 @@ import useAuth from '@/core/hooks/useAuth' import { getItemAddress, updateItemAddress } from '@/core/utils/address' import { useRouter } from 'next/router' import useAddresses from '../hooks/useAddresses' +import MobileView from '@/core/components/views/MobileView' +import DesktopView from '@/core/components/views/DesktopView' +import Menu from '@/lib/auth/components/Menu' const Addresses = () => { const router = useRouter() const { select = null } = router.query - const auth = useAuth() const { addresses } = useAddresses() - const selectedAdress = getItemAddress(select || '') + const selectedAddress = getItemAddress(select || '') const changeSelectedAddress = (id) => { if (!select) return updateItemAddress(select, id) @@ -26,43 +28,96 @@ const Addresses = () => { } return ( - <div className='p-4'> - <div className='text-right'> - <Link href='/my/address/create'>Tambah Alamat</Link> - </div> + <> + <MobileView> + <div className='p-4'> + <div className='text-right'> + <Link href='/my/address/create'>Tambah Alamat</Link> + </div> + + <div className='grid gap-y-4 mt-4'> + {addresses.data?.map((address, index) => { + const type = address.type.charAt(0).toUpperCase() + address.type.slice(1) + ' Address' + return ( + <AddressCard + key={index} + address={address} + type={type} + changeSelectedAddress={changeSelectedAddress} + selectedAddress={selectedAddress} + select={select} + /> + ) + })} + </div> + </div> + </MobileView> - <div className='grid gap-y-4 mt-4'> - {addresses.data?.map((address, index) => { - let type = address.type.charAt(0).toUpperCase() + address.type.slice(1) + ' Address' - return ( - <div - key={index} - className={ - 'p-4 rounded-md border ' + - (selectedAdress && selectedAdress == address.id - ? 'border-gray_r-7 bg-gray_r-4' - : 'border-gray_r-7') - } - > - <div onClick={() => changeSelectedAddress(address.id)}> - <div className='flex gap-x-2'> - <div className='badge-red'>{type}</div> - {auth?.partnerId == address.id && <div className='badge-green'>Utama</div>} - </div> - <p className='font-medium mt-2'>{address.name}</p> - {address.mobile && <p className='mt-2 text-gray_r-11'>{address.mobile}</p>} - <p className='mt-1 leading-6 text-gray_r-11'>{address.street}</p> - </div> - <Link - href={`/my/address/${address.id}/edit`} - className='btn-light bg-white mt-3 w-full !text-gray_r-11' - > - Ubah Alamat + <DesktopView> + <div className='container mx-auto flex py-10'> + <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='flex items-center mb-6'> + <h1 className='text-title-sm font-semibold'>Daftar Alamat</h1> + <Link href='/my/address/create' className='btn-solid-red py-2 px-3 text-gray_r-1 h-fit ml-auto'> + Tambah Alamat </Link> </div> - ) - })} + + <div className='grid grid-cols-2 gap-4'> + {addresses.data?.map((address, index) => { + const type = + address.type.charAt(0).toUpperCase() + address.type.slice(1) + ' Address' + return ( + <AddressCard + key={index} + address={address} + type={type} + changeSelectedAddress={changeSelectedAddress} + selectedAddress={selectedAddress} + select={select} + /> + ) + })} + </div> + </div> + </div> + </DesktopView> + </> + ) +} + +const AddressCard = ({ address, selectedAddress, changeSelectedAddress, type, select }) => { + const auth = useAuth() + + return ( + <div + className={`p-4 rounded-md border + ${ + (selectedAddress && selectedAddress == address.id + ? 'border-gray_r-7 bg-gray_r-4' + : 'border-gray_r-7') + + ' ' + + (select && 'cursor-pointer hover:bg-gray_r-4 transition') + }`} + > + <div onClick={() => changeSelectedAddress(address.id)} className={select && 'cursor-pointer'}> + <div className='flex gap-x-2'> + <div className='badge-red'>{type}</div> + {auth?.partnerId == address.id && <div className='badge-green'>Utama</div>} + </div> + <p className='font-medium mt-2'>{address.name}</p> + {address.mobile && <p className='mt-2 text-gray_r-11'>{address.mobile}</p>} + <p className='mt-1 leading-6 text-gray_r-11'>{address.street}</p> </div> + <Link + href={`/my/address/${address.id}/edit`} + className='btn-light bg-white mt-3 w-full !text-gray_r-11' + > + Ubah Alamat + </Link> </div> ) } diff --git a/src/lib/address/components/CreateAddress.jsx b/src/lib/address/components/CreateAddress.jsx index 849b4c01..475d8548 100644 --- a/src/lib/address/components/CreateAddress.jsx +++ b/src/lib/address/components/CreateAddress.jsx @@ -10,6 +10,7 @@ import { useEffect, useState } from 'react' import createAddressApi from '../api/createAddressApi' import { toast } from 'react-hot-toast' import { yupResolver } from '@hookform/resolvers/yup' +import Menu from '@/lib/auth/components/Menu' const CreateAddress = () => { const auth = useAuth() @@ -88,133 +89,119 @@ const CreateAddress = () => { } return ( - <form - className='p-4 flex flex-col gap-y-4' - onSubmit={handleSubmit(onSubmitHandler)} - > - <div> - <label className='form-label mb-2'>Label Alamat</label> - <Controller - name='type' - control={control} - render={(props) => ( - <HookFormSelect - {...props} - isSearchable={false} - options={types} - /> - )} - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.type?.message}</div> + <div className='max-w-none md:container mx-auto flex p-0 md:py-10'> + <div className='hidden md:block w-3/12 pr-4'> + <Menu /> </div> - - <div> - <label className='form-label mb-2'>Nama</label> - <input - {...register('name')} - placeholder='John Doe' - type='text' - className='form-input' - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.name?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Email</label> - <input - {...register('email')} - placeholder='contoh@email.com' - type='email' - className='form-input' - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.email?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Mobile</label> - <input - {...register('mobile')} - placeholder='08xxxxxxxx' - type='tel' - className='form-input' - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.mobile?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Alamat</label> - <input - {...register('street')} - placeholder='Jl. Bandengan Utara 85A' - type='text' - className='form-input' - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.street?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Kode Pos</label> - <input - {...register('zip')} - placeholder='10100' - type='number' - className='form-input' - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.zip?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Kota</label> - <Controller - name='city' - control={control} - render={(props) => ( - <HookFormSelect - {...props} - options={cities} - /> - )} - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.city?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Kecamatan</label> - <Controller - name='district' - control={control} - render={(props) => ( - <HookFormSelect - {...props} - options={districts} - disabled={!watchCity} - /> - )} - /> - </div> - - <div> - <label className='form-label mb-2'>Kelurahan</label> - <Controller - name='subDistrict' - control={control} - render={(props) => ( - <HookFormSelect - {...props} - options={subDistricts} - disabled={!watchDistrict} - /> - )} - /> + <div className='w-full md:w-9/12 p-4 bg-white border border-gray_r-6 rounded'> + <form onSubmit={handleSubmit(onSubmitHandler)}> + <div className='grid grid-cols-1 md:grid-cols-2 gap-4'> + <div> + <label className='form-label mb-2'>Label Alamat</label> + <Controller + name='type' + control={control} + render={(props) => ( + <HookFormSelect {...props} isSearchable={false} options={types} /> + )} + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.type?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Nama</label> + <input + {...register('name')} + placeholder='John Doe' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.name?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Email</label> + <input + {...register('email')} + placeholder='contoh@email.com' + type='email' + className='form-input' + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.email?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Mobile</label> + <input + {...register('mobile')} + placeholder='08xxxxxxxx' + type='tel' + className='form-input' + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.mobile?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Alamat</label> + <input + {...register('street')} + placeholder='Jl. Bandengan Utara 85A' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.street?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Kode Pos</label> + <input + {...register('zip')} + placeholder='10100' + type='number' + className='form-input' + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.zip?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Kota</label> + <Controller + name='city' + control={control} + render={(props) => <HookFormSelect {...props} options={cities} />} + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.city?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Kecamatan</label> + <Controller + name='district' + control={control} + render={(props) => ( + <HookFormSelect {...props} options={districts} disabled={!watchCity} /> + )} + /> + </div> + + <div> + <label className='form-label mb-2'>Kelurahan</label> + <Controller + name='subDistrict' + control={control} + render={(props) => ( + <HookFormSelect {...props} options={subDistricts} disabled={!watchDistrict} /> + )} + /> + </div> + </div> + + <button type='submit' className='btn-yellow w-full md:w-fit mt-6 ml-0 md:ml-auto'> + Simpan + </button> + </form> </div> - - <button - type='submit' - className='btn-yellow mt-2 w-full' - > - Simpan - </button> - </form> + </div> ) } diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx index a832edbc..d754cbd9 100644 --- a/src/lib/address/components/EditAddress.jsx +++ b/src/lib/address/components/EditAddress.jsx @@ -9,6 +9,7 @@ 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 Menu from '@/lib/auth/components/Menu' const EditAddress = ({ id, defaultValues }) => { const router = useRouter() @@ -102,133 +103,119 @@ const EditAddress = ({ id, defaultValues }) => { } return ( - <form - className='p-4 flex flex-col gap-y-4' - onSubmit={handleSubmit(onSubmitHandler)} - > - <div> - <label className='form-label mb-2'>Label Alamat</label> - <Controller - name='type' - control={control} - render={(props) => ( - <HookFormSelect - {...props} - isSearchable={false} - options={types} - /> - )} - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.type?.message}</div> + <div className='max-w-none md:container mx-auto flex p-0 md:py-10'> + <div className='hidden md:block w-3/12 pr-4'> + <Menu /> </div> - - <div> - <label className='form-label mb-2'>Nama</label> - <input - {...register('name')} - placeholder='John Doe' - type='text' - className='form-input' - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.name?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Email</label> - <input - {...register('email')} - placeholder='johndoe@example.com' - type='email' - className='form-input' - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.email?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Mobile</label> - <input - {...register('mobile')} - placeholder='08xxxxxxxx' - type='tel' - className='form-input' - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.mobile?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Alamat</label> - <input - {...register('street')} - placeholder='Jl. Bandengan Utara 85A' - type='text' - className='form-input' - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.street?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Kode Pos</label> - <input - {...register('zip')} - placeholder='10100' - type='number' - className='form-input' - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.zip?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Kota</label> - <Controller - name='city' - control={control} - render={(props) => ( - <HookFormSelect - {...props} - options={cities} - /> - )} - /> - <div className='text-caption-2 text-red_r-11 mt-1'>{errors.city?.message}</div> - </div> - - <div> - <label className='form-label mb-2'>Kecamatan</label> - <Controller - name='district' - control={control} - render={(props) => ( - <HookFormSelect - {...props} - options={districts} - disabled={!watchCity} - /> - )} - /> - </div> - - <div> - <label className='form-label mb-2'>Kelurahan</label> - <Controller - name='subDistrict' - control={control} - render={(props) => ( - <HookFormSelect - {...props} - options={subDistricts} - disabled={!watchDistrict} - /> - )} - /> + <div className='w-full md:w-9/12 p-4 bg-white border border-gray_r-6 rounded'> + <h1 className='text-title-sm font-semibold mb-6 hidden md:block'>Ubah Alamat</h1> + <form onSubmit={handleSubmit(onSubmitHandler)}> + <div className='grid grid-cols-1 md:grid-cols-2 gap-4'> + <div> + <label className='form-label mb-2'>Label Alamat</label> + <Controller + name='type' + control={control} + render={(props) => ( + <HookFormSelect {...props} isSearchable={false} options={types} /> + )} + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.type?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Nama</label> + <input + {...register('name')} + placeholder='John Doe' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.name?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Email</label> + <input + {...register('email')} + placeholder='johndoe@example.com' + type='email' + className='form-input' + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.email?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Mobile</label> + <input + {...register('mobile')} + placeholder='08xxxxxxxx' + type='tel' + className='form-input' + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.mobile?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Alamat</label> + <input + {...register('street')} + placeholder='Jl. Bandengan Utara 85A' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.street?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Kode Pos</label> + <input + {...register('zip')} + placeholder='10100' + type='number' + className='form-input' + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.zip?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Kota</label> + <Controller + name='city' + control={control} + render={(props) => <HookFormSelect {...props} options={cities} />} + /> + <div className='text-caption-2 text-red_r-11 mt-1'>{errors.city?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Kecamatan</label> + <Controller + name='district' + control={control} + render={(props) => ( + <HookFormSelect {...props} options={districts} disabled={!watchCity} /> + )} + /> + </div> + + <div> + <label className='form-label mb-2'>Kelurahan</label> + <Controller + name='subDistrict' + control={control} + render={(props) => ( + <HookFormSelect {...props} options={subDistricts} disabled={!watchDistrict} /> + )} + /> + </div> + </div> + <button type='submit' className='btn-yellow w-full md:w-fit mt-6 ml-0 md:ml-auto'> + Simpan + </button> + </form> </div> - - <button - type='submit' - className='btn-yellow mt-2 w-full' - > - Simpan - </button> - </form> + </div> ) } diff --git a/src/lib/auth/components/Menu.jsx b/src/lib/auth/components/Menu.jsx new file mode 100644 index 00000000..9a73609d --- /dev/null +++ b/src/lib/auth/components/Menu.jsx @@ -0,0 +1,47 @@ +import Link from '@/core/components/elements/Link/Link' +import { useRouter } from 'next/router' + +const Menu = () => { + const router = useRouter() + + const routeStartWith = (route) => router.pathname.startsWith(route) + + return ( + <div className='grid grid-cols-1 bg-white border border-gray_r-6 rounded py-2 px-4'> + <div className='mt-4 mb-1 font-medium'>Menu</div> + <LinkItem href='/my/transactions' active={routeStartWith('/my/transaction')}> + Daftar Transaksi + </LinkItem> + <LinkItem href='/my/invoices' active={routeStartWith('/my/invoice')}> + Invoice & Faktur Pajak + </LinkItem> + <LinkItem href='/my/wishlist' active={routeStartWith('/my/wishlist')}> + Wishlist + </LinkItem> + + <div className='mt-4 mb-1 font-medium'>Pusat Bantuan</div> + <LinkItem href='/'>Layanan Pelanggan</LinkItem> + + <div className='mt-4 mb-1 font-medium'>Pengaturan Akun</div> + <LinkItem href='/my/address' active={routeStartWith('/my/address')}> + Daftar Alamat + </LinkItem> + <button type='button' className='text-gray_r-12/80 p-2 text-left'> + Keluar Akun + </button> + </div> + ) +} + +const LinkItem = ({ children, ...props }) => ( + <Link + {...props} + className={`!text-gray_r-12/80 !font-normal p-2 rounded ${ + props.active == true ? 'bg-gray_r-3' : '' + }`} + > + {children} + </Link> +) + +export default Menu diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index 8bd9e362..8cd6df96 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -128,16 +128,16 @@ const Cart = () => { Apakah anda yakin menghapus barang{' '} <span className='underline'>{deleteConfirmation?.name}</span> dari keranjang? </div> - <div className='flex mt-6 gap-x-4'> + <div className='flex mt-6 gap-x-4 md:justify-end'> <button - className='btn-solid-red flex-1' + className='btn-solid-red flex-1 md:flex-none' type='button' onClick={() => deleteProduct(deleteConfirmation?.id)} > Ya, Hapus </button> <button - className='btn-light flex-1' + className='btn-light flex-1 md:flex-none' type='button' onClick={() => setDeleteConfirmation(null)} > @@ -302,6 +302,15 @@ const Cart = () => { </tr> </thead> <tbody> + {cart.isLoading && ( + <tr> + <td colSpan={6}> + <div className='flex justify-center my-2'> + <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' /> + </div> + </td> + </tr> + )} {!cart.isLoading && (!products || products?.length == 0) && ( <tr> <td colSpan={6}>Keranjang belanja anda masih kosong</td> diff --git a/src/lib/invoice/components/Invoice.jsx b/src/lib/invoice/components/Invoice.jsx index e34ad8c2..355f36e5 100644 --- a/src/lib/invoice/components/Invoice.jsx +++ b/src/lib/invoice/components/Invoice.jsx @@ -4,10 +4,34 @@ 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' +import MobileView from '@/core/components/views/MobileView' +import DesktopView from '@/core/components/views/DesktopView' +import Menu from '@/lib/auth/components/Menu' +import Link from '@/core/components/elements/Link/Link' +import Image from '@/core/components/elements/Image/Image' +import { createSlug } from '@/core/utils/slug' +import { useEffect, useState } from 'react' const Invoice = ({ id }) => { const { invoice } = useInvoice({ id }) + const [totalAmount, setTotalAmount] = useState(0) + const [totalDiscountAmount, setTotalDiscountAmount] = useState(0) + + useEffect(() => { + if (invoice?.data?.products) { + let calculateTotalAmount = 0 + let calculateTotalDiscountAmount = 0 + invoice.data.products.forEach((product) => { + calculateTotalAmount += product.price.price * product.quantity + calculateTotalDiscountAmount += + (product.price.price - product.price.priceDiscount) * product.quantity + }) + setTotalAmount(calculateTotalAmount) + setTotalDiscountAmount(calculateTotalDiscountAmount) + } + }, [invoice]) + if (invoice.isLoading) { return ( <div className='flex justify-center my-6'> @@ -27,74 +51,226 @@ const Invoice = ({ id }) => { return ( invoice.data?.name && ( <> - <div className='flex flex-col gap-y-4 p-4'> - <DescriptionRow label='No Invoice'>{invoice.data?.name}</DescriptionRow> - <DescriptionRow label='Status Transaksi'> - {invoice.data?.amountResidual > 0 ? ( - <span className='badge-solid-red'>Belum Lunas</span> - ) : ( - <span className='badge-solid-green'>Lunas</span> - )} - </DescriptionRow> - <DescriptionRow label='Purchase Order'> - {invoice.data?.purchaseOrderName || '-'} - </DescriptionRow> - <DescriptionRow label='Ketentuan Pembayaran'>{invoice.data?.paymentTerm}</DescriptionRow> - {invoice.data?.amountResidual > 0 && invoice.invoiceDate != invoice.invoiceDateDue && ( - <DescriptionRow label='Tanggal Jatuh Tempo'> - {invoice.data?.invoiceDateDue} + <MobileView> + <div className='flex flex-col gap-y-4 p-4'> + <DescriptionRow label='No Invoice'>{invoice.data?.name}</DescriptionRow> + <DescriptionRow label='Status Transaksi'> + {invoice.data?.amountResidual > 0 ? ( + <span className='badge-solid-red'>Belum Lunas</span> + ) : ( + <span className='badge-solid-green'>Lunas</span> + )} + </DescriptionRow> + <DescriptionRow label='Purchase Order'> + {invoice.data?.purchaseOrderName || '-'} + </DescriptionRow> + <DescriptionRow label='Ketentuan Pembayaran'> + {invoice.data?.paymentTerm} </DescriptionRow> - )} - <DescriptionRow label='Nama Sales'>{invoice.data?.sales}</DescriptionRow> - <DescriptionRow label='Tanggal Invoice'>{invoice.data?.invoiceDate}</DescriptionRow> - <div className='flex items-center'> - <p className='text-gray_r-11 leading-none'>Invoice</p> - <button - type='button' - className='btn-light py-1.5 px-3 ml-auto' - onClick={() => downloadInvoice(invoice.data)} - > - Download - </button> + {invoice.data?.amountResidual > 0 && invoice.invoiceDate != invoice.invoiceDateDue && ( + <DescriptionRow label='Tanggal Jatuh Tempo'> + {invoice.data?.invoiceDateDue} + </DescriptionRow> + )} + <DescriptionRow label='Nama Sales'>{invoice.data?.sales}</DescriptionRow> + <DescriptionRow label='Tanggal Invoice'>{invoice.data?.invoiceDate}</DescriptionRow> + <div className='flex items-center'> + <p className='text-gray_r-11 leading-none'>Invoice</p> + <button + type='button' + className='btn-light py-1.5 px-3 ml-auto' + onClick={() => downloadInvoice(invoice.data)} + > + Download + </button> + </div> + <div className='flex items-center'> + <p className='text-gray_r-11 leading-none'>Faktur Pajak</p> + <button + type='button' + className='btn-light py-1.5 px-3 ml-auto' + onClick={() => downloadTaxInvoice(invoice.data)} + disabled={!invoice.data?.efaktur} + > + Download + </button> + </div> </div> - <div className='flex items-center'> - <p className='text-gray_r-11 leading-none'>Faktur Pajak</p> - <button - type='button' - className='btn-light py-1.5 px-3 ml-auto' - onClick={() => downloadTaxInvoice(invoice.data)} - disabled={!invoice.data?.efaktur} - > - Download - </button> + + <Divider /> + + <div className='p-4 font-medium'>Detail Penagihan</div> + + <div className='flex flex-col gap-y-4 p-4 border-t border-gray_r-6'> + <DescriptionRow label='Nama'>{address?.name}</DescriptionRow> + <DescriptionRow label='Email'>{address?.email || '-'}</DescriptionRow> + <DescriptionRow label='No Telepon'>{address?.mobile || '-'}</DescriptionRow> + <DescriptionRow label='Alamat'>{fullAddress}</DescriptionRow> </div> - </div> - <Divider /> + <Divider /> + + <div className='font-medium p-4'>Detail Produk</div> + + <div className='p-4 pt-0 flex flex-col gap-y-3'> + <VariantGroupCard variants={invoice.data?.products} buyMore /> + <div className='flex justify-between mt-3 font-medium'> + <p>Total Belanja</p> + <p>{currencyFormat(invoice.data?.amountTotal)}</p> + </div> + </div> + </MobileView> + + <DesktopView> + <div className='container mx-auto flex py-10'> + <div className='w-3/12 pr-4'> + <Menu /> + </div> + <div className='w-9/12 p-4 bg-white border border-gray_r-6 rounded'> + <h1 className='text-title-sm font-semibold mb-6'>Invoice & Faktur Pajak</h1> + + <div className='flex items-center gap-x-2 mb-3'> + <span className='text-h-sm font-medium'>{invoice?.data?.name}</span> + {invoice?.data?.amountResidual > 0 ? ( + <div className='badge-solid-red h-fit'>Belum Lunas</div> + ) : ( + <div className='badge-solid-green h-fit'>Lunas</div> + )} + </div> + + <button + type='button' + className='btn-solid-red px-3 py-2' + onClick={() => downloadInvoice(invoice.data)} + > + Download + </button> + + <div className='grid grid-cols-2 gap-x-6 mt-6'> + <div className='h-fit grid grid-cols-2 gap-y-4'> + <div>Tanggal Invoice</div> + <div>: {invoice?.data?.invoiceDate}</div> + + <div>Purchase Order</div> + <div> + :{' '} + <button + type='button' + className='inline-block text-red_r-11' + onClick={() => downloadInvoice(invoice.data)} + > + Download + </button> + </div> + + <div>Ketentuan Pembayaran</div> + <div>: {invoice?.data?.paymentTerm}</div> + </div> + <div className='h-fit grid grid-cols-2 gap-y-4'> + <div>Nama Sales</div> + <div>: {invoice?.data?.sales}</div> + + <div>Faktur Pajak</div> + <div> + :{' '} + {invoice.data?.efaktur ? ( + <button + type='button' + className='inline-block text-red_r-11' + onClick={() => downloadTaxInvoice(invoice?.data)} + > + Download + </button> + ) : ( + '-' + )} + </div> + </div> + </div> - <div className='p-4 font-medium'>Detail Penagihan</div> + <div className='text-h-sm font-semibold mt-6 mb-4'>Rincian Pembelian</div> + <table className='table-data'> + <thead> + <tr> + <th>Nama Produk</th> + <th>Jumlah</th> + <th>Harga</th> + <th>Diskon</th> + <th>Subtotal</th> + </tr> + </thead> + <tbody> + {invoice?.data?.products?.map((product) => ( + <tr key={product.id}> + <td className='flex'> + <Link + href={createSlug( + '/shop/product/', + product?.parent.name, + product?.parent.id + )} + className='w-[30%] flex-shrink-0' + > + <Image + src={product?.parent?.image} + alt={product?.name} + className='object-contain object-center border border-gray_r-6 h-40 w-full rounded-md' + /> + </Link> + <div className='px-2 text-left'> + <Link + href={createSlug( + '/shop/product/', + product?.parent.name, + product?.parent.id + )} + className='line-clamp-2 leading-6 !text-gray_r-12 font-normal' + > + {product?.parent?.name} + </Link> + <div className='text-gray_r-11 mt-2'> + {product?.code}{' '} + {product?.attributes.length > 0 + ? `| ${product?.attributes.join(', ')}` + : ''} + </div> + </div> + </td> + <td>{product.quantity}</td> + <td>{currencyFormat(product.price.price)}</td> + <td> + {product.price.discountPercentage > 0 + ? `${product.price.discountPercentage}%` + : ''} + </td> + <td>{currencyFormat(product.price.priceDiscount * product.quantity)}</td> + </tr> + ))} + </tbody> + </table> - <div className='flex flex-col gap-y-4 p-4 border-t border-gray_r-6'> - <DescriptionRow label='Nama'>{address?.name}</DescriptionRow> - <DescriptionRow label='Email'>{address?.email || '-'}</DescriptionRow> - <DescriptionRow label='No Telepon'>{address?.mobile || '-'}</DescriptionRow> - <DescriptionRow label='Alamat'>{fullAddress}</DescriptionRow> - </div> + <div className='flex justify-end mt-4'> + <div className='w-1/4 grid grid-cols-2 gap-y-2 text-gray_r-12/80'> + <div className='text-right'>Subtotal</div> + <div className='text-right font-medium'>{currencyFormat(totalAmount)}</div> - <Divider /> + <div className='text-right'>Total Diskon</div> + <div className='text-right font-medium'> + {currencyFormat(-totalDiscountAmount)} + </div> - <div className='font-medium p-4'>Detail Produk</div> + <div className='text-right'>PPN 11% (Incl.)</div> + <div className='text-right font-medium'>{currencyFormat(totalAmount * 0.11)}</div> - <div className='p-4 pt-0 flex flex-col gap-y-3'> - <VariantGroupCard - variants={invoice.data?.products} - buyMore - /> - <div className='flex justify-between mt-3 font-medium'> - <p>Total Belanja</p> - <p>{currencyFormat(invoice.data?.amountTotal)}</p> + <div className='text-right'>Grand Total</div> + <div className='text-right font-medium text-gray_r-12'> + {currencyFormat(invoice.data?.amountTotal)} + </div> + </div> + </div> + </div> </div> - </div> + </DesktopView> </> ) ) diff --git a/src/lib/invoice/components/Invoices.jsx b/src/lib/invoice/components/Invoices.jsx index ab318a3c..51041316 100644 --- a/src/lib/invoice/components/Invoices.jsx +++ b/src/lib/invoice/components/Invoices.jsx @@ -16,6 +16,9 @@ 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 MobileView from '@/core/components/views/MobileView' +import DesktopView from '@/core/components/views/DesktopView' +import Menu from '@/lib/auth/components/Menu' const Invoices = () => { const router = useRouter() @@ -44,133 +47,201 @@ const Invoices = () => { } return ( - <div className='p-4 flex flex-col gap-y-4'> - <form - className='flex gap-x-3' - onSubmit={handleSubmit} - > - <input - type='text' - className='form-input' - placeholder='Cari Invoice...' - value={inputQuery} - onChange={(e) => setInputQuery(e.target.value)} - /> - <button - className='btn-light bg-transparent px-3' - type='submit' - > - <MagnifyingGlassIcon className='w-6' /> - </button> - </form> - - {invoices.isLoading && ( - <div className='flex justify-center my-4'> - <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' /> - </div> - )} - - {!invoices.isLoading && invoices.data?.invoices?.length === 0 && ( - <Alert - type='info' - className='text-center' - > - Tidak ada data invoice - </Alert> - )} - - {invoices.data?.invoices?.map((invoice, index) => ( - <div - className='p-4 shadow border border-gray_r-3 rounded-md' - key={index} - > - <div className='grid grid-cols-2'> - <Link href={`/my/invoice/${invoice.id}`}> - <span className='text-caption-2 text-gray_r-11'>No. Invoice</span> - <h2 className='text-red_r-11 mt-1'>{invoice.name}</h2> - </Link> - <div className='flex gap-x-1 justify-end'> - {invoice.amountResidual > 0 ? ( - <div className='badge-solid-red h-fit ml-auto'>Belum Lunas</div> - ) : ( - <div className='badge-solid-green h-fit ml-auto'>Lunas</div> - )} - <EllipsisVerticalIcon - className='w-5 h-5' - onClick={() => setToOthers(invoice)} - /> - </div> - </div> - <Link href={`/my/invoice/${invoice.id}`}> - <div className='grid grid-cols-2 text-caption-2 text-gray_r-11 mt-2 font-normal'> - <p>{invoice.invoiceDate}</p> - <p className='text-right'>{invoice.paymentTerm}</p> + <> + <MobileView> + <div className='p-4 flex flex-col gap-y-4'> + <form className='flex gap-x-3' onSubmit={handleSubmit}> + <input + type='text' + className='form-input' + placeholder='Cari Invoice...' + value={inputQuery} + onChange={(e) => setInputQuery(e.target.value)} + /> + <button className='btn-light bg-transparent px-3' type='submit'> + <MagnifyingGlassIcon className='w-6' /> + </button> + </form> + + {invoices.isLoading && ( + <div className='flex justify-center my-4'> + <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' /> </div> - <hr className='my-3' /> - <div className='grid grid-cols-2'> - <div> - <span className='text-caption-2 text-gray_r-11'>No. Purchase Order</span> - <p className='mt-1 font-medium text-gray_r-12'> - {invoice.purchaseOrderName || '-'} - </p> - </div> - <div className='text-right'> - <span className='text-caption-2 text-gray_r-11'>Total Invoice</span> - <p className='mt-1 font-medium text-gray_r-12'> - {currencyFormat(invoice.amountTotal)} - </p> + )} + + {!invoices.isLoading && invoices.data?.invoices?.length === 0 && ( + <Alert type='info' className='text-center'> + Tidak ada data invoice + </Alert> + )} + + {invoices.data?.invoices?.map((invoice, index) => ( + <div className='p-4 shadow border border-gray_r-3 rounded-md' key={index}> + <div className='grid grid-cols-2'> + <Link href={`/my/invoice/${invoice.id}`}> + <span className='text-caption-2 text-gray_r-11'>No. Invoice</span> + <h2 className='text-red_r-11 mt-1'>{invoice.name}</h2> + </Link> + <div className='flex gap-x-1 justify-end'> + {invoice.amountResidual > 0 ? ( + <div className='badge-solid-red h-fit ml-auto'>Belum Lunas</div> + ) : ( + <div className='badge-solid-green h-fit ml-auto'>Lunas</div> + )} + <EllipsisVerticalIcon className='w-5 h-5' onClick={() => setToOthers(invoice)} /> + </div> </div> + <Link href={`/my/invoice/${invoice.id}`}> + <div className='grid grid-cols-2 text-caption-2 text-gray_r-11 mt-2 font-normal'> + <p>{invoice.invoiceDate}</p> + <p className='text-right'>{invoice.paymentTerm}</p> + </div> + <hr className='my-3' /> + <div className='grid grid-cols-2'> + <div> + <span className='text-caption-2 text-gray_r-11'>No. Purchase Order</span> + <p className='mt-1 font-medium text-gray_r-12'> + {invoice.purchaseOrderName || '-'} + </p> + </div> + <div className='text-right'> + <span className='text-caption-2 text-gray_r-11'>Total Invoice</span> + <p className='mt-1 font-medium text-gray_r-12'> + {currencyFormat(invoice.amountTotal)} + </p> + </div> + </div> + </Link> + {invoice.efaktur ? ( + <div className='badge-green h-fit mt-3 ml-auto flex items-center gap-x-0.5'> + <CheckIcon className='w-4 stroke-2' /> + Faktur Pajak + </div> + ) : ( + <div className='badge-red h-fit mt-3 ml-auto flex items-center gap-x-0.5'> + <ClockIcon className='w-4 stroke-2' /> + Faktur Pajak + </div> + )} </div> - </Link> - {invoice.efaktur ? ( - <div className='badge-green h-fit mt-3 ml-auto flex items-center gap-x-0.5'> - <CheckIcon className='w-4 stroke-2' /> - Faktur Pajak - </div> - ) : ( - <div className='badge-red h-fit mt-3 ml-auto flex items-center gap-x-0.5'> - <ClockIcon className='w-4 stroke-2' /> - Faktur Pajak + ))} + + <Pagination + pageCount={pageCount} + currentPage={parseInt(page)} + url={`/my/invoices${pageQuery}`} + className='mt-2 mb-2' + /> + + <BottomPopup title='Lainnya' active={toOthers} close={() => setToOthers(null)}> + <div className='flex flex-col gap-y-4 mt-2'> + <button + className='text-left disabled:opacity-60' + onClick={() => { + downloadInvoice(toOthers) + setToOthers(null) + }} + > + Download Invoice + </button> + <button + className='text-left disabled:opacity-60' + disabled={!toOthers?.efaktur} + onClick={() => { + downloadTaxInvoice(toOthers) + setToOthers(null) + }} + > + Download Faktur Pajak + </button> </div> - )} + </BottomPopup> </div> - ))} - - <Pagination - pageCount={pageCount} - currentPage={parseInt(page)} - url={`/my/invoices${pageQuery}`} - className='mt-2 mb-2' - /> - - <BottomPopup - title='Lainnya' - active={toOthers} - close={() => setToOthers(null)} - > - <div className='flex flex-col gap-y-4 mt-2'> - <button - className='text-left disabled:opacity-60' - onClick={() => { - downloadInvoice(toOthers) - setToOthers(null) - }} - > - Download Invoice - </button> - <button - className='text-left disabled:opacity-60' - disabled={!toOthers?.efaktur} - onClick={() => { - downloadTaxInvoice(toOthers) - setToOthers(null) - }} - > - Download Faktur Pajak - </button> + </MobileView> + + <DesktopView> + <div className='container mx-auto flex py-10'> + <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='flex mb-6 items-center justify-between'> + <h1 className='text-title-sm font-semibold'> + Invoice & Faktur Pajak{' '} + {invoices?.data?.invoices ? `(${invoices?.data?.invoices.length})` : ''} + </h1> + <form className='flex gap-x-2' onSubmit={handleSubmit}> + <input + type='text' + className='form-input' + placeholder='Cari Invoice...' + value={inputQuery} + onChange={(e) => setInputQuery(e.target.value)} + /> + <button className='btn-light bg-transparent px-3' type='submit'> + <MagnifyingGlassIcon className='w-6' /> + </button> + </form> + </div> + + <table className='table-data'> + <thead> + <tr> + <th>No. Invoice</th> + <th>No. PO</th> + <th>Tanggal</th> + <th className='!text-left'>Salesperson</th> + <th>Status</th> + <th className='!text-left'>Total</th> + </tr> + </thead> + <tbody> + {invoices.isLoading && ( + <tr> + <td colSpan={5}> + <div className='flex justify-center my-2'> + <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' /> + </div> + </td> + </tr> + )} + {!invoices.isLoading && + (!invoices?.data?.invoices || invoices?.data?.invoices?.length == 0) && ( + <tr> + <td colSpan={6}>Tidak ada data invoice</td> + </tr> + )} + {invoices.data?.invoices?.map((invoice) => ( + <tr key={invoice.id}> + <td> + <Link href={`/my/invoice/${invoice.id}`}>{invoice.name}</Link> + </td> + <td>{invoice.purchaseOrderName || '-'}</td> + <td>{invoice.invoiceDate}</td> + <td className='!text-left'>{invoice.sales}</td> + <td> + {invoice.amountResidual > 0 ? ( + <div className='badge-solid-red h-fit mx-auto'>Belum Lunas</div> + ) : ( + <div className='badge-solid-green h-fit mx-auto'>Lunas</div> + )} + </td> + <td className='!text-left'>{currencyFormat(invoice.amountTotal)}</td> + </tr> + ))} + </tbody> + </table> + + <Pagination + pageCount={pageCount} + currentPage={parseInt(page)} + url={`/my/transactions${pageQuery}`} + className='mt-2 mb-2' + /> + </div> </div> - </BottomPopup> - </div> + </DesktopView> + </> ) } diff --git a/src/lib/product/api/productPriceApi.js b/src/lib/product/api/productPriceApi.js new file mode 100644 index 00000000..94a68216 --- /dev/null +++ b/src/lib/product/api/productPriceApi.js @@ -0,0 +1,8 @@ +import odooApi from '@/core/api/odooApi' + +const productPriceApi = async ({ id }) => { + const dataProductPrice = await odooApi('GET', `/api/v1/product/template/price/${id}`) + return dataProductPrice +} + +export default productPriceApi diff --git a/src/lib/product/components/Product/ProductDesktop.jsx b/src/lib/product/components/Product/ProductDesktop.jsx index 98b40400..663d5a74 100644 --- a/src/lib/product/components/Product/ProductDesktop.jsx +++ b/src/lib/product/components/Product/ProductDesktop.jsx @@ -8,8 +8,12 @@ import LazyLoad from 'react-lazy-load' import ProductSimilar from '../ProductSimilar' import { toast } from 'react-hot-toast' import { updateItemCart } from '@/core/utils/cart' +import useVariantPrice from '@/lib/variant/hooks/useVariantPrice' +import useProductPrice from '../../hooks/useProductPrice' +import PriceSkeleton from '@/core/components/elements/Skeleton/PriceSkeleton' const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { + const { productPrice } = useProductPrice({ id: product.id }) const [informationTab, setInformationTab] = useState(informationTabOptions[0].value) const variantQuantityRefs = useRef([]) @@ -104,28 +108,33 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { {product.variants.length > 1 && product.lowestPrice.priceDiscount > 0 && ( <div className='text-gray_r-12/80'>Harga mulai dari: </div> )} - {product?.lowestPrice.discountPercentage > 0 && ( - <div className='flex gap-x-1 items-center mt-2'> - <div className='badge-solid-red text-caption-1'> - {product?.lowestPrice.discountPercentage}% - </div> - <div className='text-gray_r-11 line-through text-caption-1'> - {currencyFormat(product?.lowestPrice.price)} - </div> - </div> + {productPrice.isLoading && <PriceSkeleton />} + {productPrice.isFetched && ( + <> + {productPrice?.data?.discount > 0 && ( + <div className='flex gap-x-1 items-center mt-2'> + <div className='badge-solid-red text-caption-1'> + {productPrice?.data?.discount}% + </div> + <div className='text-gray_r-11 line-through text-caption-1'> + {currencyFormat(productPrice?.data?.priceExclude)} + </div> + </div> + )} + <h3 className='text-red_r-11 font-semibold mt-1 text-title-md'> + {productPrice?.data?.priceExcludeAfterDiscount > 0 ? ( + currencyFormat(productPrice?.data?.priceExcludeAfterDiscount) + ) : ( + <span className='text-gray_r-11 leading-6 font-normal'> + Hubungi kami untuk dapatkan harga terbaik, + <a href='https://wa.me/' className='text-red_r-11 underline'> + klik disini + </a> + </span> + )} + </h3> + </> )} - <h3 className='text-red_r-11 font-semibold mt-1 text-title-md'> - {product?.lowestPrice.priceDiscount > 0 ? ( - currencyFormat(product?.lowestPrice.priceDiscount) - ) : ( - <span className='text-gray_r-11 leading-6 font-normal'> - Hubungi kami untuk dapatkan harga terbaik, - <a href='https://wa.me/' className='text-red_r-11 underline'> - klik disini - </a> - </span> - )} - </h3> <button type='button' onClick={goToVariantSection} @@ -166,14 +175,7 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { <td>{variant.code}</td> <td>{variant.attributes.join(', ')}</td> <td> - {variant.price.discountPercentage > 0 && ( - <> - <span className='line-through text-caption-1 text-gray_r-11'> - {currencyFormat(variant.price.price)} - </span>{' '} - </> - )} - {currencyFormat(variant.price.priceDiscount)} + <VariantPrice id={variant.id} /> </td> <td> <input @@ -245,6 +247,25 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { ) } +const VariantPrice = ({ id }) => { + const { variantPrice } = useVariantPrice({ id }) + + if (variantPrice.isLoading) return <PriceSkeleton /> + + return ( + <> + {variantPrice?.data?.discount > 0 && ( + <> + <span className='line-through text-caption-1 text-gray_r-11'> + {currencyFormat(variantPrice?.data?.priceExclude)} + </span>{' '} + </> + )} + {currencyFormat(variantPrice?.data?.priceExcludeAfterDiscount)} + </> + ) +} + const informationTabOptions = [ { value: 'description', label: 'Deskripsi' }, { value: 'information', label: 'Info Penting' } diff --git a/src/lib/product/components/Product/ProductMobile.jsx b/src/lib/product/components/Product/ProductMobile.jsx index c572a58e..f6ed3d40 100644 --- a/src/lib/product/components/Product/ProductMobile.jsx +++ b/src/lib/product/components/Product/ProductMobile.jsx @@ -11,6 +11,8 @@ import { HeartIcon } from '@heroicons/react/24/outline' import { useRouter } from 'next/router' import MobileView from '@/core/components/views/MobileView' import { toast } from 'react-hot-toast' +import useVariantPrice from '@/lib/variant/hooks/useVariantPrice' +import PriceSkeleton from '@/core/components/elements/Skeleton/PriceSkeleton' const ProductMobile = ({ product, wishlist, toggleWishlist }) => { const router = useRouter() @@ -115,26 +117,7 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { </button> </div> <h1 className='leading-6 font-medium'>{activeVariant?.name}</h1> - {activeVariant?.price?.discountPercentage > 0 && ( - <div className='flex gap-x-1 items-center mt-2'> - <div className='text-gray_r-11 line-through text-caption-1'> - {currencyFormat(activeVariant?.price?.price)} - </div> - <div className='badge-solid-red'>{activeVariant?.price?.discountPercentage}%</div> - </div> - )} - <h3 className='text-red_r-11 font-semibold mt-1'> - {activeVariant?.price?.priceDiscount > 0 ? ( - currencyFormat(activeVariant?.price?.priceDiscount) - ) : ( - <span className='text-gray_r-11 leading-6 font-normal'> - Hubungi kami untuk dapatkan harga terbaik, - <a href='https://wa.me/' className='text-red_r-11 underline'> - klik disini - </a> - </span> - )} - </h3> + <VariantPrice id={activeVariant.id} /> </div> <Divider /> @@ -249,6 +232,37 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { ) } +const VariantPrice = ({ id }) => { + const { variantPrice } = useVariantPrice({ id }) + + if (variantPrice.isLoading) return <PriceSkeleton /> + + return ( + <> + {variantPrice?.data?.discount > 0 && ( + <div className='flex gap-x-1 items-center mt-2'> + <div className='text-gray_r-11 line-through text-caption-1'> + {currencyFormat(variantPrice?.data?.priceExclude)} + </div> + <div className='badge-solid-red'>{variantPrice?.data?.discount}%</div> + </div> + )} + <h3 className='text-red_r-11 font-semibold mt-1'> + {variantPrice?.data?.priceExcludeAfterDiscount > 0 ? ( + currencyFormat(variantPrice?.data?.priceExcludeAfterDiscount) + ) : ( + <span className='text-gray_r-11 leading-6 font-normal'> + Hubungi kami untuk dapatkan harga terbaik, + <a href='https://wa.me/' className='text-red_r-11 underline'> + klik disini + </a> + </span> + )} + </h3> + </> + ) +} + const informationTabOptions = [ { value: 'specification', label: 'Spesifikasi' }, { value: 'description', label: 'Deskripsi' }, diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index 3454d4fd..e48ab88a 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -2,6 +2,9 @@ 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 useProductPrice from '../hooks/useProductPrice' +import { LazyLoadComponent } from 'react-lazy-load-image-component' +import PriceSkeleton from '@/core/components/elements/Skeleton/PriceSkeleton' const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { if (variant == 'vertical') { @@ -45,22 +48,9 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { > {product?.name} </Link> - {product?.lowestPrice?.discountPercentage > 0 && ( - <div className='flex gap-x-1 mb-1 items-center'> - <div className='text-gray_r-11 line-through text-[11px] sm:text-caption-2'> - {currencyFormat(product?.lowestPrice?.price)} - </div> - <div className='badge-solid-red'>{product?.lowestPrice?.discountPercentage}%</div> - </div> - )} - - <div className='text-red_r-11 font-semibold mb-2'> - {product?.lowestPrice?.priceDiscount > 0 ? ( - currencyFormat(product?.lowestPrice?.priceDiscount) - ) : ( - <a href='https://wa.me/'>Call for price</a> - )} - </div> + <LazyLoadComponent> + <ProductCardPrice variant='vertical' id={product.id} /> + </LazyLoadComponent> {product?.stockTotal > 0 && ( <div className='flex gap-x-1'> <div className='badge-solid-red'>Ready Stock</div> @@ -116,30 +106,83 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { {product?.name} </Link> - {product?.lowestPrice?.discountPercentage > 0 && ( + <LazyLoadComponent> + <ProductCardPrice variant='horizontal' id={product.id} /> + </LazyLoadComponent> + {product?.stockTotal > 0 && ( + <div className='flex gap-x-1'> + <div className='badge-solid-red'>Ready Stock</div> + <div className='badge-gray'>{product?.stockTotal > 5 ? '> 5' : '< 5'}</div> + </div> + )} + </div> + </div> + ) + } +} + +const ProductCardPrice = ({ variant, id }) => { + const { productPrice } = useProductPrice({ id }) + + if (productPrice.isLoading) return <PriceSkeleton /> + + if (variant == 'vertical') { + return ( + productPrice.isFetched && ( + <> + {productPrice?.data?.discount > 0 && ( <div className='flex gap-x-1 mb-1 items-center'> - <div className='badge-solid-red'>{product?.lowestPrice?.discountPercentage}%</div> - <div className='text-gray_r-11 line-through text-caption-2'> - {currencyFormat(product?.lowestPrice?.price)} + <div className='text-gray_r-11 line-through text-[11px] sm:text-caption-2'> + {currencyFormat( + productPrice?.data?.priceStartFrom || productPrice?.data?.priceExclude + )} </div> + <div className='badge-solid-red'>{productPrice?.data?.discount}%</div> </div> )} <div className='text-red_r-11 font-semibold mb-2'> - {product?.lowestPrice?.priceDiscount > 0 ? ( - currencyFormat(product?.lowestPrice?.priceDiscount) + {productPrice?.data?.priceExcludeAfterDiscount > 0 ? ( + currencyFormat( + productPrice?.data?.priceDiscStartFrom || + productPrice?.data?.priceExcludeAfterDiscount + ) ) : ( <a href='https://wa.me/'>Call for price</a> )} </div> - {product?.stockTotal > 0 && ( - <div className='flex gap-x-1'> - <div className='badge-solid-red'>Ready Stock</div> - <div className='badge-gray'>{product?.stockTotal > 5 ? '> 5' : '< 5'}</div> + </> + ) + ) + } + + if (variant == 'horizontal') { + return ( + productPrice.isFetched && ( + <> + {productPrice?.data?.discount > 0 && ( + <div className='flex gap-x-1 mb-1 items-center'> + <div className='badge-solid-red'>{productPrice?.data?.discount}%</div> + <div className='text-gray_r-11 line-through text-caption-2'> + {currencyFormat( + productPrice?.data?.priceStartFrom || productPrice?.data?.priceExclude + )} + </div> </div> )} - </div> - </div> + + <div className='text-red_r-11 font-semibold mb-2'> + {productPrice?.data?.priceExcludeAfterDiscount > 0 ? ( + currencyFormat( + productPrice?.data?.priceDiscStartFrom || + productPrice?.data?.priceExcludeAfterDiscount + ) + ) : ( + <a href='https://wa.me/'>Call for price</a> + )} + </div> + </> + ) ) } } diff --git a/src/lib/product/hooks/useProductPrice.js b/src/lib/product/hooks/useProductPrice.js new file mode 100644 index 00000000..f8ef62b8 --- /dev/null +++ b/src/lib/product/hooks/useProductPrice.js @@ -0,0 +1,13 @@ +import { useQuery } from 'react-query' +import productPriceApi from '../api/productPriceApi' + +const useProductPrice = ({ id }) => { + const fetchProductPrice = async () => await productPriceApi({ id }) + const productPrice = useQuery(`productPrice-${id}`, fetchProductPrice, { + refetchOnWindowFocus: false + }) + + return { productPrice } +} + +export default useProductPrice diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index dc9338fa..104a7f2d 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -15,6 +15,12 @@ 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' +import DesktopView from '@/core/components/views/DesktopView' +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' const Transaction = ({ id }) => { const { transaction } = useTransaction({ id }) @@ -88,10 +94,7 @@ const Transaction = ({ id }) => { const memoizeVariantGroupCard = useMemo( () => ( <div className='p-4 pt-0 flex flex-col gap-y-3'> - <VariantGroupCard - variants={transaction.data?.products} - buyMore - /> + <VariantGroupCard variants={transaction.data?.products} buyMore /> <div className='flex justify-between mt-1'> <p className='text-gray_r-12/70'>Subtotal</p> <p>{currencyFormat(totalAmount)}</p> @@ -124,120 +127,6 @@ const Transaction = ({ id }) => { return ( transaction.data?.name && ( <> - <div className='flex flex-col gap-y-4 p-4'> - <DescriptionRow label='Status Transaksi'> - <div className='flex justify-end'> - <TransactionStatusBadge status={transaction.data?.status} /> - </div> - </DescriptionRow> - <DescriptionRow label='No Transaksi'>{transaction.data?.name}</DescriptionRow> - <DescriptionRow label='Ketentuan Pembayaran'> - {transaction.data?.paymentTerm} - </DescriptionRow> - <DescriptionRow label='Nama Sales'>{transaction.data?.sales}</DescriptionRow> - <DescriptionRow label='Waktu Transaksi'>{transaction.data?.dateOrder}</DescriptionRow> - </div> - - <Divider /> - - <div className='p-4 flex flex-col gap-y-4'> - <DescriptionRow label='Purchase Order'> - {transaction.data?.purchaseOrderName || '-'} - </DescriptionRow> - <div className='flex items-center'> - <p className='text-gray_r-11 leading-none'>Dokumen PO</p> - <button - type='button' - className='btn-light py-1.5 px-3 ml-auto' - onClick={ - transaction.data?.purchaseOrderFile - ? () => downloadPurchaseOrder(transaction.data) - : openUploadPo - } - > - {transaction.data?.purchaseOrderFile ? 'Download' : 'Upload'} - </button> - </div> - </div> - - <Divider /> - - <div className='font-medium p-4'>Detail Produk</div> - - {memoizeVariantGroupCard} - - <Divider /> - - <SectionAddress address={transaction.data?.address} /> - - <Divider /> - - <div className='p-4'> - <p className='font-medium'>Invoice</p> - <div className='flex flex-col gap-y-3 mt-4'> - {transaction.data?.invoices?.map((invoice, index) => ( - <Link - href={`/my/invoice/${invoice.id}`} - key={index} - > - <div className='shadow rounded-md p-4 text-gray_r-12 font-normal flex justify-between'> - <div> - <p className='mb-2'>{invoice?.name}</p> - <div className='flex items-center gap-x-1'> - {invoice.amountResidual > 0 ? ( - <div className='badge-red'>Belum Lunas</div> - ) : ( - <div className='badge-green'>Lunas</div> - )} - <p className='text-caption-2 text-gray_r-11'> - {currencyFormat(invoice.amountTotal)} - </p> - </div> - </div> - <ChevronRightIcon className='w-5 stroke-2' /> - </div> - </Link> - ))} - {transaction.data?.invoices?.length === 0 && ( - <Alert - type='info' - className='text-center' - > - Belum ada Invoice - </Alert> - )} - </div> - </div> - - <Divider /> - - <div className='p-4 pt-0'> - {transaction.data?.status == 'draft' && ( - <button - className='btn-yellow w-full mt-4' - onClick={checkout} - > - Lanjutkan Transaksi - </button> - )} - <button - className='btn-light w-full mt-4' - disabled={transaction.data?.status != 'draft'} - onClick={downloadQuotation} - > - Download Quotation - </button> - {transaction.data?.status != 'draft' && ( - <button - className='btn-light w-full mt-4' - disabled={transaction.data?.status != 'waiting'} - onClick={openCancelTransaction} - > - Batalkan Transaksi - </button> - )} - </div> - <BottomPopup active={cancelTransaction} close={closeCancelTransaction} @@ -255,54 +144,334 @@ const Transaction = ({ id }) => { > Ya, Batalkan </button> - <button - className='btn-light flex-1' - type='button' - onClick={closeCancelTransaction} - > + <button className='btn-light flex-1' type='button' onClick={closeCancelTransaction}> Batal </button> </div> </BottomPopup> - <BottomPopup - title='Upload PO' - close={closeUploadPo} - active={uploadPo} - > + <BottomPopup title='Upload PO' close={closeUploadPo} active={uploadPo}> <div> <label>Nomor PO</label> - <input - type='text' - className='form-input mt-3' - ref={poNumber} - /> + <input type='text' className='form-input mt-3' ref={poNumber} /> </div> <div className='mt-4'> <label>Dokumen PO</label> - <input - type='file' - className='form-input mt-3 py-2' - ref={poFile} - /> + <input type='file' className='form-input mt-3 py-2' ref={poFile} /> </div> <div className='grid grid-cols-2 gap-x-3 mt-6'> - <button - type='button' - className='btn-light w-full' - onClick={closeUploadPo} - > + <button type='button' className='btn-light w-full' onClick={closeUploadPo}> Batal </button> - <button - type='button' - className='btn-solid-red w-full' - onClick={submitUploadPo} - > + <button type='button' className='btn-solid-red w-full' onClick={submitUploadPo}> Upload </button> </div> </BottomPopup> + + <MobileView> + <div className='flex flex-col gap-y-4 p-4'> + <DescriptionRow label='Status Transaksi'> + <div className='flex justify-end'> + <TransactionStatusBadge status={transaction.data?.status} /> + </div> + </DescriptionRow> + <DescriptionRow label='No Transaksi'>{transaction.data?.name}</DescriptionRow> + <DescriptionRow label='Ketentuan Pembayaran'> + {transaction.data?.paymentTerm} + </DescriptionRow> + <DescriptionRow label='Nama Sales'>{transaction.data?.sales}</DescriptionRow> + <DescriptionRow label='Waktu Transaksi'>{transaction.data?.dateOrder}</DescriptionRow> + </div> + + <Divider /> + + <div className='p-4 flex flex-col gap-y-4'> + <DescriptionRow label='Purchase Order'> + {transaction.data?.purchaseOrderName || '-'} + </DescriptionRow> + <div className='flex items-center'> + <p className='text-gray_r-11 leading-none'>Dokumen PO</p> + <button + type='button' + className='btn-light py-1.5 px-3 ml-auto' + onClick={ + transaction.data?.purchaseOrderFile + ? () => downloadPurchaseOrder(transaction.data) + : openUploadPo + } + > + {transaction.data?.purchaseOrderFile ? 'Download' : 'Upload'} + </button> + </div> + </div> + + <Divider /> + + <div className='font-medium p-4'>Detail Produk</div> + + {memoizeVariantGroupCard} + + <Divider /> + + <SectionAddress address={transaction.data?.address} /> + + <Divider /> + + <div className='p-4'> + <p className='font-medium'>Invoice</p> + <div className='flex flex-col gap-y-3 mt-4'> + {transaction.data?.invoices?.map((invoice, index) => ( + <Link href={`/my/invoice/${invoice.id}`} key={index}> + <div className='shadow rounded-md p-4 text-gray_r-12 font-normal flex justify-between'> + <div> + <p className='mb-2'>{invoice?.name}</p> + <div className='flex items-center gap-x-1'> + {invoice.amountResidual > 0 ? ( + <div className='badge-red'>Belum Lunas</div> + ) : ( + <div className='badge-green'>Lunas</div> + )} + <p className='text-caption-2 text-gray_r-11'> + {currencyFormat(invoice.amountTotal)} + </p> + </div> + </div> + <ChevronRightIcon className='w-5 stroke-2' /> + </div> + </Link> + ))} + {transaction.data?.invoices?.length === 0 && ( + <Alert type='info' className='text-center'> + Belum ada Invoice + </Alert> + )} + </div> + </div> + + <Divider /> + + <div className='p-4 pt-0'> + {transaction.data?.status == 'draft' && ( + <button className='btn-yellow w-full mt-4' onClick={checkout}> + Lanjutkan Transaksi + </button> + )} + <button + className='btn-light w-full mt-4' + disabled={transaction.data?.status != 'draft'} + onClick={downloadQuotation} + > + Download Quotation + </button> + {transaction.data?.status != 'draft' && ( + <button + className='btn-light w-full mt-4' + disabled={transaction.data?.status != 'waiting'} + onClick={openCancelTransaction} + > + Batalkan Transaksi + </button> + )} + </div> + </MobileView> + + <DesktopView> + <div className='container mx-auto flex py-10'> + <div className='w-3/12 pr-4'> + <Menu /> + </div> + <div className='w-9/12 p-4 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' + disabled={transaction.data?.status != 'draft'} + onClick={downloadQuotation} + > + Download + </button> + + <div className='grid grid-cols-2 gap-x-6 mt-6'> + <div className='grid grid-cols-2 gap-y-4'> + <div>Nama Sales</div> + <div>: {transaction?.data?.sales}</div> + + <div>Tanggal Transaksi</div> + <div>: {transaction?.data?.dateOrder}</div> + </div> + <div className='grid grid-cols-2 gap-y-4'> + <div>Ketentuan Pembayaran</div> + <div>: {transaction?.data?.paymentTerm}</div> + + <div>Purchase Order</div> + <div> + : {transaction?.data?.purchaseOrderName}{' '} + <button + type='button' + className='inline-block text-red_r-11' + onClick={ + transaction.data?.purchaseOrderFile + ? () => downloadPurchaseOrder(transaction.data) + : openUploadPo + } + > + {transaction?.data?.purchaseOrderFile ? 'Download' : 'Upload'} + </button> + </div> + </div> + </div> + + <div className='text-h-sm font-semibold mt-6 mb-4'>Info Pengiriman</div> + <div className='grid grid-cols-3 gap-x-4'> + <div className='border border-gray_r-6 rounded p-3'> + <div className='font-medium mb-4'>Detail Pelanggan</div> + <SectionContent address={transaction?.data?.address?.customer} /> + </div> + <div className='border border-gray_r-6 rounded p-3'> + <div className='font-medium mb-4'>Detail Pengiriman</div> + <SectionContent address={transaction?.data?.address?.shipping} /> + </div> + <div className='border border-gray_r-6 rounded p-3'> + <div className='font-medium mb-4'>Detail Penagihan</div> + <SectionContent address={transaction?.data?.address?.invoice} /> + </div> + </div> + + <div className='text-h-sm font-semibold mt-6 mb-4'>Rincian Pembelian</div> + <table className='table-data'> + <thead> + <tr> + <th>Nama Produk</th> + <th>Jumlah</th> + <th>Harga</th> + <th>Diskon</th> + <th>Subtotal</th> + </tr> + </thead> + <tbody> + {transaction?.data?.products?.map((product) => ( + <tr key={product.id}> + <td className='flex'> + <Link + href={createSlug( + '/shop/product/', + product?.parent.name, + product?.parent.id + )} + className='w-[30%] flex-shrink-0' + > + <Image + src={product?.parent?.image} + alt={product?.name} + className='object-contain object-center border border-gray_r-6 h-40 w-full rounded-md' + /> + </Link> + <div className='px-2 text-left'> + <Link + href={createSlug( + '/shop/product/', + product?.parent.name, + product?.parent.id + )} + className='line-clamp-2 leading-6 !text-gray_r-12 font-normal' + > + {product?.parent?.name} + </Link> + <div className='text-gray_r-11 mt-2'> + {product?.code}{' '} + {product?.attributes.length > 0 + ? `| ${product?.attributes.join(', ')}` + : ''} + </div> + </div> + </td> + <td>{product.quantity}</td> + <td>{currencyFormat(product.price.price)}</td> + <td> + {product.price.discountPercentage > 0 + ? `${product.price.discountPercentage}%` + : ''} + </td> + <td>{currencyFormat(product.price.priceDiscount * product.quantity)}</td> + </tr> + ))} + </tbody> + </table> + + <div className='flex justify-end mt-4'> + <div className='w-1/4 grid grid-cols-2 gap-y-2 text-gray_r-12/80'> + <div className='text-right'>Subtotal</div> + <div className='text-right font-medium'>{currencyFormat(totalAmount)}</div> + + <div className='text-right'>Total Diskon</div> + <div className='text-right font-medium'> + {currencyFormat(-totalDiscountAmount)} + </div> + + <div className='text-right'>PPN 11% (Incl.)</div> + <div className='text-right font-medium'>{currencyFormat(totalAmount * 0.11)}</div> + + <div className='text-right'>Grand Total</div> + <div className='text-right font-medium text-gray_r-12'> + {currencyFormat(transaction.data?.amountTotal)} + </div> + </div> + </div> + + <div className='text-h-sm font-semibold mt-6 mb-4'>Invoice</div> + <div className='grid grid-cols-4 gap-4'> + {transaction.data?.invoices?.map((invoice, index) => ( + <Link href={`/my/invoice/${invoice.id}`} key={index}> + <div className='shadow rounded-md p-4 text-gray_r-12 font-normal flex justify-between'> + <div> + <p className='mb-2'>{invoice?.name}</p> + <div className='flex items-center gap-x-1'> + {invoice.amountResidual > 0 ? ( + <div className='badge-red'>Belum Lunas</div> + ) : ( + <div className='badge-green'>Lunas</div> + )} + <p className='text-caption-2 text-gray_r-11'> + {currencyFormat(invoice.amountTotal)} + </p> + </div> + </div> + <ChevronRightIcon className='w-5 stroke-2' /> + </div> + </Link> + ))} + </div> + {transaction.data?.invoices?.length === 0 && ( + <Alert type='info' className='text-center'> + Belum ada Invoice + </Alert> + )} + + <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> </> ) ) @@ -351,10 +520,7 @@ const SectionAddress = ({ address }) => { } const SectionButton = ({ label, active, toggle }) => ( - <button - className='p-4 font-medium flex justify-between w-full' - onClick={toggle} - > + <button className='p-4 font-medium flex justify-between w-full' onClick={toggle}> <span>{label}</span> {active ? <ChevronUpIcon className='w-5' /> : <ChevronDownIcon className='w-5' />} </button> @@ -363,13 +529,13 @@ const SectionButton = ({ label, active, toggle }) => ( const SectionContent = ({ address }) => { 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) + if (address?.subDistrict?.name) fullAddress.push(toTitleCase(address.subDistrict.name)) + if (address?.district?.name) fullAddress.push(toTitleCase(address.district.name)) + if (address?.city?.name) fullAddress.push(toTitleCase(address.city.name)) fullAddress = fullAddress.join(', ') return ( - <div className='flex flex-col gap-y-4 p-4 border-t border-gray_r-6'> + <div className='flex flex-col gap-y-4 p-4 md:p-0 border-t border-gray_r-6 md:border-0'> <DescriptionRow label='Nama'>{address.name}</DescriptionRow> <DescriptionRow label='Email'>{address.email || '-'}</DescriptionRow> <DescriptionRow label='No Telepon'>{address.mobile || '-'}</DescriptionRow> diff --git a/src/lib/transaction/components/TransactionStatusBadge.jsx b/src/lib/transaction/components/TransactionStatusBadge.jsx index 7372e4da..88467c2b 100644 --- a/src/lib/transaction/components/TransactionStatusBadge.jsx +++ b/src/lib/transaction/components/TransactionStatusBadge.jsx @@ -1,6 +1,6 @@ const TransactionStatusBadge = ({ status }) => { let badgeProps = { - className: ['h-fit'], + className: ['h-fit md:text-caption-2'], text: '' } switch (status) { diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index ccbdede2..30c670ae 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -15,6 +15,9 @@ 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 MobileView from '@/core/components/views/MobileView' +import DesktopView from '@/core/components/views/DesktopView' +import Menu from '@/lib/auth/components/Menu' const Transactions = () => { const router = useRouter() @@ -55,161 +58,222 @@ const Transactions = () => { } return ( - <div className='p-4 flex flex-col gap-y-4'> - <form - className='flex gap-x-3' - onSubmit={handleSubmit} - > - <input - type='text' - className='form-input' - placeholder='Cari Transaksi...' - value={inputQuery} - onChange={(e) => setInputQuery(e.target.value)} - /> - <button - className='btn-light bg-transparent px-3' - type='submit' - > - <MagnifyingGlassIcon className='w-6' /> - </button> - </form> - - {transactions.isLoading && ( - <div className='flex justify-center my-4'> - <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' /> - </div> - )} - - {!transactions.isLoading && transactions.data?.saleOrders?.length === 0 && ( - <Alert - type='info' - className='text-center' - > - Tidak ada data transaksi - </Alert> - )} - - {transactions.data?.saleOrders?.map((saleOrder, index) => ( - <div - className='p-4 shadow border border-gray_r-3 rounded-md' - key={index} - > - <div className='grid grid-cols-2'> - <Link href={`/my/transaction/${saleOrder.id}`}> - <span className='text-caption-2 text-gray_r-11'>No. Transaksi</span> - <h2 className='text-red_r-11 mt-1'>{saleOrder.name}</h2> - </Link> - <div className='flex gap-x-1 justify-end'> - <TransactionStatusBadge status={saleOrder.status} /> - <EllipsisVerticalIcon - className='w-5 h-5' - onClick={() => setToOthers(saleOrder)} - /> + <> + <MobileView> + <div className='p-4 flex flex-col gap-y-4'> + <form className='flex gap-x-3' onSubmit={handleSubmit}> + <input + type='text' + className='form-input' + placeholder='Cari Transaksi...' + value={inputQuery} + onChange={(e) => setInputQuery(e.target.value)} + /> + <button className='btn-light bg-transparent px-3' type='submit'> + <MagnifyingGlassIcon className='w-6' /> + </button> + </form> + + {transactions.isLoading && ( + <div className='flex justify-center my-4'> + <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' /> </div> - </div> - <Link href={`/my/transaction/${saleOrder.id}`}> - <div className='grid grid-cols-2 mt-3'> - <div> - <span className='text-caption-2 text-gray_r-11'>No. Purchase Order</span> - <p className='mt-1 font-medium text-gray_r-12'> - {saleOrder.purchaseOrderName || '-'} - </p> - </div> - <div className='text-right'> - <span className='text-caption-2 text-gray_r-11'>Total Invoice</span> - <p className='mt-1 font-medium text-gray_r-12'>{saleOrder.invoiceCount} Invoice</p> + )} + + {!transactions.isLoading && transactions.data?.saleOrders?.length === 0 && ( + <Alert type='info' className='text-center'> + Tidak ada data transaksi + </Alert> + )} + + {transactions.data?.saleOrders?.map((saleOrder, index) => ( + <div className='p-4 shadow border border-gray_r-3 rounded-md' key={index}> + <div className='grid grid-cols-2'> + <Link href={`/my/transaction/${saleOrder.id}`}> + <span className='text-caption-2 text-gray_r-11'>No. Transaksi</span> + <h2 className='text-red_r-11 mt-1'>{saleOrder.name}</h2> + </Link> + <div className='flex gap-x-1 justify-end'> + <TransactionStatusBadge status={saleOrder.status} /> + <EllipsisVerticalIcon + className='w-5 h-5' + onClick={() => setToOthers(saleOrder)} + /> + </div> </div> + <Link href={`/my/transaction/${saleOrder.id}`}> + <div className='grid grid-cols-2 mt-3'> + <div> + <span className='text-caption-2 text-gray_r-11'>No. Purchase Order</span> + <p className='mt-1 font-medium text-gray_r-12'> + {saleOrder.purchaseOrderName || '-'} + </p> + </div> + <div className='text-right'> + <span className='text-caption-2 text-gray_r-11'>Total Invoice</span> + <p className='mt-1 font-medium text-gray_r-12'> + {saleOrder.invoiceCount} Invoice + </p> + </div> + </div> + <div className='grid grid-cols-2 mt-3'> + <div> + <span className='text-caption-2 text-gray_r-11'>Sales</span> + <p className='mt-1 font-medium text-gray_r-12'>{saleOrder.sales}</p> + </div> + <div className='text-right'> + <span className='text-caption-2 text-gray_r-11'>Total Harga</span> + <p className='mt-1 font-medium text-gray_r-12'> + {currencyFormat(saleOrder.amountTotal)} + </p> + </div> + </div> + </Link> </div> - <div className='grid grid-cols-2 mt-3'> - <div> - <span className='text-caption-2 text-gray_r-11'>Sales</span> - <p className='mt-1 font-medium text-gray_r-12'>{saleOrder.sales}</p> - </div> - <div className='text-right'> - <span className='text-caption-2 text-gray_r-11'>Total Harga</span> - <p className='mt-1 font-medium text-gray_r-12'> - {currencyFormat(saleOrder.amountTotal)} - </p> - </div> + ))} + + <Pagination + pageCount={pageCount} + currentPage={parseInt(page)} + url={`/my/transactions${pageQuery}`} + className='mt-2 mb-2' + /> + + <BottomPopup title='Lainnya' active={toOthers} close={() => setToOthers(null)}> + <div className='flex flex-col gap-y-4 mt-2'> + <button + className='text-left disabled:opacity-60' + disabled={!toOthers?.purchaseOrderFile} + onClick={() => { + downloadPurchaseOrder(toOthers) + setToOthers(null) + }} + > + Download PO + </button> + <button + className='text-left disabled:opacity-60' + disabled={toOthers?.status != 'draft'} + onClick={() => { + downloadQuotation(toOthers) + setToOthers(null) + }} + > + Download Quotation + </button> + <button + className='text-left disabled:opacity-60' + disabled={toOthers?.status != 'waiting'} + onClick={() => { + setToCancel(toOthers) + setToOthers(null) + }} + > + Batalkan Transaksi + </button> </div> - </Link> - </div> - ))} - - <Pagination - pageCount={pageCount} - currentPage={parseInt(page)} - url={`/my/transactions${pageQuery}`} - className='mt-2 mb-2' - /> - - <BottomPopup - title='Lainnya' - active={toOthers} - close={() => setToOthers(null)} - > - <div className='flex flex-col gap-y-4 mt-2'> - <button - className='text-left disabled:opacity-60' - disabled={!toOthers?.purchaseOrderFile} - onClick={() => { - downloadPurchaseOrder(toOthers) - setToOthers(null) - }} - > - Download PO - </button> - <button - className='text-left disabled:opacity-60' - disabled={toOthers?.status != 'draft'} - onClick={() => { - downloadQuotation(toOthers) - setToOthers(null) - }} - > - Download Quotation - </button> - <button - className='text-left disabled:opacity-60' - disabled={toOthers?.status != 'waiting'} - onClick={() => { - setToCancel(toOthers) - setToOthers(null) - }} - > - Batalkan Transaksi - </button> - </div> - </BottomPopup> - - <BottomPopup - active={toCancel} - close={() => setToCancel(null)} - title='Batalkan Transaksi' - > - <div className='leading-7 text-gray_r-12/80'> - Apakah anda yakin membatalkan transaksi{' '} - <span className='underline'>{toCancel?.name}</span>? + </BottomPopup> + + <BottomPopup active={toCancel} close={() => setToCancel(null)} title='Batalkan Transaksi'> + <div className='leading-7 text-gray_r-12/80'> + Apakah anda yakin membatalkan transaksi{' '} + <span className='underline'>{toCancel?.name}</span>? + </div> + <div className='flex mt-6 gap-x-4'> + <button + className='btn-solid-red flex-1' + type='button' + onClick={submitCancelTransaction} + > + Ya, Batalkan + </button> + <button className='btn-light flex-1' type='button' onClick={() => setToCancel(null)}> + Batal + </button> + </div> + </BottomPopup> </div> - <div className='flex mt-6 gap-x-4'> - <button - className='btn-solid-red flex-1' - type='button' - onClick={submitCancelTransaction} - > - Ya, Batalkan - </button> - <button - className='btn-light flex-1' - type='button' - onClick={() => setToCancel(null)} - > - Batal - </button> + </MobileView> + + <DesktopView> + <div className='container mx-auto flex py-10'> + <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='flex mb-6 items-center justify-between'> + <h1 className='text-title-sm font-semibold'> + Daftar Transaksi{' '} + {transactions?.data?.saleOrders ? `(${transactions?.data?.saleOrders.length})` : ''} + </h1> + <form className='flex gap-x-2' onSubmit={handleSubmit}> + <input + type='text' + className='form-input' + placeholder='Cari Transaksi...' + value={inputQuery} + onChange={(e) => setInputQuery(e.target.value)} + /> + <button className='btn-light bg-transparent px-3' type='submit'> + <MagnifyingGlassIcon className='w-6' /> + </button> + </form> + </div> + <table className='table-data'> + <thead> + <tr> + <th>No. Transaksi</th> + <th>Tanggal</th> + <th className='!text-left'>Salesperson</th> + <th className='!text-left'>Total</th> + <th>Status</th> + </tr> + </thead> + <tbody> + {transactions.isLoading && ( + <tr> + <td colSpan={5}> + <div className='flex justify-center my-2'> + <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' /> + </div> + </td> + </tr> + )} + {!transactions.isLoading && + (!transactions?.data?.saleOrders || + transactions?.data?.saleOrders?.length == 0) && ( + <tr> + <td colSpan={5}>Tidak ada data transaksi</td> + </tr> + )} + {transactions.data?.saleOrders?.map((saleOrder) => ( + <tr key={saleOrder.id}> + <td> + <Link href={`/my/transaction/${saleOrder.id}`}>{saleOrder.name}</Link> + </td> + <td>-</td> + <td className='!text-left'>{saleOrder.sales}</td> + <td className='!text-left'>{currencyFormat(saleOrder.amountTotal)}</td> + <td> + <div className='flex justify-center'> + <TransactionStatusBadge status={saleOrder.status} /> + </div> + </td> + </tr> + ))} + </tbody> + </table> + + <Pagination + pageCount={pageCount} + currentPage={parseInt(page)} + url={`/my/transactions${pageQuery}`} + className='mt-2 mb-2' + /> + </div> </div> - </BottomPopup> - </div> + </DesktopView> + </> ) } diff --git a/src/lib/variant/api/variantPriceApi.js b/src/lib/variant/api/variantPriceApi.js new file mode 100644 index 00000000..8621ca78 --- /dev/null +++ b/src/lib/variant/api/variantPriceApi.js @@ -0,0 +1,8 @@ +import odooApi from '@/core/api/odooApi' + +const variantPriceApi = async ({ id }) => { + const dataVariantPrice = await odooApi('GET', `/api/v1/product/product/price/${id}`) + return dataVariantPrice +} + +export default variantPriceApi diff --git a/src/lib/variant/hooks/useVariantPrice.js b/src/lib/variant/hooks/useVariantPrice.js new file mode 100644 index 00000000..d00eb810 --- /dev/null +++ b/src/lib/variant/hooks/useVariantPrice.js @@ -0,0 +1,13 @@ +import { useQuery } from 'react-query' +import variantPriceApi from '../api/variantPriceApi' + +const useVariantPrice = ({ id }) => { + const fetchVariantPrice = async () => await variantPriceApi({ id }) + const variantPrice = useQuery(`variantPrice-${id}`, fetchVariantPrice, { + refetchOnWindowFocus: false + }) + + return { variantPrice } +} + +export default useVariantPrice diff --git a/src/lib/wishlist/components/Wishlists.jsx b/src/lib/wishlist/components/Wishlists.jsx index e61efcc3..e13c61e5 100644 --- a/src/lib/wishlist/components/Wishlists.jsx +++ b/src/lib/wishlist/components/Wishlists.jsx @@ -4,6 +4,9 @@ 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 MobileView from '@/core/components/views/MobileView' +import DesktopView from '@/core/components/views/DesktopView' +import Menu from '@/lib/auth/components/Menu' const Wishlists = () => { const router = useRouter() @@ -22,33 +25,46 @@ const Wishlists = () => { } return ( - <div className='px-4 py-6'> - {wishlists.data?.products?.length == 0 && ( - <Alert - type='info' - className='text-center' - > - Wishlist anda masih kosong - </Alert> - )} - - <div className='grid grid-cols-2 gap-3'> - {wishlists.data?.products.map((product) => ( - <ProductCard - key={product.id} - product={product} - /> - ))} - </div> + <> + <MobileView> + <div className='px-4 py-6'> + {wishlists.data?.products?.length == 0 && ( + <Alert type='info' className='text-center'> + Wishlist anda masih kosong + </Alert> + )} - <div className='mt-6'> - <Pagination - currentPage={page} - pageCount={pageCount} - url={`/my/wishlist`} - /> - </div> - </div> + <div className='grid grid-cols-2 gap-3'> + {wishlists.data?.products.map((product) => ( + <ProductCard key={product.id} product={product} /> + ))} + </div> + + <div className='mt-6'> + <Pagination currentPage={page} pageCount={pageCount} url={`/my/wishlist`} /> + </div> + </div> + </MobileView> + + <DesktopView> + <div className='container mx-auto flex py-10'> + <div className='w-3/12 pr-4'> + <Menu /> + </div> + <div className='w-9/12'> + <div className='grid grid-cols-5 gap-3'> + {wishlists.data?.products.map((product) => ( + <ProductCard key={product.id} product={product} /> + ))} + </div> + + <div className='mt-6'> + <Pagination currentPage={page} pageCount={pageCount} url={`/my/wishlist`} /> + </div> + </div> + </div> + </DesktopView> + </> ) } diff --git a/src/pages/my/address/[id]/edit.jsx b/src/pages/my/address/[id]/edit.jsx index bc5f3471..6706b458 100644 --- a/src/pages/my/address/[id]/edit.jsx +++ b/src/pages/my/address/[id]/edit.jsx @@ -1,4 +1,7 @@ import AppLayout from '@/core/components/layouts/AppLayout' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import DesktopView from '@/core/components/views/DesktopView' +import MobileView from '@/core/components/views/MobileView' import addressApi from '@/lib/address/api/addressApi' import EditAddressComponent from '@/lib/address/components/EditAddress' import IsAuth from '@/lib/auth/components/IsAuth' @@ -6,12 +9,17 @@ import IsAuth from '@/lib/auth/components/IsAuth' export default function EditAddress({ id, defaultValues }) { return ( <IsAuth> - <AppLayout title='Ubah Alamat'> - <EditAddressComponent - id={id} - defaultValues={defaultValues} - /> - </AppLayout> + <MobileView> + <AppLayout title='Ubah Alamat'> + <EditAddressComponent id={id} defaultValues={defaultValues} /> + </AppLayout> + </MobileView> + + <DesktopView> + <BasicLayout> + <EditAddressComponent id={id} defaultValues={defaultValues} /> + </BasicLayout> + </DesktopView> </IsAuth> ) } diff --git a/src/pages/my/address/create.jsx b/src/pages/my/address/create.jsx index ec17f987..be645853 100644 --- a/src/pages/my/address/create.jsx +++ b/src/pages/my/address/create.jsx @@ -1,13 +1,24 @@ import AppLayout from '@/core/components/layouts/AppLayout' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import DesktopView from '@/core/components/views/DesktopView' +import MobileView from '@/core/components/views/MobileView' import CreateAddressComponent from '@/lib/address/components/CreateAddress' import IsAuth from '@/lib/auth/components/IsAuth' export default function CreateAddress() { return ( <IsAuth> - <AppLayout title='Tambah Alamat'> - <CreateAddressComponent /> - </AppLayout> + <MobileView> + <AppLayout title='Tambah Alamat'> + <CreateAddressComponent /> + </AppLayout> + </MobileView> + + <DesktopView> + <BasicLayout> + <CreateAddressComponent /> + </BasicLayout> + </DesktopView> </IsAuth> ) } diff --git a/src/pages/my/address/index.jsx b/src/pages/my/address/index.jsx index 93ed40b0..61d72a41 100644 --- a/src/pages/my/address/index.jsx +++ b/src/pages/my/address/index.jsx @@ -1,13 +1,24 @@ import AppLayout from '@/core/components/layouts/AppLayout' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import DesktopView from '@/core/components/views/DesktopView' +import MobileView from '@/core/components/views/MobileView' import AddressesComponent from '@/lib/address/components/Addresses' import IsAuth from '@/lib/auth/components/IsAuth' export default function Addresses() { return ( <IsAuth> - <AppLayout title='Daftar Alamat'> - <AddressesComponent /> - </AppLayout> + <MobileView> + <AppLayout title='Daftar Alamat'> + <AddressesComponent /> + </AppLayout> + </MobileView> + + <DesktopView> + <BasicLayout> + <AddressesComponent /> + </BasicLayout> + </DesktopView> </IsAuth> ) } diff --git a/src/pages/my/invoice/[id].jsx b/src/pages/my/invoice/[id].jsx index 4938d8f8..740819cc 100644 --- a/src/pages/my/invoice/[id].jsx +++ b/src/pages/my/invoice/[id].jsx @@ -1,4 +1,7 @@ import AppLayout from '@/core/components/layouts/AppLayout' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import DesktopView from '@/core/components/views/DesktopView' +import MobileView from '@/core/components/views/MobileView' import IsAuth from '@/lib/auth/components/IsAuth' import InvoiceComponent from '@/lib/invoice/components/Invoice' import { useRouter } from 'next/router' @@ -8,9 +11,17 @@ export default function Invoice() { return ( <IsAuth> - <AppLayout title='Invoice & Faktur Pajak'> - <InvoiceComponent id={router.query.id} /> - </AppLayout> + <MobileView> + <AppLayout title='Invoice & Faktur Pajak'> + <InvoiceComponent id={router.query.id} /> + </AppLayout> + </MobileView> + + <DesktopView> + <BasicLayout> + <InvoiceComponent id={router.query.id} /> + </BasicLayout> + </DesktopView> </IsAuth> ) } diff --git a/src/pages/my/invoices.jsx b/src/pages/my/invoices.jsx index 12a5ff7e..59059b2e 100644 --- a/src/pages/my/invoices.jsx +++ b/src/pages/my/invoices.jsx @@ -1,13 +1,24 @@ import AppLayout from '@/core/components/layouts/AppLayout' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import DesktopView from '@/core/components/views/DesktopView' +import MobileView from '@/core/components/views/MobileView' import IsAuth from '@/lib/auth/components/IsAuth' import InvoicesComponent from '@/lib/invoice/components/Invoices' export default function Invoices() { return ( <IsAuth> - <AppLayout title='Invoice & Faktur Pajak'> - <InvoicesComponent /> - </AppLayout> + <MobileView> + <AppLayout title='Invoice & Faktur Pajak'> + <InvoicesComponent /> + </AppLayout> + </MobileView> + + <DesktopView> + <BasicLayout> + <InvoicesComponent /> + </BasicLayout> + </DesktopView> </IsAuth> ) } diff --git a/src/pages/my/menu.jsx b/src/pages/my/menu.jsx index 67158242..851bcf56 100644 --- a/src/pages/my/menu.jsx +++ b/src/pages/my/menu.jsx @@ -53,7 +53,7 @@ export default function Menu() { <MenuHeader>Pusat Bantuan</MenuHeader> <div className='divide-y divide-gray_r-6 border-y border-gray_r-6 mt-4'> - <LinkItem href='/'>Customer Support</LinkItem> + <LinkItem href='/'>Layanan Pelanggan</LinkItem> </div> </div> diff --git a/src/pages/my/transaction/[id].jsx b/src/pages/my/transaction/[id].jsx index 5167748c..c3283783 100644 --- a/src/pages/my/transaction/[id].jsx +++ b/src/pages/my/transaction/[id].jsx @@ -1,4 +1,7 @@ import AppLayout from '@/core/components/layouts/AppLayout' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import DesktopView from '@/core/components/views/DesktopView' +import MobileView from '@/core/components/views/MobileView' import IsAuth from '@/lib/auth/components/IsAuth' import TransactionComponent from '@/lib/transaction/components/Transaction' import { useRouter } from 'next/router' @@ -8,9 +11,17 @@ export default function Transaction() { return ( <IsAuth> - <AppLayout title='Transaksi'> - <TransactionComponent id={router.query.id} /> - </AppLayout> + <MobileView> + <AppLayout title='Transaksi'> + <TransactionComponent id={router.query.id} /> + </AppLayout> + </MobileView> + + <DesktopView> + <BasicLayout> + <TransactionComponent id={router.query.id} /> + </BasicLayout> + </DesktopView> </IsAuth> ) } diff --git a/src/pages/my/transactions.jsx b/src/pages/my/transactions.jsx index 30b9be07..a8ca78b8 100644 --- a/src/pages/my/transactions.jsx +++ b/src/pages/my/transactions.jsx @@ -1,4 +1,7 @@ import AppLayout from '@/core/components/layouts/AppLayout' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import DesktopView from '@/core/components/views/DesktopView' +import MobileView from '@/core/components/views/MobileView' import IsAuth from '@/lib/auth/components/IsAuth' import dynamic from 'next/dynamic' @@ -7,9 +10,17 @@ const TransactionsComponent = dynamic(() => import('@/lib/transaction/components export default function Transactions() { return ( <IsAuth> - <AppLayout title='Transaksi'> - <TransactionsComponent /> - </AppLayout> + <MobileView> + <AppLayout title='Transaksi'> + <TransactionsComponent /> + </AppLayout> + </MobileView> + + <DesktopView> + <BasicLayout> + <TransactionsComponent /> + </BasicLayout> + </DesktopView> </IsAuth> ) } diff --git a/src/pages/my/wishlist.jsx b/src/pages/my/wishlist.jsx index 196adf50..7d9ad0d7 100644 --- a/src/pages/my/wishlist.jsx +++ b/src/pages/my/wishlist.jsx @@ -1,13 +1,24 @@ import AppLayout from '@/core/components/layouts/AppLayout' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import DesktopView from '@/core/components/views/DesktopView' +import MobileView from '@/core/components/views/MobileView' import IsAuth from '@/lib/auth/components/IsAuth' import Wishlists from '@/lib/wishlist/components/Wishlists' export default function Wishlist() { return ( <IsAuth> - <AppLayout title='Wishlist'> - <Wishlists /> - </AppLayout> + <MobileView> + <AppLayout title='Wishlist'> + <Wishlists /> + </AppLayout> + </MobileView> + + <DesktopView> + <BasicLayout> + <Wishlists /> + </BasicLayout> + </DesktopView> </IsAuth> ) } diff --git a/src/styles/globals.css b/src/styles/globals.css index 90761309..d43dad1f 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -394,10 +394,6 @@ button { @apply !border-yellow_r-9; } -nav:has(> div.overlay) { - @apply z-0; -} - .table-specification { @apply max-h-[500px] overflow-y-auto border border-gray_r-6; } @@ -463,6 +459,33 @@ nav:has(> div.overlay) { @apply font-medium; } +.table-data { + @apply w-full; +} + +.table-data thead tr { + @apply bg-gray_r-3; +} + +.table-data thead th { + @apply font-medium; +} + +.table-data thead th, +.table-data tbody td { + @apply py-3 + text-center; +} + +.table-data tbody td { + @apply text-gray_r-12/90; +} + +.table-data tbody tr { + @apply border-b + border-gray_r-6; +} + .navbar-user-dropdown-button { @apply flex-1 flex @@ -480,8 +503,9 @@ nav:has(> div.overlay) { @apply line-clamp-1; } -.navbar-user-dropdown-wrapper a { - @apply text-gray_r-12/80 hover:bg-gray_r-5 font-medium py-2 px-4; +.navbar-user-dropdown-wrapper a, +.navbar-user-dropdown-wrapper button { + @apply text-gray_r-12/80 hover:bg-gray_r-5 font-medium py-2 px-4 w-full text-left; } .navbar-user-dropdown { |
