summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/auth/components/Menu.jsx49
-rw-r--r--src/lib/shipment/components/Shipments.jsx122
-rw-r--r--src/lib/treckingAwb/api/getManifest.js9
-rw-r--r--src/lib/treckingAwb/component/Manifest.jsx217
4 files changed, 318 insertions, 79 deletions
diff --git a/src/lib/auth/components/Menu.jsx b/src/lib/auth/components/Menu.jsx
index e54992be..939a0d5f 100644
--- a/src/lib/auth/components/Menu.jsx
+++ b/src/lib/auth/components/Menu.jsx
@@ -1,5 +1,6 @@
import Link from '@/core/components/elements/Link/Link'
import { useRouter } from 'next/router'
+import ImageNext from 'next/image'
const Menu = () => {
const router = useRouter()
@@ -10,33 +11,61 @@ const Menu = () => {
<div className='grid grid-cols-1 bg-white border border-gray_r-6 rounded py-2 px-4 sticky top-48'>
<div className='mt-4 mb-1 font-medium'>Menu</div>
<LinkItem href='/my/quotations' active={routeStartWith('/my/quotations')}>
- Daftar Quotation
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext src='/images/icon/icon_daftar_quotation.svg' width={18} height={20} />
+ <p>Daftar Quotation</p>
+ </div>
</LinkItem>
<LinkItem href='/my/transactions' active={routeStartWith('/my/transactions')}>
- Daftar Transaksi
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext src='/images/icon/icon_daftar_transaksi.svg' width={18} height={20} />
+ <p>Daftar Transaksi</p>
+ </div>
</LinkItem>
<LinkItem href='/my/shipments' active={routeStartWith('/my/shipments')}>
- Daftar Pengiriman
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext src='/images/icon/icon_pengiriman.svg' width={18} height={20} />
+ <p>Daftar Pengiriman</p>
+ </div>
</LinkItem>
<LinkItem href='/my/invoices' active={routeStartWith('/my/invoices')}>
- Invoice & Faktur Pajak
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext src='/images/icon/icon_invoice.svg' width={18} height={20} />
+ <p>Invoice & Faktur Pajak</p>
+ </div>
</LinkItem>
<LinkItem href='/my/wishlist' active={routeStartWith('/my/wishlist')}>
- Wishlist
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext src='/images/icon/icon_wishlist.svg' width={18} height={20} />
+ <p>Wishlist</p>
+ </div>
</LinkItem>
-
<div className='mt-4 mb-1 font-medium'>Pusat Bantuan</div>
- <LinkItem href='/'>Layanan Pelanggan</LinkItem>
+ <LinkItem href='/'>
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext src='/images/icon/icon_layanan_pelanggan.svg' width={18} height={20} />
+ <p>Layanan Pelanggan</p>
+ </div>
+ </LinkItem>
<div className='mt-4 mb-1 font-medium'>Pengaturan Akun</div>
<LinkItem href='/my/address' active={routeStartWith('/my/address')}>
- Daftar Alamat
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext src='/images/icon/icon_daftar_alamat.svg' width={18} height={20} />
+ <p>Daftar Alamat</p>
+ </div>
</LinkItem>
<LinkItem href='/my/profile' active={routeStartWith('/my/profile')}>
- Profil Saya
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext src='/images/icon/icon_profile.svg' width={18} height={20} />
+ <p>Profil Saya</p>
+ </div>
</LinkItem>
<button type='button' className='text-gray_r-12/80 p-2 text-left'>
- Keluar Akun
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext src='/images/icon/icon_logout.svg' width={18} height={20} />
+ <p>Keluar Akun</p>
+ </div>
</button>
</div>
)
diff --git a/src/lib/shipment/components/Shipments.jsx b/src/lib/shipment/components/Shipments.jsx
index eb14c77f..d62afcb7 100644
--- a/src/lib/shipment/components/Shipments.jsx
+++ b/src/lib/shipment/components/Shipments.jsx
@@ -1,7 +1,7 @@
import DesktopView from '@/core/components/views/DesktopView'
import MobileView from '@/core/components/views/MobileView'
import Menu from '@/lib/auth/components/Menu'
-import { MagnifyingGlassIcon } from '@heroicons/react/24/outline'
+import { EllipsisVerticalIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline'
import ImageNext from 'next/image'
import { useRouter } from 'next/router'
import { useQuery } from 'react-query'
@@ -9,6 +9,9 @@ import _ from 'lodash-contrib'
import Spinner from '@/core/components/elements/Spinner/Spinner'
import Manifest from '@/lib/treckingAwb/component/Manifest'
import { useState } from 'react'
+import Pagination from '@/core/components/elements/Pagination/Pagination'
+import Link from 'next/link'
+import TransactionStatusBadge from '@/lib/transaction/components/TransactionStatusBadge'
const { listShipments } = require('../api/listShipment')
@@ -19,21 +22,30 @@ const Shipments = () => {
const limit = 15
const query = {
- name: q,
+ q: q,
offset: (page - 1) * limit,
limit
}
-
+ console.log('ini query', query)
+ const [inputQuery, setInputQuery] = useState(q)
const queryString = _.toQuery(query)
const { data: shipments } = useQuery('shipments', () => listShipments({ query: queryString }))
- const [ idAWB, setIdAWB ] = useState(null)
+ const [idAWB, setIdAWB] = useState(null)
+
+ const pageCount = Math.ceil(shipments?.pickingTotal / limit)
+ let pageQuery = _.omit(query, ['limit', 'offset', 'context'])
+ pageQuery = _.pickBy(pageQuery, _.identity)
+ pageQuery = _.toQuery(pageQuery)
const closePopup = () => {
setIdAWB(null)
}
- const handleSubmit = async () => {}
+ const handleSubmit = async (e) => {
+ e.preventDefault()
+ router.push(`${router.pathname}?q=${inputQuery}`)
+ }
return (
<>
<MobileView>
@@ -44,21 +56,30 @@ const Shipments = () => {
<ImageNext src='/images/BOX(1).svg' width={15} height={20} />
</div>
<h1 className='text-xs'>Pending</h1>
- <h1 className='text-xs'>99 {'>'}</h1>
+ <h1 className='text-xs'>
+ {' '}
+ {shipments?.summary?.pendingCount} {'>'}
+ </h1>
</div>
<div className='flex justify-between items-center gap-x-2 p-2 bg-white border border-gray-200 rounded-lg shadow'>
<div>
- <ImageNext src='/images/BOX_DELIVER_(1).svg' width={15} height={20} />
+ <ImageNext src='/images/BOX_DELIVER_(1).svg' width={18} height={20} />
</div>
<h1 className='text-xs'>Pengiriman</h1>
- <h1 className='text-xs'>99 {'>'}</h1>
+ <h1 className='text-xs'>
+ {' '}
+ {shipments?.summary?.shipmentCount} {'>'}
+ </h1>
</div>
<div className='flex justify-between items-center gap-x-2 p-2 bg-white border border-gray-200 rounded-lg shadow'>
<div>
<ImageNext src='/images/open-box(1).svg' width={16} height={20} />
</div>
<h1 className='text-xs'>Selesai</h1>
- <h1 className='text-xs'>99 {'>'}</h1>
+ <h1 className='text-xs'>
+ {' '}
+ {shipments?.summary?.shipmentCount} {'>'}
+ </h1>
</div>
</div>
@@ -67,14 +88,80 @@ const Shipments = () => {
type='text'
className='form-input'
placeholder='Cari Pengiriman...'
- value=''
+ value={inputQuery}
onChange={(e) => setInputQuery(e.target.value)}
/>
<button className='btn-light bg-transparent px-3' type='submit'>
<MagnifyingGlassIcon className='w-6' />
</button>
</form>
+
+ {shipments?.pickings.map((shipment) => (
+ <div className='p-4 shadow border border-gray_r-3 rounded-md' key={shipment.id}>
+ <div className='flex justify-between items-center mb-3'>
+ <div className='text-caption-2 text-gray_r-11'>
+ <p>
+ Kurir :{' '}
+ <span className='text-gray_r-11 font-semibold'>
+ {shipment.carrierName || '-'}
+ </span>
+ </p>
+ <p className='mt-2'>No. Resi : {shipment.trackingNumber || '-'}</p>
+ </div>
+ <div className='flex justify-between'>
+ {shipment?.delivered && (
+ <div className='bg-green-100 p-2 rounded '>
+ <p className='text-green-600 text-sm'>Pesanan Tiba</p>
+ </div>
+ )}
+ {!shipment?.delivered && (
+ <div className='bg-red-100 p-2 rounded '>
+ <p className='text-red-600 text-sm'>Sedang Dikirim</p>
+ </div>
+ )}
+ </div>
+ </div>
+ <hr />
+ <div className='flex justify-between mt-2 items-center mb-5'>
+ <div>
+ <span className='text-caption-2 text-gray_r-11'>No. Transaksi</span>
+ <Link href={`/my/transactions/${shipment.saleOrder.id}`}>
+ <h2 className='text-danger-500 mt-1 mb-2'>{shipment.saleOrder.name}</h2>
+ </Link>
+ <span className='text-caption-2 text-gray_r-11'>{shipment.date}</span>
+ </div>
+ <div>
+ <button
+ onClick={() => setIdAWB(shipment.id)}
+ className='bg-red-600 p-2 border border-red-600 rounded text-white w-24'
+ >
+ {!shipment.delivered ? 'Lacak' : 'Histori'}
+ </button>
+ </div>
+ </div>
+ <hr />
+ <button
+ onClick={() => setIdAWB(shipment.id)}
+ className='flex items-center mt-1 gap-x-1 min-w-full'
+ >
+ <ImageNext src={`/images/BOX_DELIVER_(1).svg`} width={20} height={20} />
+ <p className='text-sm text-green-600 truncate'>
+ {shipment.lastManifest.description}
+ </p>
+ <p className='ml-auto'>{'>'}</p>
+ </button>
+ </div>
+ ))}
+
+ <Pagination
+ pageCount={pageCount}
+ currentPage={parseInt(page)}
+ url={router.pathname + pageQuery}
+ className='mt-2 mb-2'
+ />
</div>
+
+ <Manifest idAWB={idAWB} closePopup={closePopup} />
</MobileView>
<DesktopView>
<div className='container mx-auto flex py-10'>
@@ -155,7 +242,7 @@ const Shipments = () => {
type='text'
className='form-input'
placeholder='Cari Pengiriman...'
- value=''
+ value={inputQuery}
onChange={(e) => setInputQuery(e.target.value)}
/>
<button className='btn-light bg-transparent px-3' type='submit'>
@@ -199,7 +286,10 @@ const Shipments = () => {
<td>{shipment.saleOrder.clientOrderRef || '-'}</td>
<td>{shipment.carrierName || '-'}</td>
<td>
- <button onClick={() => setIdAWB(shipment.id) }className='bg-red-600 border border-red-600 rounded-md text-sm text-white p-1 w-20'>
+ <button
+ onClick={() => setIdAWB(shipment.id)}
+ className='bg-red-600 border border-red-600 rounded-md text-sm text-white p-1 w-20'
+ >
Lacak
</button>
</td>
@@ -207,10 +297,16 @@ const Shipments = () => {
))}
</tbody>
</table>
+ <Pagination
+ pageCount={pageCount}
+ currentPage={parseInt(page)}
+ url={router.pathname + pageQuery}
+ className='mt-2 mb-2'
+ />
</div>
</div>
</div>
- <Manifest idAWB={idAWB} closePopup={closePopup}/>
+ <Manifest idAWB={idAWB} closePopup={closePopup} />
</DesktopView>
</>
)
diff --git a/src/lib/treckingAwb/api/getManifest.js b/src/lib/treckingAwb/api/getManifest.js
index e69de29b..7d78a5f2 100644
--- a/src/lib/treckingAwb/api/getManifest.js
+++ b/src/lib/treckingAwb/api/getManifest.js
@@ -0,0 +1,9 @@
+const { default: odooApi } = require("@/core/api/odooApi")
+const { getAuth } = require("@/core/utils/auth")
+
+export const getManifest = async ({id}) => {
+ const auth = getAuth()
+ const manifest = await odooApi('GET', `/api/v1/partner/${auth.partnerId}/stock-picking/${id}/tracking`)
+
+ return manifest
+} \ No newline at end of file
diff --git a/src/lib/treckingAwb/component/Manifest.jsx b/src/lib/treckingAwb/component/Manifest.jsx
index b8ad78c4..185a9d55 100644
--- a/src/lib/treckingAwb/component/Manifest.jsx
+++ b/src/lib/treckingAwb/component/Manifest.jsx
@@ -1,70 +1,175 @@
+import odooApi from '@/core/api/odooApi'
import BottomPopup from '@/core/components/elements/Popup/BottomPopup'
-import { useState } from 'react'
+import LogoSpinner from '@/core/components/elements/Spinner/LogoSpinner'
+import { getAuth } from '@/core/utils/auth'
+import { useEffect, useState } from 'react'
+import { toast } from 'react-hot-toast'
+import ImageNext from 'next/image'
const Manifest = ({ idAWB, closePopup }) => {
- console.log('ini adalah', idAWB)
- const airway = {
- waybillNumber: '1234',
- deliveryOrder: {
- name: 'name',
- carrier: 'carrier'
- },
- manifests: [
- {
- datetime: '12/12/2023',
- description: 'ini descripsi'
- }
+ const [manifests, setManifests] = useState(null)
+ const [isLoading, setIsLoading] = useState(false)
+
+ const formatCustomDate = (date) => {
+ const months = [
+ 'Jan',
+ 'Feb',
+ 'Mar',
+ 'Apr',
+ 'May',
+ 'Jun',
+ 'Jul',
+ 'Aug',
+ 'Sep',
+ 'Oct',
+ 'Nov',
+ 'Dec'
]
+
+ const parts = date.split(' ') // Pisahkan tanggal dan waktu
+ const [datePart, timePart] = parts
+ const [yyyy, mm, dd] = datePart.split('-')
+ const [hh, min] = timePart.split(':')
+
+ const monthAbbreviation = months[parseInt(mm, 10) - 1]
+
+ return `${dd} ${monthAbbreviation} ${hh}:${min}`
+ }
+
+ const getManifest = async () => {
+ setIsLoading(true)
+ const auth = getAuth()
+ const list = await odooApi(
+ 'GET',
+ `/api/v1/partner/${auth.partnerId}/stock-picking/${idAWB}/tracking`
+ )
+ setManifests(list)
+ setIsLoading(false)
+ }
+
+ useEffect(() => {
+ if (idAWB) {
+ getManifest()
+ } else {
+ setManifests(null)
+ }
+ }, [idAWB])
+
+ const [copied, setCopied] = useState(false)
+
+ const handleCopyClick = () => {
+ const textToCopy = manifests?.waybillNumber
+ navigator.clipboard.writeText(textToCopy)
+ setCopied(true)
+ toast.success('No Resi Berhasil di Copy')
+ setTimeout(() => setCopied(false), 2000) // Reset copied state after 2 seconds
}
+
return (
<>
- <BottomPopup
- key={airway.waybillNumber}
- title='Detail Pengiriman'
- active={idAWB}
- close={closePopup}
- >
- <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>
+ {isLoading && (
+ <BottomPopup active={true} close=''>
+ <div className='leading-7 text-gray_r-12/80 flex justify-center'>Mohon Tunggu</div>
+ <div className='container flex justify-center my-4'>
+ <LogoSpinner width={48} height={48} />
</div>
- <div className='flex justify-between'>
- <div className='text-gray_r-11'>Kurir</div>
- <div>{airway?.deliveryOrder?.carrier}</div>
+ </BottomPopup>
+ )}
+ {!isLoading && (
+ <BottomPopup
+ key={manifests?.waybillNumber}
+ title='Detail Pengiriman'
+ active={idAWB}
+ close={closePopup}
+ >
+ <div className='flex justify-between items-center mb-5'>
+ <h1 className='text-body-1'>Status Pesanan</h1>
+ {manifests?.delivered && (
+ <div className='bg-green-100 p-2 rounded '>
+ <p className='text-green-600 text-sm'>Pesanan Tiba</p>
+ </div>
+ )}
+ {!manifests?.delivered && (
+ <div className='bg-red-100 p-2 rounded '>
+ <p className='text-red-600 text-sm'>Sedang Dikirim</p>
+ </div>
+ )}
</div>
- <div className='flex justify-between'>
- <div className='text-gray_r-11'>No Resi</div>
- <div>{airway?.waybillNumber}</div>
+ <div className=''>
+ <h1 className='text-body-1'>
+ Estimasi tiba pada <span className='text-gray_r-11 text-sm'>({manifests?.eta})</span>
+ </h1>
+ <h1 className='text-sm mt-2 mb-3'>
+ Dikirim Menggunakan{' '}
+ <span className='text-red-500 font-semibold'>{manifests?.deliveryOrder.carrier}</span>
+ </h1>
+ {manifests?.waybillNumber && (
+ <div className='flex justify-between items-center'>
+ <h1>No. Resi</h1>
+ <div className='flex justify-between gap-x-2 items-center'>
+ <h1 className='font-semibold'>{manifests?.waybillNumber}</h1>
+ <button
+ className={`${copied ? 'text-gray-400' : 'text-red-600 '}`}
+ onClick={() => handleCopyClick()}
+ >
+ <svg
+ aria-hidden='true'
+ fill='none'
+ stroke='currentColor'
+ stroke-width='1.5'
+ viewBox='0 0 24 24'
+ className='w-5 h-6'
+ >
+ <path
+ d='M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 01-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 011.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 00-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 01-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 00-3.375-3.375h-1.5a1.125 1.125 0 01-1.125-1.125v-1.5a3.375 3.375 0 00-3.375-3.375H9.75'
+ stroke-linecap='round'
+ stroke-linejoin='round'
+ ></path>
+ </svg>
+ </button>
+ </div>
+ </div>
+ )}
</div>
- </div>
+ <hr className='mt-4' />
+ <div className='pt-4'>
+ <ol class='relative border-l border-gray_r-7'>
+ {manifests?.manifests?.map((manifest, index) => (
+ <>
+ <li class='mb-6 ml-4' key={index}>
+ {manifests.delivered == true && index == 0 ? (
+ <div
+ class={`absolute w-6 h-6 rounded-full mt-1.5 -left-3 border ${
+ index == 0 ? 'bg-green-100 border-green-100' : 'bg-gray_r-7 border-white'
+ }`}
+ >
+ <ImageNext src='/images/open-box(1).svg' width={30} height={20} />
+ </div>
+ ) : (
+ <div
+ class={`absolute w-3 h-3 rounded-full mt-1.5 -left-1.5 border bg-gray_r-7 border-white`}
+ />
+ )}
+ {manifests.delivered != true && (
+ <div
+ class={`absolute w-3 h-3 rounded-full mt-1.5 -left-1.5 border ${index == 0 ? 'bg-green-600 border-green-600' : 'bg-gray_r-7 border-white'} `}
+ />
+ )}
- <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>
+ <time class='text-sm leading-none text-gray-400'>
+ {formatCustomDate(manifest.datetime)}
+ </time>
+ {manifests.delivered == true && index == 0 && (
+ <p class={`leading-6 font-semibold text-sm text-green-600 `}>Sudah Sampai</p>
+ )}
+ <p class={`leading-6 text-[12px] text-gray_r-11`}>{manifest.description}</p>
+ </li>
+ </>
+ ))}
+ </ol>
+ </div>
+ </BottomPopup>
+ )}
</>
)
}