refactor(FE): refactor UI Sales Order and Delivery Order

This commit is contained in:
randy-ar
2026-02-02 22:00:42 +07:00
parent a57f65a420
commit 17589cb2b4
15 changed files with 2338 additions and 821 deletions
@@ -2,19 +2,10 @@
import Button from '@/components/Button';
import CheckboxInput from '@/components/input/CheckboxInput';
import SelectInput, {
OptionType,
useSelect,
} from '@/components/input/SelectInput';
import Modal, { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
import Table from '@/components/Table';
import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import { TableRowSizeSelector } from '@/components/table/TableRowSizeSelector';
import { TableToolbar } from '@/components/table/TableToolbar';
import { ROWS_OPTIONS } from '@/config/constant';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { cn, formatCurrency, formatDate, formatTitleCase } from '@/lib/helper';
import {
@@ -30,17 +21,12 @@ import {
import { Icon } from '@iconify/react';
import { CellContext, ColumnDef, Row } from '@tanstack/react-table';
import { useRouter } from 'next/navigation';
import { useCallback, useMemo, useState } from 'react';
import { useMemo, useState } from 'react';
import toast from 'react-hot-toast';
import useSWR from 'swr';
import RequirePermission from '@/components/helper/RequirePermission';
import { useAuth } from '@/services/hooks/useAuth';
import { CustomerApi, ProductApi } from '@/services/api/master-data';
import { MARKETING_APPROVAL_LINE } from '@/config/approval-line';
import Badge from '@/components/Badge';
import ButtonFilter from '@/components/helper/ButtonFilter';
import Menu from '@/components/menu/Menu';
import MenuItem from '@/components/menu/MenuItem';
import Dropdown from '@/components/Dropdown';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
@@ -59,15 +45,13 @@ const RowsOptionsMenu = ({
deliveryClickHandler?: () => void;
popoverPosition?: 'top' | 'bottom';
}) => {
const showEditButton =
props.row.original.latest_approval.action !== 'APPROVED' &&
props.row.original.latest_approval.action !== 'REJECTED';
const showDeleteButton = showEditButton;
const popoverId = `marketing#${props.row.original.id}`;
const popoverAnchorName = `--anchor-marketing#${props.row.original.id}`;
const isDeliveryRejected =
props.row.original.latest_approval.action === 'REJECTED' &&
props.row.original.latest_approval.step_number === 3;
return (
<div className='relative'>
<PopoverButton
@@ -88,7 +72,7 @@ const RowsOptionsMenu = ({
<div className='flex flex-col bg-base-100 rounded-xl'>
<RequirePermission permissions='lti.marketing.delivery_order.detail'>
<Button
href={`/marketing/detail?marketingId=${props.row.original.id}`}
href={`/marketing?action=detail&id=${props.row.original.id}`}
variant='ghost'
color='none'
className='p-3 justify-start text-sm font-semibold w-full'
@@ -97,43 +81,44 @@ const RowsOptionsMenu = ({
View Details
</Button>
</RequirePermission>
{props.row.original.latest_approval.step_number != 1 && (
<>
<RequirePermission
permissions={
props.row.original.latest_approval.step_number == 3
? 'lti.marketing.delivery_order.update'
: 'lti.marketing.delivery_order.create'
}
>
<Button
href={
{props.row.original.latest_approval.step_number != 1 &&
!isDeliveryRejected && (
<>
<RequirePermission
permissions={
props.row.original.latest_approval.step_number == 3
? `/marketing/detail/delivery-orders/edit?marketingId=${props.row.original.id}`
: props.row.original.latest_approval.step_number == 2
? `/marketing/add/delivery-orders?marketingId=${props.row.original.id}`
: undefined
? 'lti.marketing.delivery_order.update'
: 'lti.marketing.delivery_order.create'
}
onClick={() => {
if (props.row.original.latest_approval.step_number == 2) {
deliveryClickHandler?.();
}
}}
variant='ghost'
color='none'
className='p-3 justify-start text-sm font-semibold w-full'
>
<Icon icon='heroicons:truck' width={20} height={20} />
Deliver Item
</Button>
</RequirePermission>
</>
)}
<Button
href={
props.row.original.latest_approval.step_number == 3
? `/marketing?action=edit_delivery&id=${props.row.original.id}`
: props.row.original.latest_approval.step_number == 2
? `/marketing?action=add_delivery&id=${props.row.original.id}`
: undefined
}
onClick={() => {
if (props.row.original.latest_approval.step_number == 2) {
deliveryClickHandler?.();
}
}}
variant='ghost'
color='none'
className='p-3 justify-start text-sm font-semibold w-full'
>
<Icon icon='heroicons:truck' width={20} height={20} />
Deliver Item
</Button>
</RequirePermission>
</>
)}
{props.row.original.latest_approval.step_number != 3 && (
<>
<RequirePermission permissions='lti.marketing.sales_order.update'>
<Button
href={`/marketing/detail/sales-orders/edit?marketingId=${props.row.original.id}`}
href={`/marketing?action=edit&id=${props.row.original.id}`}
variant='ghost'
color='none'
className='p-3 justify-start text-sm font-semibold w-full'
@@ -328,7 +313,7 @@ const MarketingTable = () => {
toast.success(res?.message as string);
refreshMarketing?.();
router.push(
`/marketing/detail/delivery-orders/edit?marketingId=${selectedItem?.id}`
`/marketing/detail/delivery-orders/edit?id=${selectedItem?.id}`
);
};
@@ -411,18 +396,19 @@ const MarketingTable = () => {
const approval = props.row.original.latest_approval;
const isRejected = approval?.action == 'REJECTED';
const isApproved = approval?.action == 'APPROVED';
const isUpdated = approval?.action == 'UPDATED';
return (
<StatusBadge
color={
isRejected
? 'error'
: isApproved
: isApproved || isUpdated
? approval?.step_number == 1
? 'neutral'
: approval?.step_number == 2
? 'primary'
? 'info'
: approval?.step_number == 3
? 'success'
? 'warning'
: 'neutral'
: 'neutral'
}
@@ -431,6 +417,9 @@ const MarketingTable = () => {
? 'Ditolak'
: formatTitleCase(approval?.step_name || '')
}
className={{
badge: 'whitespace-nowrap',
}}
/>
);
},
@@ -462,8 +451,7 @@ const MarketingTable = () => {
return (
<Button
variant='link'
color='success'
className='p-0 text-none'
className='p-0 text-none text-sm'
onClick={() => {
productsClickHandler(props?.row?.original);
}}
@@ -705,19 +693,23 @@ const MarketingTable = () => {
<Modal
ref={productsModal.ref}
className={{
modalBox: 'xs:max-w-2/5 z-100',
modalBox: 'xs:max-w-2/5 z-100 rounded-lg p-0 flex flex-col',
modal: 'rounded-lg',
}}
closeOnBackdrop
>
<div className='flex flex-row justify-between items-center mb-3'>
<h4 className='text-xl font-semibold'>Daftar Produk</h4>
<div className='flex flex-row justify-between items-center border-b border-base-content/10 p-4'>
<h4 className='flex items-center justify-center gap-2 text-sm font-medium text-base-content'>
<Icon icon='heroicons:information-circle' width={20} height={20} />{' '}
Daftar Produk
</h4>
<Button
variant='ghost'
color='error'
color='none'
onClick={productsModal.closeModal}
className='justify-start text-sm rounded-full'
className='justify-start text-sm p-1'
>
<Icon icon='mdi:close' width={16} height={16} />
<Icon icon='mdi:close' width={20} height={20} />
</Button>
</div>
<Table<BaseSalesOrder>
@@ -747,15 +739,9 @@ const MarketingTable = () => {
},
]}
className={{
containerClassName: 'p-6',
tableWrapperClassName: 'overflow-x-auto min-h-full!',
tableClassName: 'font-inter w-full table-auto min-h-full!',
headerRowClassName: 'border-b border-b-gray-200',
headerColumnClassName:
'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
bodyRowClassName: 'border-b border-b-gray-200',
bodyColumnClassName:
'px-6 py-3 last:flex last:flex-row last:justify-end',
containerClassName: 'p-4',
headerColumnClassName: 'whitespace-nowrap',
bodyColumnClassName: 'last:flex last:flex-row last:justify-end',
paginationClassName: 'hidden',
}}
isLoading={isLoadingMarketing}