fix(FE): remove bypass permission and integrate table filter

This commit is contained in:
randy-ar
2026-01-02 10:04:56 +07:00
parent 046fb74cab
commit d6b8b10183
2 changed files with 141 additions and 146 deletions
+130 -108
View File
@@ -2,7 +2,10 @@
import Button from '@/components/Button'; import Button from '@/components/Button';
import CheckboxInput from '@/components/input/CheckboxInput'; import CheckboxInput from '@/components/input/CheckboxInput';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, {
OptionType,
useSelect,
} from '@/components/input/SelectInput';
import Modal, { useModal } from '@/components/Modal'; import Modal, { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModal from '@/components/modal/ConfirmationModal';
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
@@ -28,6 +31,8 @@ import toast from 'react-hot-toast';
import useSWR from 'swr'; import useSWR from 'swr';
import RequirePermission from '@/components/helper/RequirePermission'; import RequirePermission from '@/components/helper/RequirePermission';
import { useAuth } from '@/services/hooks/useAuth'; import { useAuth } from '@/services/hooks/useAuth';
import { CustomerApi, ProductApi } from '@/services/api/master-data';
import { MARKETING_APPROVAL_LINE } from '@/config/approval-line';
const RowsOptionsMenu = ({ const RowsOptionsMenu = ({
type = 'dropdown', type = 'dropdown',
@@ -52,7 +57,7 @@ const RowsOptionsMenu = ({
)} )}
> >
<div className='flex flex-col gap-1'> <div className='flex flex-col gap-1'>
{/* <RequirePermission permissions='lti.marketing.delivery_order.detail'> <RequirePermission permissions='lti.marketing.delivery_order.detail'>
<Button <Button
href={`/marketing/detail?marketingId=${props.row.original.id}`} href={`/marketing/detail?marketingId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -62,19 +67,10 @@ const RowsOptionsMenu = ({
<Icon icon='mdi:eye-outline' width={16} height={16} /> <Icon icon='mdi:eye-outline' width={16} height={16} />
Detail Detail
</Button> </Button>
</RequirePermission> */} </RequirePermission>
<Button
href={`/marketing/detail?marketingId=${props.row.original.id}`}
variant='ghost'
color='primary'
className='justify-start text-sm'
>
<Icon icon='mdi:eye-outline' width={16} height={16} />
Detail
</Button>
{props.row.original.latest_approval.step_number != 1 && ( {props.row.original.latest_approval.step_number != 1 && (
<> <>
{/* <RequirePermission <RequirePermission
permissions={ permissions={
props.row.original.latest_approval.step_number == 3 props.row.original.latest_approval.step_number == 3
? 'lti.marketing.delivery_order.update' ? 'lti.marketing.delivery_order.update'
@@ -101,32 +97,12 @@ const RowsOptionsMenu = ({
<Icon icon='mdi:truck' width={16} height={16} /> <Icon icon='mdi:truck' width={16} height={16} />
Deliver Deliver
</Button> </Button>
</RequirePermission> */} </RequirePermission>
<Button
href={
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
}
onClick={() => {
if (props.row.original.latest_approval.step_number == 2) {
deliveryClickHandler?.();
}
}}
variant='ghost'
color='success'
className='justify-start text-sm'
>
<Icon icon='mdi:truck' width={16} height={16} />
Deliver
</Button>
</> </>
)} )}
{props.row.original.latest_approval.step_number != 3 && ( {props.row.original.latest_approval.step_number != 3 && (
<> <>
{/* <RequirePermission permissions='lti.marketing.sales_order.update'> <RequirePermission permissions='lti.marketing.sales_order.update'>
<Button <Button
href={`/marketing/detail/sales-orders/edit?marketingId=${props.row.original.id}`} href={`/marketing/detail/sales-orders/edit?marketingId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -136,19 +112,10 @@ const RowsOptionsMenu = ({
<Icon icon='mdi:pencil-outline' width={16} height={16} /> <Icon icon='mdi:pencil-outline' width={16} height={16} />
Edit Edit
</Button> </Button>
</RequirePermission> */} </RequirePermission>
<Button
href={`/marketing/detail/sales-orders/edit?marketingId=${props.row.original.id}`}
variant='ghost'
color='warning'
className='justify-start text-sm'
>
<Icon icon='mdi:pencil-outline' width={16} height={16} />
Edit
</Button>
</> </>
)} )}
{/* <RequirePermission permissions='lti.marketing.sales_order.delete'> <RequirePermission permissions='lti.marketing.sales_order.delete'>
<Button <Button
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
@@ -158,16 +125,7 @@ const RowsOptionsMenu = ({
<Icon icon='mdi:delete-outline' width={16} height={16} /> <Icon icon='mdi:delete-outline' width={16} height={16} />
Delete Delete
</Button> </Button>
</RequirePermission> */} </RequirePermission>
<Button
onClick={deleteClickHandler}
variant='ghost'
color='error'
className='text-error hover:text-inherit justify-start text-sm'
>
<Icon icon='mdi:delete-outline' width={16} height={16} />
Delete
</Button>
</div> </div>
</div> </div>
); );
@@ -175,8 +133,6 @@ const RowsOptionsMenu = ({
const MarketingTable = () => { const MarketingTable = () => {
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [approveAction, setApproveAction] = useState<'APPROVED' | 'REJECTED'>( const [approveAction, setApproveAction] = useState<'APPROVED' | 'REJECTED'>(
'APPROVED' 'APPROVED'
@@ -186,18 +142,64 @@ const MarketingTable = () => {
const { permissionCheck } = useAuth(); const { permissionCheck } = useAuth();
const router = useRouter(); const router = useRouter();
const {
data: marketing,
isLoading: isLoadingMarketing,
mutate: refreshMarketing,
} = useSWR(MarketingApi.basePath, MarketingApi.getAllFetcher);
const deleteModal = useModal(); const deleteModal = useModal();
const confirmationModal = useModal(); const confirmationModal = useModal();
const productsModal = useModal(); const productsModal = useModal();
const deliveryModal = useModal(); const deliveryModal = useModal();
const {
state: tableFilterState,
updateFilter,
setPage,
setPageSize,
toQueryString: getTableFilterToQueryString,
} = useTableFilter({
initial: {
search: '',
product_ids: '',
status: '',
customer_id: '',
page: 1,
limit: 10,
},
paramMap: {
page: 'page',
pageSize: 'limit',
product_ids: 'product_ids',
status: 'status',
customer_id: 'customer_id',
},
});
// ===== FETCH DATA =====
const {
data: marketing,
isLoading: isLoadingMarketing,
mutate: refreshMarketing,
} = useSWR(
`${MarketingApi.basePath}${getTableFilterToQueryString()}`,
MarketingApi.getAllFetcher
);
// ===== OPTIONS =====
const {
options: productsOptions,
isLoadingOptions: isLoadingProductsOptions,
} = useSelect(ProductApi.basePath, 'id', 'name', '', {
limit: 'limit',
});
const {
options: customersOptions,
isLoadingOptions: isLoadingCustomersOptions,
} = useSelect(CustomerApi.basePath, 'id', 'name', '', {
limit: 'limit',
});
const statusOptions = MARKETING_APPROVAL_LINE.map((item) => ({
value: item.step_number,
label: item.step_name,
}));
// ===== HANDLER =====
const searchChangeHandler = useCallback( const searchChangeHandler = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => { (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value); setSearch(e.target.value);
@@ -209,7 +211,8 @@ const MarketingTable = () => {
(val: OptionType | OptionType[] | null) => { (val: OptionType | OptionType[] | null) => {
const newVal = val as OptionType; const newVal = val as OptionType;
setPageSize(newVal.value as number); setPageSize(newVal.value as number);
setPage(1); updateFilter('page', 1);
updateFilter('limit', newVal.value as number);
}, },
[] []
); );
@@ -314,20 +317,6 @@ const MarketingTable = () => {
); );
}; };
const {
state: tableFilterState,
updateFilter,
toQueryString: getTableFilterToQueryString,
} = useTableFilter({
initial: {
search: '',
},
paramMap: {
page: 'page',
pageSize: 'limit',
},
});
const getRowCanSelect = (row: Row<Marketing>): boolean => { const getRowCanSelect = (row: Row<Marketing>): boolean => {
const approval = row.original.latest_approval; const approval = row.original.latest_approval;
return approval?.step_number === 1 && approval?.action !== 'REJECTED'; return approval?.step_number === 1 && approval?.action !== 'REJECTED';
@@ -353,7 +342,7 @@ const MarketingTable = () => {
}} }}
/> />
<div className='flex flex-row gap-2'> <div className='flex flex-row gap-2'>
{/* <RequirePermission permissions='lti.marketing.sales_order.approve'> <RequirePermission permissions='lti.marketing.sales_order.approve'>
<Button <Button
color='success' color='success'
onClick={approveClickHandler} onClick={approveClickHandler}
@@ -363,18 +352,9 @@ const MarketingTable = () => {
<Icon icon='material-symbols:check' width={24} height={24} /> <Icon icon='material-symbols:check' width={24} height={24} />
Approve Approve
</Button> </Button>
</RequirePermission> */} </RequirePermission>
<Button
color='success'
onClick={approveClickHandler}
className='justify-start text-sm'
disabled={disableApprove}
>
<Icon icon='material-symbols:check' width={24} height={24} />
Approve
</Button>
{/* <RequirePermission permissions='lti.marketing.sales_order.approve'> <RequirePermission permissions='lti.marketing.sales_order.approve'>
<Button <Button
color='error' color='error'
onClick={rejectClickHandler} onClick={rejectClickHandler}
@@ -384,19 +364,10 @@ const MarketingTable = () => {
<Icon icon='material-symbols:close' width={24} height={24} /> <Icon icon='material-symbols:close' width={24} height={24} />
Reject Reject
</Button> </Button>
</RequirePermission> */} </RequirePermission>
<Button
color='error'
onClick={rejectClickHandler}
className='justify-start text-sm'
disabled={disableReject}
>
<Icon icon='material-symbols:close' width={24} height={24} />
Reject
</Button>
</div> </div>
<TableRowSizeSelector <TableRowSizeSelector
value={pageSize} value={tableFilterState.pageSize}
onChange={pageSizeChangeHandler} onChange={pageSizeChangeHandler}
options={ROWS_OPTIONS} options={ROWS_OPTIONS}
className='flex sm:flex-row flex-col gap-3 items-end justify-end' className='flex sm:flex-row flex-col gap-3 items-end justify-end'
@@ -406,7 +377,29 @@ const MarketingTable = () => {
label='Product' label='Product'
isClearable isClearable
placeholder='Pilih product' placeholder='Pilih product'
options={[]} options={productsOptions}
isLoading={isLoadingProductsOptions}
value={
tableFilterState.product_ids
?.split(',')
.map((id) =>
productsOptions.find(
(option) => option.value === Number(id)
)
)
.filter(
(option): option is { value: number; label: string } =>
option !== undefined
) ?? null
}
onChange={(value: OptionType | OptionType[] | null) =>
updateFilter(
'product_ids',
(value as OptionType[])
?.map((item: OptionType) => item.value.toString())
.join(',') || ''
)
}
isMulti isMulti
/> />
{/* select status */} {/* select status */}
@@ -414,14 +407,43 @@ const MarketingTable = () => {
label='Status' label='Status'
isClearable isClearable
placeholder='Pilih status' placeholder='Pilih status'
options={[]} options={statusOptions}
value={
tableFilterState.status
? statusOptions.find(
(option) =>
option.value === Number(tableFilterState.status)
)
: null
}
onChange={(value: OptionType | OptionType[] | null) =>
updateFilter(
'status',
(value as OptionType)?.value.toString() || ''
)
}
/> />
{/* select customer */} {/* select customer */}
<SelectInput <SelectInput
label='Customer' label='Customer'
isClearable isClearable
placeholder='Pilih customer' placeholder='Pilih customer'
options={[]} options={customersOptions}
isLoading={isLoadingCustomersOptions}
value={
tableFilterState.customer_id
? customersOptions.find(
(option) =>
option.value === Number(tableFilterState.customer_id)
)
: null
}
onChange={(value: OptionType | OptionType[] | null) =>
updateFilter(
'customer_id',
(value as OptionType)?.value.toString() || ''
)
}
/> />
</TableRowSizeSelector> </TableRowSizeSelector>
</div> </div>
@@ -587,8 +609,8 @@ const MarketingTable = () => {
}, },
}, },
]} ]}
pageSize={pageSize} pageSize={tableFilterState.pageSize}
page={page} page={tableFilterState.page}
onPageChange={setPage} onPageChange={setPage}
className={{ className={{
tableWrapperClassName: 'overflow-x-auto min-h-full!', tableWrapperClassName: 'overflow-x-auto min-h-full!',
+11 -38
View File
@@ -3,7 +3,6 @@ export const ROUTE_PERMISSIONS: Record<string, string[]> = {
// Dashboard // Dashboard
'/dashboard/': ['lti.dashboard.list'], '/dashboard/': ['lti.dashboard.list'],
'/dashboard': ['lti.dashboard.list'],
// Production // Production
// Production - Project Flock // Production - Project Flock
@@ -58,27 +57,14 @@ export const ROUTE_PERMISSIONS: Record<string, string[]> = {
'/purchase/detail/edit/': ['lti.purchase.update'], '/purchase/detail/edit/': ['lti.purchase.update'],
// Marketing // Marketing
'/marketing/': ['lti.dashboard.list', 'lti.marketing.delivery_order.list'], '/marketing/': ['lti.marketing.delivery_order.list'],
'/marketing/add/delivery-orders/': [ '/marketing/add/delivery-orders/': ['lti.marketing.delivery_order.create'],
'lti.dashboard.list', '/marketing/add/sales-orders/': ['lti.marketing.sales_order.create'],
'lti.marketing.delivery_order.create', '/marketing/detail/': ['lti.marketing.delivery_order.detail'],
],
'/marketing/add/sales-orders/': [
'lti.dashboard.list',
'lti.marketing.sales_order.create',
],
'/marketing/detail/': [
'lti.dashboard.list',
'lti.marketing.delivery_order.detail',
],
'/marketing/detail/delivery-orders/edit/': [ '/marketing/detail/delivery-orders/edit/': [
'lti.dashboard.list',
'lti.marketing.delivery_order.update', 'lti.marketing.delivery_order.update',
], ],
'/marketing/detail/sales-orders/edit/': [ '/marketing/detail/sales-orders/edit/': ['lti.marketing.sales_order.update'],
'lti.dashboard.list',
'lti.marketing.sales_order.update',
],
// Expense // Expense
'/expense/': ['lti.expense.list'], '/expense/': ['lti.expense.list'],
@@ -89,19 +75,12 @@ export const ROUTE_PERMISSIONS: Record<string, string[]> = {
'/expense/realization/edit/': ['lti.expense.update.realization'], '/expense/realization/edit/': ['lti.expense.update.realization'],
// Finance // Finance
'/finance/': ['lti.dashboard.list', 'lti.finance.transaction.list'], '/finance/': ['lti.finance.transaction.list'],
'/finance/detail/': ['lti.dashboard.list', 'lti.finance.transaction.detail'], '/finance/detail/': ['lti.finance.transaction.detail'],
'/finance/add/': ['lti.dashboard.list', 'lti.finance.payments.create'], '/finance/add/': ['lti.finance.payments.create'],
'/finance/detail/edit/': [ '/finance/detail/edit/': ['lti.finance.payments.update'],
'lti.dashboard.list', '/finance/add/initial-balance/': ['lti.finance.initial_balances.create'],
'lti.finance.payments.update',
],
'/finance/add/initial-balance/': [
'lti.dashboard.list',
'lti.finance.initial_balances.create',
],
'/finance/detail/edit/initial-balance/': [ '/finance/detail/edit/initial-balance/': [
'lti.dashboard.list',
'lti.finance.initial_balances.update', 'lti.finance.initial_balances.update',
], ],
'/finance/add/injection/': ['lti.finance.injections.create'], '/finance/add/injection/': ['lti.finance.injections.create'],
@@ -203,20 +182,14 @@ export const ROUTE_PERMISSIONS: Record<string, string[]> = {
'/master-data/flock/detail/': ['lti.master.flocks.detail'], '/master-data/flock/detail/': ['lti.master.flocks.detail'],
'/master-data/flock/detail/edit/': ['lti.master.flocks.update'], '/master-data/flock/detail/edit/': ['lti.master.flocks.update'],
'/master-data/production-standard/': [ '/master-data/production-standard/': ['lti.master.production_standards.list'],
'lti.dashboard.list',
'lti.master.production_standards.list',
],
'/master-data/production-standard/add/': [ '/master-data/production-standard/add/': [
'lti.dashboard.list',
'lti.master.production_standards.create', 'lti.master.production_standards.create',
], ],
'/master-data/production-standard/detail/': [ '/master-data/production-standard/detail/': [
'lti.dashboard.list',
'lti.master.production_standards.detail', 'lti.master.production_standards.detail',
], ],
'/master-data/production-standard/detail/edit/': [ '/master-data/production-standard/detail/edit/': [
'lti.dashboard.list',
'lti.master.production_standards.update', 'lti.master.production_standards.update',
], ],
}; };