feat(FE): API integration debt supplier report

This commit is contained in:
randy-ar
2026-01-11 21:02:58 +07:00
parent 0da9f9d651
commit 677025b4a2
6 changed files with 91 additions and 282 deletions
@@ -2,7 +2,7 @@
import Tabs from '@/components/Tabs'; import Tabs from '@/components/Tabs';
import CustomerPaymentTab from '@/components/pages/report/finance/tab/CustomerPaymentTab'; import CustomerPaymentTab from '@/components/pages/report/finance/tab/CustomerPaymentTab';
import DebtSupplier from '@/components/pages/report/finance/tab/DebtSupplier'; import DebtSupplierTab from '@/components/pages/report/finance/tab/DebtSupplierTab';
const FinanceTabs = () => { const FinanceTabs = () => {
const tabs = [ const tabs = [
@@ -16,7 +16,7 @@ const FinanceTabs = () => {
id: '2', id: '2',
label: 'Rekapitulasi Hutang Ke Supplier', label: 'Rekapitulasi Hutang Ke Supplier',
content: <DebtSupplier />, content: <DebtSupplierTab />,
}, },
]; ];
@@ -22,6 +22,7 @@ import { ColumnDef } from '@tanstack/react-table';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import useSWR from 'swr'; import useSWR from 'swr';
import Pagination from '@/components/Pagination';
const DebtSupplierTab = () => { const DebtSupplierTab = () => {
// ===== STATE MANAGEMENT ===== // ===== STATE MANAGEMENT =====
@@ -87,37 +88,37 @@ const DebtSupplierTab = () => {
}, [filterModal, filterStartDate, filterEndDate]); }, [filterModal, filterStartDate, filterEndDate]);
// ===== DATA FETCHING ===== // ===== DATA FETCHING =====
// const { data: debtSupplier, isLoading } = useSWR( const { data: debtSupplier, isLoading } = useSWR(
// isSubmitted isSubmitted
// ? () => { ? () => {
// const params = { const params = {
// supplier_id: supplier_id:
// filterSupplier.length > 0 filterSupplier.length > 0
// ? filterSupplier.map((v) => String(v.value)).join(',') ? filterSupplier.map((v) => String(v.value)).join(',')
// : undefined, : undefined,
// filter_by: 'do_date' as const, filter_by: 'do_date' as const,
// start_date: filterStartDate || undefined, start_date: filterStartDate || undefined,
// end_date: filterEndDate || undefined, end_date: filterEndDate || undefined,
// page: currentPage, page: currentPage,
// limit: pageSize, limit: pageSize,
// }; };
// return ['debt-supplier-report', params]; return ['debt-supplier-report', params];
// } }
// : null, : null,
// ([, params]) => ([, params]) =>
// FinanceApi.getDebtSupplierReport( FinanceApi.getDebtSupplierReport(
// params.supplier_id, params.supplier_id,
// params.filter_by, params.filter_by,
// params.start_date, params.start_date,
// params.end_date, params.end_date,
// params.page, params.page,
// params.limit params.limit
// ) )
// );
const { data: debtSupplier, isLoading } = useSWR(FinanceApi.basePath, () =>
FinanceApi.getDebtSupplierReport()
); );
// const { data: debtSupplier, isLoading } = useSWR(FinanceApi.basePath, () =>
// FinanceApi.getDebtSupplierReport()
// );
const data: DebtSupplier[] = useMemo( const data: DebtSupplier[] = useMemo(
() => () =>
@@ -391,7 +392,7 @@ const DebtSupplierTab = () => {
<> <>
<div className='w-full p-0 sm:p-4 flex flex-col gap-4'> <div className='w-full p-0 sm:p-4 flex flex-col gap-4'>
<Card <Card
subtitle='Laporan > Kontrol Pembayaran Customer' subtitle='Laporan > Kontrol Hutang Supplier'
className={{ wrapper: 'w-full', body: 'p-1!' }} className={{ wrapper: 'w-full', body: 'p-1!' }}
> >
<div className='mb-4 flex justify-end gap-2 [&_button]:px-4'> <div className='mb-4 flex justify-end gap-2 [&_button]:px-4'>
@@ -421,7 +422,7 @@ const DebtSupplierTab = () => {
</div> </div>
</Card> </Card>
{/* {!isSubmitted ? ( {!isSubmitted ? (
<div className='mt-6 text-center text-gray-500'> <div className='mt-6 text-center text-gray-500'>
Silakan klik tombol Filter untuk mengatur filter dan menampilkan Silakan klik tombol Filter untuk mengatur filter dan menampilkan
data. data.
@@ -434,44 +435,59 @@ const DebtSupplierTab = () => {
<div className='mt-6 text-center text-gray-500'> <div className='mt-6 text-center text-gray-500'>
Tidak ada data yang dapat ditampilkan... Tidak ada data yang dapat ditampilkan...
</div> </div>
) : ( */} ) : (
{data.map((supplierReport) => { data.map((supplierReport) => {
return ( return (
<Card <Card
key={supplierReport.supplier.id} key={supplierReport.supplier.id}
title={supplierReport.supplier.name} title={supplierReport.supplier.name}
className={{ wrapper: 'w-full' }} className={{ wrapper: 'w-full' }}
variant='bordered' variant='bordered'
collapsible={true} collapsible={true}
> >
<Table <Table
data={supplierReport.rows} data={supplierReport.rows}
columns={getTableColumns(supplierReport)} columns={getTableColumns(supplierReport)}
pageSize={10} pageSize={10}
renderFooter={supplierReport.rows.length > 0} renderFooter={supplierReport.rows.length > 0}
className={{ className={{
containerClassName: 'w-full', containerClassName: 'w-full',
tableWrapperClassName: 'overflow-x-auto mt-4', tableWrapperClassName: 'overflow-x-auto mt-4',
tableClassName: 'w-full table-auto text-sm', tableClassName: 'w-full table-auto text-sm',
headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', headerRowClassName: 'border-b border-b-gray-200 bg-gray-50',
headerColumnClassName: headerColumnClassName:
'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200',
bodyRowClassName: bodyRowClassName:
'hover:bg-gray-50 transition-colors border-b border-l border-r border-b-gray-200 border-l-gray-200 border-r-gray-200', 'hover:bg-gray-50 transition-colors border-b border-l border-r border-b-gray-200 border-l-gray-200 border-r-gray-200',
bodyColumnClassName: bodyColumnClassName:
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap', 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
tableFooterClassName: tableFooterClassName:
'bg-gray-100 font-semibold border border-gray-200', 'bg-gray-100 font-semibold border border-gray-200',
footerRowClassName: 'border-t-2 border-gray-300', footerRowClassName: 'border-t-2 border-gray-300',
footerColumnClassName: footerColumnClassName:
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap', 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
paginationClassName: 'hidden', paginationClassName: 'hidden',
}} }}
/> />
</Card> </Card>
); );
})} })
)}
</div> </div>
{meta && data.length > 0 && (
<div className='mt-6'>
<Pagination
currentPage={meta.page}
totalItems={meta.total_results}
onPageChange={handlePageChange}
onRowChange={handleRowChange}
onNextPage={handleNextPage}
onPrevPage={handlePrevPage}
rowOptions={[10, 25, 50, 100]}
itemsPerPage={meta.limit}
/>
</div>
)}
{/* Filter Modal */} {/* Filter Modal */}
<Modal <Modal
@@ -557,11 +573,10 @@ const DebtSupplierTab = () => {
label='Filter Berdasarkan' label='Filter Berdasarkan'
placeholder='Pilih Filter Berdasarkan' placeholder='Pilih Filter Berdasarkan'
options={dataTypeOptions} options={dataTypeOptions}
value={dataTypeOptions[0]} value={filterDataType}
onChange={(val) => { onChange={(val) => {
setFilterDataType(val ? (val as OptionType) : undefined); setFilterDataType(val ? (val as OptionType) : undefined);
}} }}
isDisabled={true}
className={{ wrapper: 'w-full' }} className={{ wrapper: 'w-full' }}
/> />
</div> </div>
+4 -1
View File
@@ -117,7 +117,10 @@ export const ROUTE_PERMISSIONS: Record<string, string[]> = {
'/report/expense/': ['lti.repport.expense.list'], '/report/expense/': ['lti.repport.expense.list'],
'/report/marketing/': ['lti.repport.delivery.list'], '/report/marketing/': ['lti.repport.delivery.list'],
'/report/production-result/': ['lti.repport.production_result.list'], '/report/production-result/': ['lti.repport.production_result.list'],
'/report/finance/': ['lti.repport.finance.list'], '/report/finance/': [
'lti.repport.finance.list',
'lti.repport.debtsupplier.list',
],
// Inventory // Inventory
'/inventory/adjustment/': ['lti.inventory.list'], '/inventory/adjustment/': ['lti.inventory.list'],
-168
View File
@@ -1,168 +0,0 @@
[
{
"supplier": {
"id": 1,
"name": "INDOVETRACO MAKMUR ABADI (IMA)"
},
"rows": [
{
"pr_number": "PR-MBU-02145",
"po_number": "PO-MBU-02145",
"pr_date": "2025-11-03",
"po_date": "2025-11-05",
"aging": 68,
"area": {
"id": 101,
"name": "Banten 1"
},
"warehouse": {
"id": 201,
"name": "Gudang Area Banten"
},
"due_date": "2025-12-03",
"due_status": "Sudah Jatuh Tempo",
"total_price": 8658000,
"payment_price": 0,
"debt_price": -8658000,
"status": "Belum Lunas",
"travel_number": "-"
}
],
"total": {
"aging": 68,
"total_price": 8658000,
"payment_price": 0,
"debt_price": -8658000
}
},
{
"supplier": {
"id": 2,
"name": "MANDIRI BERLIAN UNGGAS (MANBU)"
},
"rows": [
{
"pr_number": "PR-MBU-01980",
"po_number": "PO-MBU-01980",
"pr_date": "2025-08-20",
"po_date": "2025-09-18",
"aging": 143,
"area": {
"id": 101,
"name": "Banten 1"
},
"warehouse": {
"id": 202,
"name": "GUDANG CIANGSANA 5 (P16)"
},
"due_date": "2025-08-21",
"due_status": "Sudah Jatuh Tempo",
"total_price": 266700000,
"payment_price": 0,
"debt_price": -267245000,
"status": "Belum Lunas",
"travel_number": "-"
},
{
"pr_number": "PR-MBU-01981",
"po_number": "PO-MBU-01981",
"pr_date": "2025-08-21",
"po_date": "2025-09-18",
"aging": 142,
"area": {
"id": 102,
"name": "Priangan Timur 2"
},
"warehouse": {
"id": 203,
"name": "GUDANG SINGAPARNA 1 P.7"
},
"due_date": "2025-08-22",
"due_status": "Sudah Jatuh Tempo",
"total_price": 157480000,
"payment_price": 0,
"debt_price": -424725000,
"status": "Belum Lunas",
"travel_number": "-"
}
],
"total": {
"aging": 143,
"total_price": 424180000,
"payment_price": 0,
"debt_price": -692465000
}
},
{
"supplier": {
"id": 3,
"name": "SUMBER PROTEIN JAYA"
},
"rows": [
{
"pr_number": "PR-SPJ-00551",
"po_number": "PO-SPJ-00551",
"pr_date": "2025-12-01",
"po_date": "2025-12-02",
"aging": 39,
"area": {
"id": 103,
"name": "Jawa Tengah"
},
"warehouse": {
"id": 204,
"name": "Gudang Solo"
},
"due_date": "2026-01-01",
"due_status": "Mendekati Jatuh Tempo",
"total_price": 45000000,
"payment_price": 15000000,
"debt_price": -30000000,
"status": "Belum Lunas",
"travel_number": "SJ-001/XII"
}
],
"total": {
"aging": 39,
"total_price": 45000000,
"payment_price": 15000000,
"debt_price": -30000000
}
},
{
"supplier": {
"id": 4,
"name": "CHAROEN POKPHAND INDONESIA"
},
"rows": [
{
"pr_number": "PR-CPI-0992",
"po_number": "PO-CPI-0992",
"pr_date": "2025-11-15",
"po_date": "2025-11-16",
"aging": 56,
"area": {
"id": 104,
"name": "Jawa Timur"
},
"warehouse": {
"id": 205,
"name": "Gudang Surabaya"
},
"due_date": "2025-12-15",
"due_status": "Sudah Jatuh Tempo",
"total_price": 125000000,
"payment_price": 0,
"debt_price": -125000000,
"status": "Belum Lunas",
"travel_number": "-"
}
],
"total": {
"aging": 56,
"total_price": 125000000,
"payment_price": 0,
"debt_price": -125000000
}
}
]
-35
View File
@@ -1,35 +0,0 @@
/**
* Dummy data for DebtSupplier[]
* Generated from: debt.supllier.dummy.json
*
* This file is auto-generated. Do not edit manually.
*/
import { DebtSupplier } from '../../types/api/report/debt-supplier';
import { BaseApiResponse } from '@/types/api/api-general';
import dummyData from './debt.supllier.dummy.json';
/**
* Get dummy DebtSupplier[] data
* @returns Promise with BaseApiResponse containing DebtSupplier[]
*/
export async function getDebtSupllierDummy(): Promise<
BaseApiResponse<DebtSupplier[]>
> {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
code: 200,
status: 'success',
message: 'Data retrieved successfully',
meta: {
total_pages: 1,
page: 1,
limit: 10,
total_results: dummyData.length,
},
data: dummyData as unknown as DebtSupplier[],
});
}, 500);
});
}
@@ -1,4 +1,3 @@
import { getDebtSupllierDummy } from '@/dummy/report/debt.supllier.dummy';
import { BaseApiService } from '@/services/api/base'; import { BaseApiService } from '@/services/api/base';
import { BaseApiResponse } from '@/types/api/api-general'; import { BaseApiResponse } from '@/types/api/api-general';
import { CustomerPaymentReport } from '@/types/api/report/customer-payment'; import { CustomerPaymentReport } from '@/types/api/report/customer-payment';
@@ -47,7 +46,6 @@ export class FinanceApiService extends BaseApiService<
page?: number, page?: number,
limit?: number limit?: number
): Promise<BaseApiResponse<DebtSupplier[]> | undefined> { ): Promise<BaseApiResponse<DebtSupplier[]> | undefined> {
return (await getDebtSupllierDummy()) as BaseApiResponse<DebtSupplier[]>;
return await this.customRequest<BaseApiResponse<DebtSupplier[]>>( return await this.customRequest<BaseApiResponse<DebtSupplier[]>>(
`debt-supplier`, `debt-supplier`,
{ {
@@ -66,7 +64,3 @@ export class FinanceApiService extends BaseApiService<
} }
export const FinanceApi = new FinanceApiService('reports'); export const FinanceApi = new FinanceApiService('reports');
// export const FinanceApi = new FinanceApiService(
// 'http://localhost:4010/api/reports/finance'
// );