From 727ac8ccdb434586714fc7a36f42ab0677a9e26a Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Wed, 22 Apr 2026 01:14:16 +0700 Subject: [PATCH] feat: implement bulk approval in expense --- .../pages/expense/ExpensesTable.tsx | 355 +++++++++++++----- src/services/api/expense.ts | 35 ++ 2 files changed, 289 insertions(+), 101 deletions(-) diff --git a/src/components/pages/expense/ExpensesTable.tsx b/src/components/pages/expense/ExpensesTable.tsx index e41024c1..f7f68805 100644 --- a/src/components/pages/expense/ExpensesTable.tsx +++ b/src/components/pages/expense/ExpensesTable.tsx @@ -7,8 +7,6 @@ import { useMemo, useState, } from 'react'; -import { usePathname, useRouter, useSearchParams } from 'next/navigation'; -import { useUiStore } from '@/stores/ui/ui.store'; import useSWR from 'swr'; import { CellContext, @@ -21,8 +19,11 @@ import toast from 'react-hot-toast'; import { Icon } from '@iconify/react'; import Table from '@/components/Table'; import DebouncedTextInput from '@/components/input/DebouncedTextInput'; +import DateInput from '@/components/input/DateInput'; +import SelectInput, { OptionType } from '@/components/input/SelectInput'; +import TextArea from '@/components/input/TextArea'; import Button from '@/components/Button'; -import { useModal } from '@/components/Modal'; +import Modal, { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; import PopoverButton from '@/components/popover/PopoverButton'; import PopoverContent from '@/components/popover/PopoverContent'; @@ -37,7 +38,6 @@ import ExpenseTableSkeleton from '@/components/pages/expense/skeleton/ExpenseTab import { Expense } from '@/types/api/expense'; import { ExpenseApi } from '@/services/api/expense'; -import { buildExpenseActionHref } from '@/lib/expense-list-navigation'; import { cn, formatCurrency, formatDate } from '@/lib/helper'; import { isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; @@ -53,16 +53,34 @@ type ExpenseTableFilters = { userId: string; }; +const approvalStatusOptions = [ + { value: 'HEAD_AREA', label: 'Approval Head Area' }, + { value: 'UNIT_VICE_PRESIDENT', label: 'Approval Unit Vice President' }, + { value: 'FINANCE', label: 'Approval Finance' }, + { value: 'REALISASI', label: 'Realisasi' }, + { value: 'SELESAI', label: 'Selesai' }, +] as const satisfies OptionType< + 'HEAD_AREA' | 'UNIT_VICE_PRESIDENT' | 'FINANCE' | 'REALISASI' | 'SELESAI' +>[]; + +type ApprovalStatusValue = + | 'HEAD_AREA' + | 'UNIT_VICE_PRESIDENT' + | 'FINANCE' + | 'REALISASI' + | 'SELESAI'; + +const isApprovalDateRequired = (status?: ApprovalStatusValue) => + status === 'REALISASI' || status === 'SELESAI'; + const RowOptionsMenu = ({ popoverPosition = 'bottom', props, deleteClickHandler, - returnToSearchParams, }: { popoverPosition: 'bottom' | 'top'; props: CellContext; deleteClickHandler: () => void; - returnToSearchParams: URLSearchParams; }) => { const popoverId = `expense#${props.row.original.id}`; const popoverAnchorName = `--anchor-expense#${props.row.original.id}`; @@ -105,11 +123,7 @@ const RowOptionsMenu = ({
+ - +
)} {/* Search and Filter */} -
+
{ totalItems={ isResponseSuccess(expenses) ? expenses?.meta?.total_results : 0 } - onPageChange={pageChangeHandler} - onPageSizeChange={pageSizeChangeHandler} + onPageChange={setPage} + onPageSizeChange={setPageSize} isLoading={isLoading} sorting={sorting} setSorting={setSorting} @@ -884,6 +943,100 @@ const ExpensesTable = () => { }} /> + +
+
+

+ Bulk Approve Expense +

+ +
+ +
+ { + const nextValue = val as OptionType | null; + setBulkApprovalStatus(nextValue); + + if (!isApprovalDateRequired(nextValue?.value)) { + setBulkApprovalDate(''); + } + }} + placeholder='Pilih status approval' + isClearable + /> + + {isApprovalDateRequired(bulkApprovalStatus?.value) && ( + + )} + +