feat(FE-331): implement permission guard in marketing

This commit is contained in:
ValdiANS
2025-12-24 15:43:45 +07:00
parent 42a56a08d7
commit df6c1ae49d
3 changed files with 165 additions and 119 deletions
@@ -26,6 +26,8 @@ import { useRouter } from 'next/navigation';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import useSWR from 'swr'; import useSWR from 'swr';
import RequirePermission from '@/components/helper/RequirePermission';
import { useAuth } from '@/services/hooks/useAuth';
const RowsOptionsMenu = ({ const RowsOptionsMenu = ({
type = 'dropdown', type = 'dropdown',
@@ -50,57 +52,71 @@ const RowsOptionsMenu = ({
)} )}
> >
<div className='flex flex-col gap-1'> <div className='flex flex-col gap-1'>
<Button <RequirePermission permissions='lti.marketing.delivery_order.detail'>
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 && (
<Button <Button
href={ href={`/marketing/detail?marketingId=${props.row.original.id}`}
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' variant='ghost'
color='success' color='primary'
className='justify-start text-sm' className='justify-start text-sm'
> >
<Icon icon='mdi:truck' width={16} height={16} /> <Icon icon='mdi:eye-outline' width={16} height={16} />
Deliver Detail
</Button> </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 == 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>
</RequirePermission>
)} )}
{props.row.original.latest_approval.step_number != 3 && ( {props.row.original.latest_approval.step_number != 3 && (
<Button <RequirePermission permissions='lti.marketing.sales_order.update'>
href={`/marketing/detail/sales-orders/edit?marketingId=${props.row.original.id}`} <Button
variant='ghost' href={`/marketing/detail/sales-orders/edit?marketingId=${props.row.original.id}`}
color='warning' variant='ghost'
className='justify-start text-sm' color='warning'
> className='justify-start text-sm'
<Icon icon='mdi:pencil-outline' width={16} height={16} /> >
Edit <Icon icon='mdi:pencil-outline' width={16} height={16} />
</Button> Edit
</Button>
</RequirePermission>
)} )}
<Button <RequirePermission permissions='lti.marketing.sales_order.delete'>
onClick={deleteClickHandler} <Button
variant='ghost' onClick={deleteClickHandler}
color='error' variant='ghost'
className='text-error hover:text-inherit justify-start text-sm' color='error'
> className='text-error hover:text-inherit justify-start text-sm'
<Icon icon='mdi:delete-outline' width={16} height={16} /> >
Delete <Icon icon='mdi:delete-outline' width={16} height={16} />
</Button> Delete
</Button>
</RequirePermission>
</div> </div>
</div> </div>
); );
@@ -116,6 +132,7 @@ const MarketingTable = () => {
); );
const [selectedItem, setSelectedItem] = useState<Marketing | null>(null); const [selectedItem, setSelectedItem] = useState<Marketing | null>(null);
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({}); const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
const { permissionCheck } = useAuth();
const router = useRouter(); const router = useRouter();
@@ -270,10 +287,14 @@ const MarketingTable = () => {
<div className='flex flex-col gap-4'> <div className='flex flex-col gap-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<TableToolbar <TableToolbar
addButton={{ addButton={
href: '/marketing/add/sales-orders', permissionCheck('lti.marketing.sales_order.create')
label: 'Tambah Sales Order', ? {
}} href: '/marketing/add/sales-orders',
label: 'Tambah Sales Order',
}
: undefined
}
search={{ search={{
value: search, value: search,
onChange: searchChangeHandler, onChange: searchChangeHandler,
@@ -281,25 +302,29 @@ const MarketingTable = () => {
}} }}
/> />
<div className='flex flex-row gap-2'> <div className='flex flex-row gap-2'>
<Button <RequirePermission permissions='lti.marketing.sales_order.approve'>
color='success' <Button
onClick={approveClickHandler} color='success'
className='justify-start text-sm' onClick={approveClickHandler}
disabled={disableApprove} className='justify-start text-sm'
> disabled={disableApprove}
<Icon icon='material-symbols:check' width={24} height={24} /> >
Approve <Icon icon='material-symbols:check' width={24} height={24} />
</Button> Approve
</Button>
</RequirePermission>
<Button <RequirePermission permissions='lti.marketing.sales_order.approve'>
color='error' <Button
onClick={rejectClickHandler} color='error'
className='justify-start text-sm' onClick={rejectClickHandler}
disabled={disableReject} className='justify-start text-sm'
> disabled={disableReject}
<Icon icon='material-symbols:close' width={24} height={24} /> >
Reject <Icon icon='material-symbols:close' width={24} height={24} />
</Button> Reject
</Button>
</RequirePermission>
</div> </div>
<TableRowSizeSelector <TableRowSizeSelector
value={pageSize} value={pageSize}
@@ -33,6 +33,7 @@ import { useState } from 'react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import SalesOrderExport from '@/components/pages/marketing/pdf/SalesOrderExport'; import SalesOrderExport from '@/components/pages/marketing/pdf/SalesOrderExport';
import DeliveryOrderExport from '@/components/pages/marketing/pdf/DeliveryOrderExport'; import DeliveryOrderExport from '@/components/pages/marketing/pdf/DeliveryOrderExport';
import RequirePermission from '@/components/helper/RequirePermission';
const MarketingDetail = ({ const MarketingDetail = ({
initialValues, initialValues,
@@ -134,45 +135,58 @@ const MarketingDetail = ({
<div className='flex-row flex gap-3'> <div className='flex-row flex gap-3'>
{initialValues?.latest_approval?.step_number == 1 && ( {initialValues?.latest_approval?.step_number == 1 && (
<> <>
<Button <RequirePermission permissions='lti.marketing.sales_order.approve'>
color='success' <Button
onClick={approveClickHandler} color='success'
disabled={ onClick={approveClickHandler}
initialValues?.latest_approval?.step_number == 1 && disabled={
initialValues?.latest_approval?.action == 'REJECTED' initialValues?.latest_approval?.step_number == 1 &&
} initialValues?.latest_approval?.action == 'REJECTED'
> }
<Icon icon='mdi:check' width={24} height={24} /> >
Approve <Icon icon='mdi:check' width={24} height={24} />
</Button> Approve
<Button </Button>
color='error' </RequirePermission>
onClick={rejectClickHandler}
disabled={ <RequirePermission permissions='lti.marketing.sales_order.approve'>
initialValues?.latest_approval?.step_number == 1 && <Button
initialValues?.latest_approval?.action == 'REJECTED' color='error'
} onClick={rejectClickHandler}
> disabled={
<Icon icon='mdi:close' width={24} height={24} /> initialValues?.latest_approval?.step_number == 1 &&
Reject initialValues?.latest_approval?.action == 'REJECTED'
</Button> }
>
<Icon icon='mdi:close' width={24} height={24} />
Reject
</Button>
</RequirePermission>
</> </>
)} )}
{initialValues?.latest_approval?.step_number != 1 && ( {initialValues?.latest_approval?.step_number != 1 && (
<Button <RequirePermission
color='success' permissions={
href={
initialValues?.latest_approval?.step_number == 3 initialValues?.latest_approval?.step_number == 3
? `/marketing/detail/delivery-orders/edit?marketingId=${initialValues?.id}` ? 'lti.marketing.delivery_order.update'
: `/marketing/add/delivery-orders?marketingId=${initialValues?.id}` : 'lti.marketing.delivery_order.create'
} }
> >
<Icon icon='mdi:truck' width={24} height={24} /> <Button
{initialValues?.latest_approval?.step_number == 3 color='success'
? 'Edit ' href={
: 'Tambah '} initialValues?.latest_approval?.step_number == 3
Delivery Order ? `/marketing/detail/delivery-orders/edit?marketingId=${initialValues?.id}`
</Button> : `/marketing/add/delivery-orders?marketingId=${initialValues?.id}`
}
>
<Icon icon='mdi:truck' width={24} height={24} />
{initialValues?.latest_approval?.step_number == 3
? 'Edit '
: 'Tambah '}
Delivery Order
</Button>
</RequirePermission>
)} )}
</div> </div>
@@ -413,19 +427,23 @@ const MarketingDetail = ({
)} )}
<div className='flex flex-row gap-3'> <div className='flex flex-row gap-3'>
{initialValues?.latest_approval?.step_number != 3 && ( {initialValues?.latest_approval?.step_number != 3 && (
<Button <RequirePermission permissions='lti.marketing.sales_order.update'>
color='warning' <Button
type='button' color='warning'
href={`/marketing/detail/${initialValues?.latest_approval.step_number == 3 ? 'delivery-orders' : 'sales-orders'}/edit?marketingId=${initialValues?.id}`} type='button'
> href={`/marketing/detail/${initialValues?.latest_approval.step_number == 3 ? 'delivery-orders' : 'sales-orders'}/edit?marketingId=${initialValues?.id}`}
<Icon icon='mdi:pencil' width={24} height={24} /> >
Edit <Icon icon='mdi:pencil' width={24} height={24} />
</Button> Edit
</Button>
</RequirePermission>
)} )}
<Button color='error' onClick={deleteClickHandler}> <RequirePermission permissions='lti.marketing.sales_order.delete'>
<Icon icon='mdi:delete' width={24} height={24} /> <Button color='error' onClick={deleteClickHandler}>
Hapus <Icon icon='mdi:delete' width={24} height={24} />
</Button> Hapus
</Button>
</RequirePermission>
</div> </div>
</div> </div>
<ConfirmationModal <ConfirmationModal
@@ -47,6 +47,7 @@ import DeliveryOrderProductTable from '@/components/pages/marketing/form/table-v
import DeliveryOrderProductForm from '@/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct'; import DeliveryOrderProductForm from '@/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct';
import { SalesOrderProductFormValues } from '@/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema'; import { SalesOrderProductFormValues } from '@/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema';
import { DeliveryOrderProductFormValues } from '@/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.schema'; import { DeliveryOrderProductFormValues } from '@/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.schema';
import RequirePermission from '@/components/helper/RequirePermission';
const MemoizedSalesOrderProductTable = memo(SalesOrderProductTable); const MemoizedSalesOrderProductTable = memo(SalesOrderProductTable);
const MemoizedSalesOrderProductForm = memo(SalesOrderProductForm); const MemoizedSalesOrderProductForm = memo(SalesOrderProductForm);
@@ -689,15 +690,17 @@ const MarketingForm = ({
{/* Actions button */} {/* Actions button */}
{formType == 'edit' && ( {formType == 'edit' && (
<div className='flex flex-row justify-start'> <div className='flex flex-row justify-start'>
<Button <RequirePermission permissions='lti.marketing.sales_order.delete'>
type='button' <Button
color='error' type='button'
onClick={handleDelete} color='error'
isLoading={isLoading} onClick={handleDelete}
> isLoading={isLoading}
<Icon icon='mdi:trash' width={24} height={24} /> >
Hapus <Icon icon='mdi:trash' width={24} height={24} />
</Button> Hapus
</Button>
</RequirePermission>
</div> </div>
)} )}