mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 07:45:47 +00:00
refactor(FE): Refactor FinanceTable and simplify Finance page structure
This commit is contained in:
@@ -3,12 +3,7 @@
|
|||||||
import FinanceTable from '@/components/pages/finance/FinanceTable';
|
import FinanceTable from '@/components/pages/finance/FinanceTable';
|
||||||
|
|
||||||
const Finance = () => {
|
const Finance = () => {
|
||||||
return (
|
return <FinanceTable />;
|
||||||
<section className='size-full p-6'>
|
|
||||||
<div className='flex flex-row gap-4'></div>
|
|
||||||
<FinanceTable />
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Finance;
|
export default Finance;
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
'use client';
|
||||||
import { CellContext } from '@tanstack/react-table';
|
|
||||||
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
import { CellContext, ColumnDef } from '@tanstack/react-table';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
import { Icon } from '@iconify/react';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
|
import { cn, formatCurrency, formatDate, formatTitleCase } from '@/lib/helper';
|
||||||
|
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import Card from '@/components/Card';
|
|
||||||
import DateInput from '@/components/input/DateInput';
|
import DateInput from '@/components/input/DateInput';
|
||||||
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
||||||
import SelectInput, {
|
import SelectInput, {
|
||||||
@@ -12,7 +21,6 @@ import SelectInput, {
|
|||||||
useSelect,
|
useSelect,
|
||||||
} from '@/components/input/SelectInput';
|
} from '@/components/input/SelectInput';
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import { formatCurrency, formatDate, formatTitleCase } from '@/lib/helper';
|
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { Finance } from '@/types/api/finance/finance';
|
import { Finance } from '@/types/api/finance/finance';
|
||||||
import {
|
import {
|
||||||
@@ -25,31 +33,61 @@ import { FinanceApi } from '@/services/api/finance';
|
|||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { BankApi, CustomerApi, SupplierApi } from '@/services/api/master-data';
|
import { BankApi, CustomerApi, SupplierApi } from '@/services/api/master-data';
|
||||||
import { Bank } from '@/types/api/master-data/bank';
|
import { Bank } from '@/types/api/master-data/bank';
|
||||||
import { useModal } from '@/components/Modal';
|
import Modal, { useModal } from '@/components/Modal';
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
|
|
||||||
import RequirePermission from '@/components/helper/RequirePermission';
|
import RequirePermission from '@/components/helper/RequirePermission';
|
||||||
import { Icon } from '@iconify/react';
|
|
||||||
import RowDropdownOptions from '@/components/table/RowDropdownOptions';
|
|
||||||
import RowCollapseOptions from '@/components/table/RowCollapseOptions';
|
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
import { useUiStore } from '@/stores/ui/ui.store';
|
||||||
import {
|
import {
|
||||||
FinanceTableFilterSchema,
|
FinanceTableFilterSchema,
|
||||||
FinanceTableFilterValues,
|
FinanceTableFilterValues,
|
||||||
} from './FinanceTableFilter.schema';
|
} from './FinanceTableFilter.schema';
|
||||||
|
import SelectInputRadio from '@/components/input/SelectInputRadio';
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
type = 'dropdown',
|
popoverPosition = 'bottom',
|
||||||
props,
|
props,
|
||||||
deleteClickHandler,
|
deleteClickHandler,
|
||||||
}: {
|
}: {
|
||||||
type: 'dropdown' | 'collapse';
|
popoverPosition: 'bottom' | 'top';
|
||||||
props: CellContext<Finance, unknown>;
|
props: CellContext<Finance, unknown>;
|
||||||
deleteClickHandler: () => void;
|
deleteClickHandler: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
|
const popoverId = `finance#${props.row.original.id}`;
|
||||||
|
const popoverAnchorName = `--anchor-finance#${props.row.original.id}`;
|
||||||
|
|
||||||
|
const closePopover = () => {
|
||||||
|
const popover = document.getElementById(popoverId) as
|
||||||
|
| HTMLDivElement
|
||||||
|
| undefined;
|
||||||
|
popover?.hidePopover?.();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RowOptionsMenuWrapper type={type}>
|
<div className='relative'>
|
||||||
|
<Button
|
||||||
|
tabIndex={0}
|
||||||
|
variant='ghost'
|
||||||
|
color='none'
|
||||||
|
className='p-2'
|
||||||
|
popoverTarget={popoverId}
|
||||||
|
style={{ anchorName: popoverAnchorName } as React.CSSProperties}
|
||||||
|
>
|
||||||
|
<Icon icon='material-symbols:more-vert' width={20} height={20} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id={popoverId}
|
||||||
|
popover='auto'
|
||||||
|
style={{ positionAnchor: popoverAnchorName } as React.CSSProperties}
|
||||||
|
className={cn(
|
||||||
|
'fixed inset-auto bg-base-100 rounded-xl border border-base-content/5 shadow-sm w-full max-w-40 z-[100]',
|
||||||
|
popoverPosition === 'bottom'
|
||||||
|
? 'top-[anchor(bottom)] left-[anchor(left)] mt-1'
|
||||||
|
: 'bottom-[anchor(top)] left-[anchor(left)] mb-1'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col bg-base-100 rounded-xl'>
|
||||||
<RequirePermission
|
<RequirePermission
|
||||||
permissions={[
|
permissions={[
|
||||||
'lti.finance.transactions.detail',
|
'lti.finance.transactions.detail',
|
||||||
@@ -61,10 +99,11 @@ const RowOptionsMenu = ({
|
|||||||
<Button
|
<Button
|
||||||
href={`/finance/detail?financeId=${props.row.original.id}`}
|
href={`/finance/detail?financeId=${props.row.original.id}`}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
color='primary'
|
color='none'
|
||||||
className='justify-start text-sm'
|
className='p-3 justify-start text-sm font-semibold w-full'
|
||||||
|
onClick={closePopover}
|
||||||
>
|
>
|
||||||
<Icon icon='mdi:eye-outline' width={16} height={16} />
|
<Icon icon='heroicons:eye' width={20} height={20} />
|
||||||
Detail
|
Detail
|
||||||
</Button>
|
</Button>
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
@@ -76,10 +115,11 @@ const RowOptionsMenu = ({
|
|||||||
<Button
|
<Button
|
||||||
href={`/finance/detail/edit?financeId=${props.row.original.id}`}
|
href={`/finance/detail/edit?financeId=${props.row.original.id}`}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
color='warning'
|
color='none'
|
||||||
className='justify-start text-sm'
|
className='p-3 justify-start text-sm font-semibold w-full'
|
||||||
|
onClick={closePopover}
|
||||||
>
|
>
|
||||||
<Icon icon='material-symbols:edit-outline' width={16} height={16} />
|
<Icon icon='mdi:pencil-outline' width={20} height={20} />
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
@@ -92,10 +132,11 @@ const RowOptionsMenu = ({
|
|||||||
<Button
|
<Button
|
||||||
href={`/finance/detail/edit/initial-balance?financeId=${props.row.original.id}`}
|
href={`/finance/detail/edit/initial-balance?financeId=${props.row.original.id}`}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
color='warning'
|
color='none'
|
||||||
className='justify-start text-sm'
|
className='p-3 justify-start text-sm font-semibold w-full'
|
||||||
|
onClick={closePopover}
|
||||||
>
|
>
|
||||||
<Icon icon='material-symbols:edit-outline' width={16} height={16} />
|
<Icon icon='mdi:pencil-outline' width={20} height={20} />
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
@@ -108,10 +149,11 @@ const RowOptionsMenu = ({
|
|||||||
<Button
|
<Button
|
||||||
href={`/finance/detail/edit/injection?financeId=${props.row.original.id}`}
|
href={`/finance/detail/edit/injection?financeId=${props.row.original.id}`}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
color='warning'
|
color='none'
|
||||||
className='justify-start text-sm'
|
className='p-3 justify-start text-sm font-semibold w-full'
|
||||||
|
onClick={closePopover}
|
||||||
>
|
>
|
||||||
<Icon icon='material-symbols:edit-outline' width={16} height={16} />
|
<Icon icon='mdi:pencil-outline' width={20} height={20} />
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
@@ -119,21 +161,21 @@ const RowOptionsMenu = ({
|
|||||||
|
|
||||||
<RequirePermission permissions='lti.finance.transactions.delete'>
|
<RequirePermission permissions='lti.finance.transactions.delete'>
|
||||||
<Button
|
<Button
|
||||||
onClick={deleteClickHandler}
|
onClick={() => {
|
||||||
|
deleteClickHandler();
|
||||||
|
closePopover();
|
||||||
|
}}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
color='error'
|
color='error'
|
||||||
className='text-error hover:text-inherit'
|
className='p-3 justify-start text-sm font-semibold w-full focus-visible:text-error-content hover:text-error-content'
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon icon='mdi:delete-outline' width={20} height={20} />
|
||||||
icon='material-symbols:delete-outline-rounded'
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
className='justify-start text-sm'
|
|
||||||
/>
|
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
</RowOptionsMenuWrapper>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -171,6 +213,9 @@ const FinanceTable = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ===== FILTER MODAL STATE =====
|
||||||
|
const filterModal = useModal();
|
||||||
|
|
||||||
// ===== State =====
|
// ===== State =====
|
||||||
const deleteModal = useModal();
|
const deleteModal = useModal();
|
||||||
const [selectedTransactionType, setSelectedTransactionType] = useState<
|
const [selectedTransactionType, setSelectedTransactionType] = useState<
|
||||||
@@ -214,6 +259,18 @@ const FinanceTable = () => {
|
|||||||
updateFilter('sortBy', values.sort_by);
|
updateFilter('sortBy', values.sort_by);
|
||||||
updateFilter('startDate', values.start_date);
|
updateFilter('startDate', values.start_date);
|
||||||
updateFilter('endDate', values.end_date);
|
updateFilter('endDate', values.end_date);
|
||||||
|
filterModal.closeModal();
|
||||||
|
},
|
||||||
|
onReset: () => {
|
||||||
|
updateFilter('search', '');
|
||||||
|
resetSearchValue();
|
||||||
|
updateFilter('transactionTypes', '');
|
||||||
|
updateFilter('bankIds', '');
|
||||||
|
updateFilter('customerIds', '');
|
||||||
|
updateFilter('supplierIds', '');
|
||||||
|
updateFilter('sortBy', '');
|
||||||
|
updateFilter('startDate', '');
|
||||||
|
updateFilter('endDate', '');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -266,10 +323,41 @@ const FinanceTable = () => {
|
|||||||
});
|
});
|
||||||
}, [bankOptions, bankRawData]);
|
}, [bankOptions, bankRawData]);
|
||||||
|
|
||||||
|
// ===== ACTIVE FILTERS COUNT =====
|
||||||
|
const activeFiltersCount = useMemo(() => {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
if (tableFilterState.transactionTypes) count += 1;
|
||||||
|
if (tableFilterState.bankIds) count += 1;
|
||||||
|
if (tableFilterState.customerIds) count += 1;
|
||||||
|
if (tableFilterState.supplierIds) count += 1;
|
||||||
|
if (tableFilterState.sortBy) count += 1;
|
||||||
|
if (tableFilterState.startDate) count += 1;
|
||||||
|
if (tableFilterState.endDate) count += 1;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}, [
|
||||||
|
tableFilterState.transactionTypes,
|
||||||
|
tableFilterState.bankIds,
|
||||||
|
tableFilterState.customerIds,
|
||||||
|
tableFilterState.supplierIds,
|
||||||
|
tableFilterState.sortBy,
|
||||||
|
tableFilterState.startDate,
|
||||||
|
tableFilterState.endDate,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const hasFilters = activeFiltersCount > 0;
|
||||||
|
|
||||||
// ===== Handler =====
|
// ===== Handler =====
|
||||||
const searchChangeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const searchChangeHandler = useCallback(
|
||||||
filterFormik.setFieldValue('search', e.target.value);
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
};
|
updateFilter('search', e.target.value);
|
||||||
|
setSearchValue(e.target.value);
|
||||||
|
setPage(1);
|
||||||
|
},
|
||||||
|
[updateFilter, setSearchValue, setPage]
|
||||||
|
);
|
||||||
|
|
||||||
const transactionTypeChangeHandler = (
|
const transactionTypeChangeHandler = (
|
||||||
val: OptionType | OptionType[] | null
|
val: OptionType | OptionType[] | null
|
||||||
) => {
|
) => {
|
||||||
@@ -387,6 +475,11 @@ const FinanceTable = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleFilterModalOpen = () => {
|
||||||
|
filterModal.openModal();
|
||||||
|
filterFormik.validateForm();
|
||||||
|
};
|
||||||
|
|
||||||
const resetFilterHandler = () => {
|
const resetFilterHandler = () => {
|
||||||
setSelectedTransactionType(null);
|
setSelectedTransactionType(null);
|
||||||
setSelectedBank(null);
|
setSelectedBank(null);
|
||||||
@@ -406,6 +499,7 @@ const FinanceTable = () => {
|
|||||||
updateFilter('startDate', '');
|
updateFilter('startDate', '');
|
||||||
updateFilter('endDate', '');
|
updateFilter('endDate', '');
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
@@ -417,8 +511,8 @@ const FinanceTable = () => {
|
|||||||
setIsDeleteLoading(false);
|
setIsDeleteLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns: ColumnDef<Finance>[] = useMemo(
|
||||||
return [
|
() => [
|
||||||
{
|
{
|
||||||
header: 'ID',
|
header: 'ID',
|
||||||
accessorKey: 'payment_code',
|
accessorKey: 'payment_code',
|
||||||
@@ -498,32 +592,17 @@ const FinanceTable = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
{currentPageSize > 2 && (
|
|
||||||
<RowDropdownOptions isLast2Rows={isLast2Rows}>
|
|
||||||
<RowOptionsMenu
|
<RowOptionsMenu
|
||||||
type='dropdown'
|
|
||||||
props={props}
|
props={props}
|
||||||
deleteClickHandler={deleteClickHandler}
|
deleteClickHandler={deleteClickHandler}
|
||||||
|
popoverPosition={isLast2Rows ? 'top' : 'bottom'}
|
||||||
/>
|
/>
|
||||||
</RowDropdownOptions>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{currentPageSize <= 2 && (
|
|
||||||
<RowCollapseOptions>
|
|
||||||
<RowOptionsMenu
|
|
||||||
type='collapse'
|
|
||||||
props={props}
|
|
||||||
deleteClickHandler={deleteClickHandler}
|
|
||||||
/>
|
|
||||||
</RowCollapseOptions>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
],
|
||||||
}, []);
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
@@ -555,69 +634,158 @@ const FinanceTable = () => {
|
|||||||
}, [resetSearchValue, dateErrorShown]);
|
}, [resetSearchValue, dateErrorShown]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className='size-full flex flex-col gap-6'>
|
<>
|
||||||
<div className='flex justify-end gap-2'>
|
<div className='w-full'>
|
||||||
|
{/* Header Section */}
|
||||||
|
<div className='w-full p-3 flex flex-row justify-between gap-3 flex-wrap border-b border-base-content/10'>
|
||||||
|
{/* Action Buttons */}
|
||||||
|
<div className='w-fit flex flex-row gap-3 flex-wrap'>
|
||||||
<RequirePermission permissions='lti.finance.injections.create'>
|
<RequirePermission permissions='lti.finance.injections.create'>
|
||||||
<Button
|
<Button
|
||||||
color='warning'
|
|
||||||
className='min-w-24'
|
|
||||||
href='/finance/add/injection'
|
href='/finance/add/injection'
|
||||||
|
color='warning'
|
||||||
|
className='px-3 py-2.5 w-fit text-sm text-base-100 rounded-lg shadow-sm'
|
||||||
>
|
>
|
||||||
Injection Saldo Bank
|
<Icon icon='mdi:bank-transfer-in' width={20} height={20} />
|
||||||
|
Add Injection (Saldo Bank)
|
||||||
</Button>
|
</Button>
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
<RequirePermission permissions='lti.finance.initial_balances.create'>
|
<RequirePermission permissions='lti.finance.initial_balances.create'>
|
||||||
<Button
|
<Button
|
||||||
color='info'
|
|
||||||
className='text-white min-w-24'
|
|
||||||
href='/finance/add/initial-balance'
|
href='/finance/add/initial-balance'
|
||||||
|
color='info'
|
||||||
|
className='px-3 py-2.5 w-fit text-sm text-base-100 rounded-lg shadow-sm'
|
||||||
>
|
>
|
||||||
Saldo Awal
|
<Icon icon='mdi:cash-register' width={20} height={20} />
|
||||||
|
Add Initial Balance
|
||||||
</Button>
|
</Button>
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
<RequirePermission permissions='lti.finance.payments.create'>
|
<RequirePermission permissions='lti.finance.payments.create'>
|
||||||
<Button color='primary' className='min-w-24' href='/finance/add'>
|
<Button
|
||||||
Tambah
|
href='/finance/add'
|
||||||
|
color='primary'
|
||||||
|
className='px-3 py-2.5 w-fit text-sm text-base-100 rounded-lg shadow-sm'
|
||||||
|
>
|
||||||
|
<Icon icon='heroicons:plus' width={20} height={20} />
|
||||||
|
Add Finance
|
||||||
</Button>
|
</Button>
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
</div>
|
</div>
|
||||||
<Card
|
|
||||||
variant='bordered'
|
{/* Search and Filter */}
|
||||||
|
<div className='flex flex-1 flex-row justify-start sm:justify-end items-center gap-3 flex-wrap'>
|
||||||
|
<DebouncedTextInput
|
||||||
|
name='search'
|
||||||
|
placeholder='Search'
|
||||||
|
value={tableFilterState.search ?? ''}
|
||||||
|
onChange={searchChangeHandler}
|
||||||
|
startAdornment={
|
||||||
|
<Icon
|
||||||
|
icon='heroicons:magnifying-glass'
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
}
|
||||||
className={{
|
className={{
|
||||||
wrapper: 'w-full',
|
wrapper: 'w-full min-w-24 max-w-3xs',
|
||||||
|
inputWrapper: 'rounded-xl! shadow-button-soft',
|
||||||
|
input:
|
||||||
|
'placeholder:font-semibold placeholder:text-base-content/50',
|
||||||
}}
|
}}
|
||||||
footer={
|
/>
|
||||||
<div className='flex justify-end gap-2'>
|
|
||||||
<Button
|
<Button
|
||||||
color='warning'
|
variant='outline'
|
||||||
className='min-w-24'
|
color='none'
|
||||||
onClick={resetFilterHandler}
|
onClick={handleFilterModalOpen}
|
||||||
|
className={cn(
|
||||||
|
'px-3 py-2.5 gap-1.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft transition-all',
|
||||||
|
{
|
||||||
|
'border-primary-gradient text-primary': hasFilters,
|
||||||
|
}
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
Reset
|
<Icon icon='heroicons:funnel' width={20} height={20} />
|
||||||
</Button>
|
Filter
|
||||||
<Button
|
{hasFilters && (
|
||||||
color='primary'
|
<span className='w-5 h-5 text-white bg-[#FF3535] rounded-lg border border-base-300 flex items-center justify-center text-xs'>
|
||||||
className='min-w-24'
|
{activeFiltersCount}
|
||||||
onClick={() => filterFormik.handleSubmit()}
|
</span>
|
||||||
>
|
)}
|
||||||
Cari
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Table Section */}
|
||||||
|
<div className='flex flex-col mb-4 -mx-4 px-4'>
|
||||||
|
{isLoading ? (
|
||||||
|
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||||
|
<span className='loading loading-spinner loading-xl' />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Table<Finance>
|
||||||
|
data={isResponseSuccess(finances) ? finances.data : []}
|
||||||
|
columns={columns}
|
||||||
|
pageSize={tableFilterState.pageSize}
|
||||||
|
page={tableFilterState.page}
|
||||||
|
totalItems={
|
||||||
|
isResponseSuccess(finances) ? finances.meta?.total_results : 0
|
||||||
}
|
}
|
||||||
|
onPageChange={setPage}
|
||||||
|
onPageSizeChange={setPageSize}
|
||||||
|
isLoading={isLoading}
|
||||||
|
className={{
|
||||||
|
containerClassName: 'p-3 mb-0',
|
||||||
|
headerColumnClassName: 'text-nowrap',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Filter Modal */}
|
||||||
|
<Modal
|
||||||
|
ref={filterModal.ref}
|
||||||
|
className={{
|
||||||
|
modal: 'p-0',
|
||||||
|
modalBox: 'p-0 rounded-[0.875rem] xl:max-w-4/12 max-w-sm',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className='grid grid-cols-4 gap-6'>
|
{/* Modal Header */}
|
||||||
|
<div className='flex items-center justify-between gap-2 border-b border-base-content/10 p-4'>
|
||||||
|
<div className='flex items-center gap-2 text-primary'>
|
||||||
|
<Icon icon='heroicons:funnel' width={20} height={20} />
|
||||||
|
<h3 className='font-medium text-sm'>Filter Data</h3>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant='link'
|
||||||
|
onClick={filterModal.closeModal}
|
||||||
|
className='text-base-content/50 hover:text-base-content transition-colors cursor-pointer'
|
||||||
|
>
|
||||||
|
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<form
|
||||||
|
onSubmit={filterFormik.handleSubmit}
|
||||||
|
onReset={filterFormik.handleReset}
|
||||||
|
>
|
||||||
|
<div className='p-4 flex flex-col gap-1.5'>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
options={FINANCE_TRANSACTION_TYPE_OPTIONS}
|
options={FINANCE_TRANSACTION_TYPE_OPTIONS}
|
||||||
label='Jenis Transaksi'
|
label='Jenis Transaksi'
|
||||||
|
placeholder='Pilih Jenis Transaksi'
|
||||||
value={selectedTransactionType}
|
value={selectedTransactionType}
|
||||||
onChange={transactionTypeChangeHandler}
|
onChange={transactionTypeChangeHandler}
|
||||||
|
onInputChange={() => {}}
|
||||||
closeMenuOnSelect={false}
|
closeMenuOnSelect={false}
|
||||||
isClearable
|
isClearable
|
||||||
isMulti
|
isMulti
|
||||||
|
className={{ wrapper: 'w-full' }}
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
options={customerOptions}
|
options={customerOptions}
|
||||||
label={'Customer'}
|
label='Customer'
|
||||||
|
placeholder='Pilih Customer'
|
||||||
value={selectedCustomerId}
|
value={selectedCustomerId}
|
||||||
onChange={customerIdChangeHandler}
|
onChange={customerIdChangeHandler}
|
||||||
onInputChange={customerInputValue}
|
onInputChange={customerInputValue}
|
||||||
@@ -626,10 +794,12 @@ const FinanceTable = () => {
|
|||||||
closeMenuOnSelect={false}
|
closeMenuOnSelect={false}
|
||||||
isClearable
|
isClearable
|
||||||
isMulti
|
isMulti
|
||||||
|
className={{ wrapper: 'w-full' }}
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
options={supplierOptions}
|
options={supplierOptions}
|
||||||
label={'Supplier'}
|
label='Supplier'
|
||||||
|
placeholder='Pilih Supplier'
|
||||||
value={selectedSupplierId}
|
value={selectedSupplierId}
|
||||||
onChange={supplierIdChangeHandler}
|
onChange={supplierIdChangeHandler}
|
||||||
onInputChange={supplierInputValue}
|
onInputChange={supplierInputValue}
|
||||||
@@ -638,10 +808,12 @@ const FinanceTable = () => {
|
|||||||
closeMenuOnSelect={false}
|
closeMenuOnSelect={false}
|
||||||
isClearable
|
isClearable
|
||||||
isMulti
|
isMulti
|
||||||
|
className={{ wrapper: 'w-full' }}
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
options={bankSelectOptions}
|
options={bankSelectOptions}
|
||||||
label='Bank'
|
label='Bank'
|
||||||
|
placeholder='Pilih Bank'
|
||||||
value={selectedBank}
|
value={selectedBank}
|
||||||
onChange={bankChangeHandler}
|
onChange={bankChangeHandler}
|
||||||
onInputChange={bankInputValue}
|
onInputChange={bankInputValue}
|
||||||
@@ -649,13 +821,16 @@ const FinanceTable = () => {
|
|||||||
closeMenuOnSelect={false}
|
closeMenuOnSelect={false}
|
||||||
isClearable
|
isClearable
|
||||||
isMulti
|
isMulti
|
||||||
|
className={{ wrapper: 'w-full' }}
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
<SelectInputRadio
|
||||||
options={sortByOptions}
|
options={sortByOptions}
|
||||||
label='Urutkan Berdasarkan'
|
label='Urutkan Berdasarkan'
|
||||||
|
placeholder='Pilih Urutan'
|
||||||
value={selectedSortBy}
|
value={selectedSortBy}
|
||||||
onChange={sortByChangeHandler}
|
onChange={sortByChangeHandler}
|
||||||
isClearable
|
isClearable
|
||||||
|
className={{ wrapper: 'w-full' }}
|
||||||
/>
|
/>
|
||||||
<DateInput
|
<DateInput
|
||||||
name='start_date'
|
name='start_date'
|
||||||
@@ -679,27 +854,38 @@ const FinanceTable = () => {
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<DebouncedTextInput
|
|
||||||
name='search'
|
|
||||||
label='Cari'
|
|
||||||
placeholder='Cari'
|
|
||||||
value={filterFormik.values.search}
|
|
||||||
onChange={searchChangeHandler}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
|
||||||
<Table<Finance>
|
{/* Modal Footer */}
|
||||||
data={isResponseSuccess(finances) ? finances.data : []}
|
<div className='flex justify-between items-center gap-4 p-4 border-t border-base-content/10 bg-gray-50'>
|
||||||
columns={columns}
|
<Button
|
||||||
pageSize={tableFilterState.pageSize}
|
type='button'
|
||||||
page={tableFilterState.page}
|
variant='soft'
|
||||||
onPageChange={setPage}
|
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
||||||
onPageSizeChange={setPageSize}
|
onClick={() => {
|
||||||
totalItems={
|
filterFormik.resetForm();
|
||||||
isResponseSuccess(finances) ? finances.meta?.total_results : 0
|
setSelectedTransactionType(null);
|
||||||
}
|
setSelectedBank(null);
|
||||||
isLoading={isLoading}
|
setSelectedCustomerId(null);
|
||||||
/>
|
setSelectedSupplierId(null);
|
||||||
|
setSelectedSortBy(null);
|
||||||
|
resetFilterHandler();
|
||||||
|
filterModal.closeModal();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reset Filter
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type='submit'
|
||||||
|
className='min-w-40 text-sm rounded-lg py-3 text-white font-semibold'
|
||||||
|
disabled={!filterFormik.isValid || filterFormik.isSubmitting}
|
||||||
|
>
|
||||||
|
Apply Filter
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
ref={deleteModal.ref}
|
ref={deleteModal.ref}
|
||||||
type='error'
|
type='error'
|
||||||
@@ -714,7 +900,7 @@ const FinanceTable = () => {
|
|||||||
onClick: confirmationModalDeleteClickHandler,
|
onClick: confirmationModalDeleteClickHandler,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</section>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user