diff options
| author | it-fixcomart <it@fixcomart.co.id> | 2025-07-31 15:55:05 +0700 |
|---|---|---|
| committer | it-fixcomart <it@fixcomart.co.id> | 2025-07-31 15:55:05 +0700 |
| commit | 04961a55929017f77ee6801d2b7ada4c05689821 (patch) | |
| tree | 5c519ac632391b34e1cc2bb31d011be37bb2d779 /src/lib/transaction | |
| parent | 077467cf53b46d8049df8b812577cd1a03011eba (diff) | |
<hafid> fix repeat order
Diffstat (limited to 'src/lib/transaction')
| -rw-r--r-- | src/lib/transaction/components/TransactionStatusBadge.jsx | 4 | ||||
| -rw-r--r-- | src/lib/transaction/components/Transactions.jsx | 461 |
2 files changed, 260 insertions, 205 deletions
diff --git a/src/lib/transaction/components/TransactionStatusBadge.jsx b/src/lib/transaction/components/TransactionStatusBadge.jsx index e061587c..cb8cbcd9 100644 --- a/src/lib/transaction/components/TransactionStatusBadge.jsx +++ b/src/lib/transaction/components/TransactionStatusBadge.jsx @@ -14,11 +14,11 @@ const TransactionStatusBadge = ({ status }) => { break case 'waiting': badgeProps.className.push('badge-yellow') - badgeProps.text = 'Pesanan Diterima' + badgeProps.text = 'Pesanan Diproses' break case 'sale': badgeProps.className.push('badge-yellow') - badgeProps.text = 'Pesanan Diproses' + badgeProps.text = 'Pesanan Dikemas' break case 'shipping': badgeProps.className.push('badge-green') diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index c4651119..acb925da 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -1,5 +1,5 @@ import { useRouter } from 'next/router'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import { toast } from 'react-hot-toast'; import { EllipsisVerticalIcon, @@ -34,7 +34,10 @@ import Image from '@/core/components/elements/Image/Image'; import { upsertUserCart } from '~/services/cart'; import { useProductCartContext } from '@/contexts/ProductCartContext'; import { Swiper, SwiperSlide } from 'swiper/react'; +import { Navigation } from 'swiper'; import 'swiper/css'; +import 'swiper/css/navigation'; +import { Calendar } from 'lucide-react'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import { DateRangePicker } from 'react-date-range'; @@ -45,6 +48,7 @@ import { Popover } from '@headlessui/react'; const Transactions = ({ context = '' }) => { const auth = useAuth(); const router = useRouter(); + const swiperRef = useRef(null); const { q = '', page = 1, @@ -80,11 +84,12 @@ const Transactions = ({ context = '' }) => { const [isOpenCalender, setIsOpenCalender] = useState(false); const [cachedAllData, setCachedAllData] = useState(null); // Simpan data "All" const [currentData, setCurrentData] = useState([]); // Data yang ditampilkan + const calendarRef = useRef(null); + const [isDateSelected, setIsDateSelected] = useState(false); const parseDate = (date) => { - if (null || 'null') return null; - if (!date) return null; - if (date instanceof Date) return date; // Jika sudah Date, langsung return + if (!date || date === 'null') return null; + if (date instanceof Date) return date; const [day, month, year] = date.split('/').map(Number); return new Date(year, month - 1, day); }; @@ -106,7 +111,7 @@ const Transactions = ({ context = '' }) => { sort: sortNew, startDate: state[0].startDate ? state[0].startDate.toLocaleDateString('id-ID') - : state[0].startDate, + : null, endDate: state[0]?.endDate?.toLocaleDateString('id-ID'), site: siteFilter || (auth?.webRole === null && auth?.site ? auth.site : null), @@ -114,8 +119,9 @@ const Transactions = ({ context = '' }) => { const statuses = [ { id: 'all', label: 'Semua' }, { id: 'quotation', label: 'Pending Quotation' }, - { id: 'diterima', label: 'Pesanan Diterima' }, { id: 'diproses', label: 'Pesanan Diproses' }, + { id: 'dikemas', label: 'Pesanan Dikemas' }, + { id: 'partial', label: 'Dikirim Sebagian' }, { id: 'dikirim', label: 'Pesanan Dikirim' }, { id: 'selesai', label: 'Pesanan Selesai' }, { id: 'cancel', label: 'Pesanan Dibatalkan' }, @@ -302,6 +308,22 @@ const Transactions = ({ context = '' }) => { } }; + useEffect(() => { + const handleClickOutside = (event) => { + if ( + calendarRef.current && + !calendarRef.current.contains(event.target) + ) { + setIsOpenCalender(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + const startItem = 1 + (pageNew - 1) * limitNew; const endItem = Math.min( limitNew * pageNew, @@ -438,82 +460,82 @@ const Transactions = ({ context = '' }) => { </option> ))} </select> - <div className='relative w-full text-xs'> - <div - className='border border-gray-300 rounded-lg bg-white shadow-sm focus:outline-none focus:ring-2 focus:ring-red-500 w-full px-2 py-1 flex items-center justify-between cursor-pointer' - onClick={() => setIsOpenCalender(true)} - > - <span className='text-nowrap px-1 truncate'> - {state[0]?.startDate - ? `${state[0].startDate.toLocaleDateString()} - ${state[0].endDate.toLocaleDateString()}` - : 'Semua Tanggal'} - </span> - </div> - - {isOpenCalender && ( - <div className='fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50'> - <div className='calendar-container bg-white shadow-lg border rounded-lg p-4 w-full'> - <DateRangePicker - onChange={(item) => setState([item.selection])} - showSelectionPreview={true} - maxDate={new Date()} - moveRangeOnFirstSelection={false} - months={1} - ranges={state} - direction='horizontal' - className='w-full' - /> - <style>{` - /* Atur container agar menjadi column */ - .rdrCalendarWrapper { - display: flex; - flex-direction: column; - } - .rdrDateRangePickerWrapper { - display: flex; - flex-direction: column; - } - - /* Pindahkan rdrStaticRanges ke atas */ - .rdrDefinedRangesWrapper { - order: -1; - width: fit-content; - } - .rdrStaticRanges { - flex-direction: row; - margin-right: 2px; - } - - /* Sembunyikan bagian input manual */ - .rdrInputRanges { - display: none !important; - } - - .rdrStaticRangeLabel { - padding: 10px 10px; - } - .rdrMonth { - width: -moz-available; - } - `}</style> - <div className='flex flex-row justify-end gap-3 mt-2'> - <button - className='px-4 py-1 bg-gray-500 text-white rounded' - onClick={() => setIsOpenCalender(false)} - > - Done - </button> - <button - className='px-4 py-1 bg-red-500 text-white rounded' - onClick={handleReset} - > - Reset - </button> - </div> + <div ref={calendarRef} className="relative inline-block"> + <button + type='button' + className='p-2 w-auto h-auto cursor-pointer hover:bg-gray-100 rounded transition duration-150 ease-in-out flex items-center justify-center' + onClick={() => setIsOpenCalender((prev) => !prev)} + > + <span className='text-nowrap px-1 truncate flex items-center gap-1'> + {state[0]?.startDate ? ( + `${state[0].startDate.toLocaleDateString()} - ${state[0].endDate.toLocaleDateString()}` + ) : ( + <Calendar size={16} className="text-gray-500" /> + )} + </span> + </button> + {isOpenCalender && ( + <div className='absolute right-1 mt-2 bg-white p-4 rounded shadow-lg z-50'> + {/* Tombol silang di sudut kanan atas */} + <button + onClick={() => setIsOpenCalender(false)} + className='absolute top-2 right-2 text-gray-600 hover:text-black text-xl font-bold' + > + × + </button> + <DateRangePicker + onChange={(item) => setState([item.selection])} + showSelectionPreview={false} + maxDate={new Date()} + moveRangeOnFirstSelection={false} + months={1} + ranges={state} + className='w-full' + /> + <style>{` + /* Atur container agar menjadi column */ + .rdrCalendarWrapper { + display: flex; + flex-direction: column; + } + .rdrDateRangePickerWrapper { + display: flex; + flex-direction: column; + } + + /* Pindahkan rdrStaticRanges ke atas */ + .rdrDefinedRangesWrapper { + order: -1; + width: fit-content; + } + .rdrStaticRanges { + flex-direction: row; + margin-right: 2px; + } + + /* Sembunyikan bagian input manual */ + .rdrInputRanges { + display: none !important; + } + + .rdrStaticRangeLabel { + padding: 10px 10px; + } + .rdrMonth { + width: -moz-available; + } + `}</style> + <div className='flex flex-row justify-end gap-3 mt-2'> + <button + className='px-4 py-1 bg-red-500 text-white rounded' + onClick={handleReset} + > + Reset + </button> </div> </div> - )} - </div> + )} + </div> {/* <div className='border border-gray-300 rounded-lg px-1 py-1 bg-white shadow-sm focus:outline-none focus:ring-2 focus:ring-red-500 text-xs'> <DatePicker closeOnScroll={(e) => e.target === document} @@ -643,7 +665,7 @@ const Transactions = ({ context = '' }) => { {saleOrder.products?.length > 1 && ( <div className='flex flex-row gap-1 justify-start items-center'> {saleOrder.products - .slice(1, 6) + .slice(1, 4) .map((product, index) => ( <Image key={index} // Tambahkan key untuk setiap elemen dalam map() @@ -652,16 +674,21 @@ const Transactions = ({ context = '' }) => { className='object-contain object-center border border-gray_r-6 h-8 w-8 rounded-md' /> ))} - <Link - href={`${router.pathname}/${saleOrder?.id}`} - className=' text-red-500 text-nowrap' - > - {saleOrder.products?.length > 6 - ? 'Lihat ' + - (saleOrder.products?.length - 6) + - ' produk lainnya' - : 'Lihat semua produk'} - </Link> + {saleOrder.products.length > 4 ? ( + <Link + href={`${router.pathname}/${saleOrder?.id}`} + className='text-red-500 text-nowrap' + > + +{saleOrder.products.length - 4} lihat semua produk + </Link> + ) : ( + <Link + href={`${router.pathname}/${saleOrder?.id}`} + className='text-red-500 text-nowrap' + > + Lihat semua produk + </Link> + )} </div> )} </div> @@ -880,33 +907,60 @@ const Transactions = ({ context = '' }) => { </div> </div> </div> - <div className='flex flex-col gap-y-2 border rounded-lg mb-2'> + <div className='flex flex-col gap-y-2 border rounded-lg mb-2 w-full'> <div className='p-2'> <div className='flex items-center space-x-3'> <span className='text-base font-semibold text-gray-600'> Status </span> - <Swiper - spaceBetween={10} - slidesPerView={5.8} - className='w-full h-8' - > - {statuses.map((status) => ( - <SwiperSlide key={status.id} className='w-auto'> - <button - className={`px-4 py-1 text-sm font-medium border rounded-lg transition whitespace-nowrap min-w-40 - ${ - statusNew === status.id - ? 'border-red-500 text-red-500 bg-white' - : 'border-gray-300 text-gray-400 bg-gray-100 hover:bg-gray-200' - }`} - onClick={() => handleStatusChange(status.id)} + <div className="relative w-full overflow-hidden"> + {/* Container flex: tombol prev - swiper - tombol next */} + <div className="flex items-center space-x-2"> + + {/* Prev */} + <button className="custom-prev w-8 h-8 flex-shrink-0 flex items-center justify-center bg-white border rounded-full shadow z-1"> + <svg className="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" /> + </svg> + </button> + + {/* Swiper container scrollable */} + <div className="w-full overflow-hidden"> + <Swiper + spaceBetween={10} + slidesPerView="auto" + className="status-swiper" + modules={[Navigation]} + navigation={{ + nextEl: '.custom-next', + prevEl: '.custom-prev', + }} > - {status.label} - </button> - </SwiperSlide> - ))} - </Swiper> + {statuses.map((status) => ( + <SwiperSlide key={status.id} className="!w-auto"> + <button + className={`px-4 py-1 text-sm font-medium border rounded-lg transition whitespace-nowrap + ${statusNew === status.id + ? 'border-red-500 text-red-500 bg-white' + : 'border-gray-300 text-gray-400 bg-gray-100 hover:bg-gray-200' + }`} + onClick={() => handleStatusChange(status.id)} + > + {status.label} + </button> + </SwiperSlide> + ))} + </Swiper> + </div> + + {/* Next */} + <button className="custom-next w-8 h-8 flex-shrink-0 flex items-center justify-center bg-white border rounded-full shadow z-10"> + <svg className="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" /> + </svg> + </button> + </div> + </div> </div> </div> <div className='flex flex-row items-center justify-between mb-2 p-2'> @@ -944,86 +998,6 @@ const Transactions = ({ context = '' }) => { </button> </form> </div> - <div className='flex flex-col gap-2 pb-2'> - <button - type='button' - className='form-input btn py-4 mt-1 cursor-pointer hover:bg-gray-100 rounded transition duration-150 ease-in-out text-left w-full' - onClick={() => setIsOpenCalender(true)} - > - <span className='text-nowrap px-1 truncate'> - {state[0]?.startDate - ? `${state[0].startDate.toLocaleDateString()} - ${state[0].endDate.toLocaleDateString()}` - : 'Filter By Tanggal'} - </span> - </button> - {isOpenCalender && ( - <div className='absolute mt-2 bg-white p-4 rounded shadow-lg'> - {/* Tombol silang di sudut kanan atas */} - <button - onClick={() => setIsOpenCalender(false)} - className='absolute top-2 right-2 text-gray-600 hover:text-black text-xl font-bold' - > - × - </button> - <DateRangePicker - onChange={(item) => setState([item.selection])} - showSelectionPreview={true} - maxDate={new Date()} - moveRangeOnFirstSelection={false} - months={1} - ranges={state} - className='w-full' - /> - <style>{` - /* Atur container agar menjadi column */ - .rdrCalendarWrapper { - display: flex; - flex-direction: column; - } - .rdrDateRangePickerWrapper { - display: flex; - flex-direction: column; - } - - /* Pindahkan rdrStaticRanges ke atas */ - .rdrDefinedRangesWrapper { - order: -1; - width: fit-content; - } - .rdrStaticRanges { - flex-direction: row; - margin-right: 2px; - } - - /* Sembunyikan bagian input manual */ - .rdrInputRanges { - display: none !important; - } - - .rdrStaticRangeLabel { - padding: 10px 10px; - } - .rdrMonth { - width: -moz-available; - } - `}</style> - <div className='flex flex-row justify-end gap-3 mt-2'> - <button - className='px-4 py-1 bg-gray-500 text-white rounded' - onClick={() => setIsOpenCalender(false)} - > - Done - </button> - <button - className='px-4 py-1 bg-red-500 text-white rounded' - onClick={handleReset} - > - Reset - </button> - </div> - </div> - )} - </div> <div className='flex flex-row gap-4 items-center justify-center'> <p> Menampilkan {startItem}- @@ -1050,6 +1024,82 @@ const Transactions = ({ context = '' }) => { <option value={15}>15</option> <option value={20}>20</option> </select> + <div ref={calendarRef} className="relative inline-block"> + <button + type='button' + className='p-2 w-auto h-auto cursor-pointer border hover:bg-gray-100 rounded transition duration-150 ease-in-out flex items-center justify-center' + onClick={() => setIsOpenCalender((prev) => !prev)} + > + <span className='text-nowrap px-1 truncate flex items-center gap-1'> + {state[0]?.startDate ? ( + `${state[0].startDate.toLocaleDateString()} - ${state[0].endDate.toLocaleDateString()}` + ) : ( + <Calendar size={16} className="text-gray-500" /> + )} + </span> + </button> + {isOpenCalender && ( + <div className='absolute right-10 mt-2 bg-white p-4 rounded shadow-lg z-50'> + {/* Tombol silang di sudut kanan atas */} + <button + onClick={() => setIsOpenCalender(false)} + className='absolute top-2 right-2 text-gray-600 hover:text-black text-xl font-bold' + > + × + </button> + <DateRangePicker + onChange={(item) => setState([item.selection])} + showSelectionPreview={false} + maxDate={new Date()} + moveRangeOnFirstSelection={false} + months={1} + ranges={state} + className='w-full' + /> + <style>{` + /* Atur container agar menjadi column */ + .rdrCalendarWrapper { + display: flex; + flex-direction: column; + } + .rdrDateRangePickerWrapper { + display: flex; + flex-direction: column; + } + + /* Pindahkan rdrStaticRanges ke atas */ + .rdrDefinedRangesWrapper { + order: -1; + width: fit-content; + } + .rdrStaticRanges { + flex-direction: row; + margin-right: 2px; + } + + /* Sembunyikan bagian input manual */ + .rdrInputRanges { + display: none !important; + } + + .rdrStaticRangeLabel { + padding: 10px 10px; + } + .rdrMonth { + width: -moz-available; + } + `}</style> + <div className='flex flex-row justify-end gap-3 mt-2'> + <button + className='px-4 py-1 bg-red-500 text-white rounded' + onClick={handleReset} + > + Reset + </button> + </div> + </div> + )} + </div> </div> </div> </div> @@ -1140,7 +1190,7 @@ const Transactions = ({ context = '' }) => { {saleOrder.products?.length > 1 && ( <div className='flex flex-row gap-1 justify-start items-center'> {saleOrder.products - .slice(1, 6) + .slice(1, 4) .map((product, index) => ( <Image key={index} // Tambahkan key untuk setiap elemen dalam map() @@ -1149,16 +1199,21 @@ const Transactions = ({ context = '' }) => { className='object-contain object-center border border-gray_r-6 h-16 w-16 rounded-md' /> ))} - <Link - href={`${router.pathname}/${saleOrder?.id}`} - className='text-sm text-red-500 text-nowrap' - > - {saleOrder.products?.length > 6 - ? 'Lihat ' + - (saleOrder.products?.length - 6) + - ' produk lainnya' - : 'Lihat semua produk'} - </Link> + {saleOrder.products.length > 4 ? ( + <Link + href={`${router.pathname}/${saleOrder?.id}`} + className='text-red-500 text-nowrap' + > + +{saleOrder.products.length - 4} lihat semua produk + </Link> + ) : ( + <Link + href={`${router.pathname}/${saleOrder?.id}`} + className='text-red-500 text-nowrap' + > + Lihat semua produk + </Link> + )} </div> )} </div> |
