feat(FE-350): add filtering table

This commit is contained in:
randy-ar
2025-12-24 16:44:53 +07:00
parent 36ff6d04ee
commit 8c95dc8327
10 changed files with 933 additions and 2843 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
npm run format npm run format
npm run lint npm run lint
npm run build npx tsc --noEmit
+1 -1
View File
@@ -11,7 +11,7 @@ const FinanceDetailPage = () => {
const financeId = useSearchParams().get('financeId'); const financeId = useSearchParams().get('financeId');
const { data: finance } = useSWR(financeId, () => const { data: finance } = useSWR(financeId, () =>
FinanceApi.getSingleFetcher(financeId as string) FinanceApi.getSingle(Number(financeId))
); );
if (!financeId) { if (!financeId) {
+3 -21
View File
@@ -1,30 +1,12 @@
'use client'; 'use client';
import FinanceTable from '@/components/pages/finance/FinanceTable'; import FinanceTable from '@/components/pages/finance/FinanceTable';
import { isResponseSuccess } from '@/lib/api-helper';
import { FinanceApi } from '@/services/api/finance';
import useSWR from 'swr';
const Finance = () => { const Finance = () => {
const { data: finances, isLoading: isLoadingFinances } = useSWR(
`${FinanceApi.basePath}`,
() => FinanceApi.getAllFetcher()
);
if (isLoadingFinances) {
return (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
);
}
return ( return (
<section className='size-full p-4'> <section className='size-full p-6'>
<h1>Finance</h1> <div className='flex flex-row gap-4'></div>
<FinanceTable <FinanceTable />
finances={isResponseSuccess(finances) ? finances.data : []}
/>
</section> </section>
); );
}; };
+174 -64
View File
@@ -1,87 +1,197 @@
'use client'; 'use client';
import { ReactNode, useEffect } from 'react'; import { ReactNode, useEffect } from 'react';
import useSWR from 'swr'; import { useRouter } from 'next/navigation';
import useSWRImmutable from 'swr/immutable';
import { useAuth } from '@/services/hooks/useAuth'; import { useAuth } from '@/services/hooks/useAuth';
import { httpClientFetcher, SWRHttpKey } from '@/services/http/client'; import { httpClientFetcher, SWRHttpKey } from '@/services/http/client';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { isResponseSuccess } from '@/lib/api-helper';
import { BaseApiResponse, GetMeResponse } from '@/types/api/api-general'; import { GetMeResponse } from '@/types/api/api-general';
import { AxiosError } from 'axios';
import { redirectToSSO } from '@/lib/auth-helper'; // TODO: delete this later, DONT HARDCODE USER DATA
const DUMMY_USER = {
id: 1,
email: 'admin@mbugroup.id',
npk: '0001',
name: 'Super Admin',
image: null,
created_at: '2025-09-30T03:24:20.899229Z',
updated_at: '2025-09-30T03:24:20.899229Z',
roles: [
{
id: 1,
key: 'mbu.super_admin',
name: 'MBU Administrator',
client: {
id: 1,
name: 'PT Mitra Berlian Unggas',
alias: 'MBU',
},
permissions: [
{
id: 1,
name: 'mbu:purchase:read',
action: 'read',
client: {
id: 1,
name: 'PT Mitra Berlian Unggas',
alias: 'MBU',
},
},
{
id: 2,
name: 'mbu:purchase:create',
action: 'create',
client: {
id: 1,
name: 'PT Mitra Berlian Unggas',
alias: 'MBU',
},
},
{
id: 3,
name: 'mbu:purchase:approve',
action: 'approve',
client: {
id: 1,
name: 'PT Mitra Berlian Unggas',
alias: 'MBU',
},
},
],
},
{
id: 2,
key: 'lti.super_admin',
name: 'LTI Administrator',
client: {
id: 2,
name: 'PT Lumbung Telur Indonesia',
alias: 'LTI',
},
permissions: [
{
id: 4,
name: 'lti:purchase:read',
action: 'read',
client: {
id: 2,
name: 'PT Lumbung Telur Indonesia',
alias: 'LTI',
},
},
{
id: 5,
name: 'lti:purchase:create',
action: 'create',
client: {
id: 2,
name: 'PT Lumbung Telur Indonesia',
alias: 'LTI',
},
},
{
id: 6,
name: 'lti:purchase:approve',
action: 'approve',
client: {
id: 2,
name: 'PT Lumbung Telur Indonesia',
alias: 'LTI',
},
},
],
},
{
id: 3,
key: 'manbu.super_admin',
name: 'MANBU Administrator',
client: {
id: 3,
name: 'PT Mandiri Berlian Unggas',
alias: 'MANBU',
},
permissions: [
{
id: 7,
name: 'manbu:purchase:read',
action: 'read',
client: {
id: 3,
name: 'PT Mandiri Berlian Unggas',
alias: 'MANBU',
},
},
{
id: 8,
name: 'manbu:purchase:create',
action: 'create',
client: {
id: 3,
name: 'PT Mandiri Berlian Unggas',
alias: 'MANBU',
},
},
{
id: 9,
name: 'manbu:purchase:approve',
action: 'approve',
client: {
id: 3,
name: 'PT Mandiri Berlian Unggas',
alias: 'MANBU',
},
},
],
},
],
};
interface RequireAuthProps { interface RequireAuthProps {
children?: ReactNode; children?: ReactNode;
} }
const RequireAuth = ({ children }: RequireAuthProps) => { const RequireAuth = ({ children }: RequireAuthProps) => {
const { user, setUser, setIsLoadingUser } = useAuth(); const router = useRouter();
const { setUser, setIsLoadingUser } = useAuth();
const { const { data: userResponse, isLoading: isLoadingUserResponse } =
data: userResponse, useSWRImmutable<GetMeResponse & { ok?: boolean }, unknown, SWRHttpKey>(
isLoading: isLoadingUserResponse, '/auth/sso/userinfo',
error: userErrorResponse, httpClientFetcher,
} = useSWR< {
GetMeResponse & { ok?: boolean }, shouldRetryOnError: false,
AxiosError<BaseApiResponse>, revalidateOnFocus: false,
SWRHttpKey revalidateOnReconnect: false,
>('/sso/userinfo', httpClientFetcher, { refreshInterval: 0,
shouldRetryOnError: false, }
}); );
useEffect(() => {
setIsLoadingUser(isLoadingUserResponse);
}, [isLoadingUserResponse, setIsLoadingUser]);
useEffect(() => { useEffect(() => {
if (isResponseSuccess(userResponse)) { if (isResponseSuccess(userResponse)) {
setUser(userResponse.data); setUser(userResponse.data);
} else {
// router.replace(process.env.NEXT_PUBLIC_SSO_LOGIN_URL as string);
// TODO: remove this later, DONT HARDCODE USER DATA
setUser(DUMMY_USER);
} }
}, [userResponse, setUser]); }, [userResponse, setIsLoadingUser, setUser]);
// Explicitly handle 401 redirect from the component level // TODO: uncomment this later
useEffect(() => { // if (isLoadingUserResponse && !userResponse) {
if ( // return (
isResponseError(userResponse) && // <div className='w-full flex flex-row justify-center items-center p-4'>
userErrorResponse?.response?.status === 401 // <span className='loading loading-spinner loading-xl' />
) { // </div>
// Clear cache to prevent stale data from rendering children // );
// mutate('/sso/userinfo', undefined, { revalidate: false }); // Optional: if using global mutate // }
setUser(undefined);
redirectToSSO();
}
}, [userErrorResponse, setUser, userResponse]);
useEffect(() => { return <>{children}</>;
setIsLoadingUser(isLoadingUserResponse);
}, [isLoadingUserResponse]);
if (
(isLoadingUserResponse && !userResponse && !userErrorResponse) ||
(!userResponse && !userErrorResponse)
) {
return (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
);
}
if (userErrorResponse) {
return (
<div className='w-full h-screen flex flex-col justify-center items-center gap-4'>
<h2 className='text-2xl font-bold text-error'>Authentication Failed</h2>
<p className='text-gray-600'>
Please try refreshing the page or contact support if the problem
persists.
</p>
<button
className='btn btn-primary'
onClick={() => window.location.reload()}
>
Retry
</button>
</div>
);
}
return <>{isResponseSuccess(userResponse) && user && children}</>;
}; };
export default RequireAuth; export default RequireAuth;
+19 -47
View File
@@ -1,14 +1,15 @@
import Card from '@/components/Card'; import Card from '@/components/Card';
import { FormHeader } from '@/components/helper/form/FormHeader';
import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import DebouncedTextInput from '@/components/input/DebouncedTextInput';
import Table from '@/components/Table'; import Table from '@/components/Table';
import { formatCurrency } from '@/lib/helper'; import { formatCurrency, formatTitleCase } from '@/lib/helper';
import { Finance, FinanceReferences } from '@/types/api/finance/finance'; import { Finance } from '@/types/api/finance/finance';
const FinanceDetail = ({ finance }: { finance: Finance }) => { const FinanceDetail = ({ finance }: { finance: Finance }) => {
const informasiUmum = [ const informasiUmum = [
{ {
label: 'ID', label: 'ID',
value: finance.id, value: finance.payment_code,
}, },
{ {
label: 'Jenis Transaksi', label: 'Jenis Transaksi',
@@ -16,41 +17,47 @@ const FinanceDetail = ({ finance }: { finance: Finance }) => {
}, },
{ {
label: 'Pihak', label: 'Pihak',
value: finance.transaction_owner.name, value: finance.party.name,
}, },
{ {
label: 'Tanggal', label: 'Tanggal',
value: finance.transaction_date, value: finance.payment_date,
}, },
{ {
label: 'Metode Pembayaran', label: 'Metode Pembayaran',
value: finance.payment_method, value: finance.payment_method,
}, },
{
label: 'Catatan',
value: finance.notes || '-',
},
]; ];
const informasiTransfer = [ const informasiTransfer = [
{ {
label: 'No. Referensi', label: 'No. Referensi',
value: finance.references_number, value: finance.reference_number,
}, },
{ {
label: 'Nomor Rekening', label: 'Nomor Rekening',
value: `${finance.bank_account.alias} - ${finance.bank_account.account_number} - ${finance.bank_account.owner}`, value: `${finance.bank.alias} - ${finance.bank.account_number} - ${finance.bank.owner}`,
}, },
{ {
label: 'Rekening Customer', label: `Rekening ${formatTitleCase(finance.party.type)}`,
value: finance.transaction_account_number, value: finance.party.account_number,
}, },
{ {
label: 'Nominal', label: 'Nominal',
value: formatCurrency(finance.transaction_amount), value: formatCurrency(finance.expense_amount),
}, },
{ {
label: 'Sisa', label: 'Sisa',
value: formatCurrency(finance.balance_amount), value: formatCurrency(finance.income_amount),
}, },
]; ];
return ( return (
<div className='flex flex-col gap-4'> <div className='flex flex-col gap-6 p-6'>
<FormHeader title='' backUrl='/finance' />
<Card <Card
title='Detail Keuangan' title='Detail Keuangan'
className={{ className={{
@@ -100,41 +107,6 @@ const FinanceDetail = ({ finance }: { finance: Finance }) => {
}} }}
/> />
</div> </div>
<div className='flex flex-col gap-4'>
<div className='flex flex-row gap-4'>
<DebouncedTextInput
className={{
wrapper: 'max-w-1/4 ml-auto',
}}
name='cari'
placeholder='Cari'
/>
</div>
<Table<FinanceReferences>
data={finance.references}
columns={[
{
header: 'No.',
id: 'index',
accessorFn: (row, index) => index + 1,
},
{
header: 'No. Referensi',
id: 'references_number',
accessorKey: 'references_number',
},
{
header: 'Nominal',
id: 'nominal',
accessorFn: (row) =>
formatCurrency(Number(row.total_allocation)),
},
]}
className={{
containerClassName: 'mb-6',
}}
/>
</div>
</Card> </Card>
</div> </div>
); );
+301 -28
View File
@@ -1,39 +1,208 @@
import { ChangeEventHandler, useMemo, useState } from 'react';
import { Row } from '@tanstack/react-table';
import { useSearchParams } from 'next/navigation';
import useSWR from 'swr';
import Button from '@/components/Button'; import Button from '@/components/Button';
import Card from '@/components/Card';
import Dropdown from '@/components/dropdown/Dropdown'; import Dropdown from '@/components/dropdown/Dropdown';
import DateInput from '@/components/input/DateInput';
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
import SelectInput, {
OptionType,
useSelect,
} from '@/components/input/SelectInput';
import Menu from '@/components/menu/Menu'; import Menu from '@/components/menu/Menu';
import MenuItem from '@/components/menu/MenuItem'; import MenuItem from '@/components/menu/MenuItem';
import Table from '@/components/Table'; import Table from '@/components/Table';
import Tooltip from '@/components/Tooltip'; import Tooltip from '@/components/Tooltip';
import { formatCurrency, formatDate } from '@/lib/helper'; import { formatCurrency, formatDate } from '@/lib/helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
import { Finance } from '@/types/api/finance/finance'; import { Finance } from '@/types/api/finance/finance';
import { Row } from '@tanstack/react-table'; import { ROWS_OPTIONS } from '@/config/constant';
import { useMemo } from 'react'; import { FinanceApi } from '@/services/api/finance';
import { isResponseSuccess } from '@/lib/api-helper';
import { BankApi, CustomerApi, SupplierApi } from '@/services/api/master-data';
import { Bank } from '@/types/api/master-data/bank';
const FinanceTable = () => {
const {
state: tableFilterState,
updateFilter,
setPage,
setPageSize,
toQueryString: getTableFilterQueryString,
} = useTableFilter({
initial: {
search: '',
transactionType: '',
bankId: '',
partyType: '',
sortBy: '',
startDate: '',
endDate: '',
},
paramMap: {
page: 'page',
pageSize: 'limit',
transactionType: 'transaction_type',
bankId: 'bank_id',
partyType: 'party_type',
sortBy: 'sort_date',
startDate: 'start_date',
endDate: 'end_date',
},
});
// ===== State =====
const [searchParams, setSearchParams] = useSearchParams();
const [pendingFilters, setPendingFilters] = useState({
search: '',
transactionType: '',
bankId: '',
partyType: '',
sortBy: '',
startDate: '',
endDate: '',
});
const [selectedTransactionType, setSelectedTransactionType] =
useState<OptionType | null>(null);
const [selectedBank, setSelectedBank] = useState<OptionType | null>(null);
const [selectedPartyType, setSelectedPartyType] = useState<OptionType | null>(
null
);
const [selectedSortBy, setSelectedSortBy] = useState<OptionType | null>(null);
// ===== Fetch Data =====
const {
data: finances,
isLoading,
mutate: refreshFinances,
} = useSWR(
`${FinanceApi.basePath}${getTableFilterQueryString()}`,
FinanceApi.getAllFetcher
);
// ===== Options =====
const transactionTypeOptions = useMemo(() => {
return [
{ label: 'Transfer', value: 'TRANSFER' },
{ label: 'Cash', value: 'CASH' },
{ label: 'Card', value: 'CARD' },
{ label: 'Cheque', value: 'CHEQUE' },
{ label: 'Saldo', value: 'SALDO' },
];
}, []);
const partyTypeOptions = useMemo(() => {
return [
{ label: 'Customer', value: 'CUSTOMER' },
{ label: 'Supplier', value: 'SUPPLIER' },
];
}, []);
const sortByOptions = useMemo(() => {
return [
{ label: 'Tanggal Pembayaran', value: 'payment_date' },
{ label: 'Tanggal Dibuat', value: 'created_at' },
];
}, []);
const { options: bankOptions, rawData: bankRawData } = useSelect<Bank>(
BankApi.basePath,
'id',
'alias',
'',
{
limit: 'limit',
}
);
// ===== Handler =====
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
setPendingFilters((prev) => ({ ...prev, search: e.target.value }));
};
const transactionTypeChangeHandler = (
val: OptionType | OptionType[] | null
) => {
setSelectedTransactionType(val as OptionType);
setPendingFilters((prev) => ({
...prev,
transactionType: val ? ((val as OptionType).value as string) : '',
}));
};
const bankChangeHandler = (val: OptionType | OptionType[] | null) => {
setSelectedBank(val as OptionType);
setPendingFilters((prev) => ({
...prev,
bankId: val ? ((val as OptionType).value as string) : '',
}));
};
const partyTypeChangeHandler = (val: OptionType | OptionType[] | null) => {
setSelectedPartyType(val as OptionType);
setPendingFilters((prev) => ({
...prev,
partyType: val ? ((val as OptionType).value as string) : '',
}));
};
const sortByChangeHandler = (val: OptionType | OptionType[] | null) => {
setSelectedSortBy(val as OptionType);
setPendingFilters((prev) => ({
...prev,
sortBy: val ? ((val as OptionType).value as string) : '',
}));
};
const startDateChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
setPendingFilters((prev) => ({ ...prev, startDate: e.target.value }));
};
const endDateChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
setPendingFilters((prev) => ({ ...prev, endDate: e.target.value }));
};
const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => {
const newVal = val as OptionType;
setPageSize(newVal.value as number);
};
const submitFilterHandler = () => {
updateFilter('search', pendingFilters.search);
updateFilter('transactionType', pendingFilters.transactionType);
updateFilter('bankId', pendingFilters.bankId);
updateFilter('partyType', pendingFilters.partyType);
updateFilter('sortBy', pendingFilters.sortBy);
updateFilter('startDate', pendingFilters.startDate);
updateFilter('endDate', pendingFilters.endDate);
};
const resetFilterHandler = () => {
setSelectedTransactionType(null);
setSelectedBank(null);
setSelectedPartyType(null);
setSelectedSortBy(null);
const emptyFilters = {
search: '',
transactionType: '',
bankId: '',
partyType: '',
sortBy: '',
startDate: '',
endDate: '',
};
setPendingFilters(emptyFilters);
updateFilter('search', '');
updateFilter('transactionType', '');
updateFilter('bankId', '');
updateFilter('partyType', '');
updateFilter('sortBy', '');
updateFilter('startDate', '');
updateFilter('endDate', '');
};
const FinanceTable = ({ finances }: { finances: Finance[] }) => {
const columns = useMemo(() => { const columns = useMemo(() => {
return [ return [
{ {
header: 'ID', header: 'ID',
accessorKey: 'id', accessorKey: 'payment_code',
},
{
header: 'Alokasi',
accessorFn: (finance: Finance) => finance.references.length,
cell: ({ row }: { row: Row<Finance> }) => (
<Tooltip
content={row.original.references
.map((ref) => ref.references_number)
.join(', ')}
>
<span className='text-primary'>
{row.original.references.length}
</span>
</Tooltip>
),
}, },
{ {
header: 'References Number', header: 'References Number',
accessorKey: 'references_number', accessorKey: 'reference_number',
}, },
{ {
header: 'Jenis Transaksi', header: 'Jenis Transaksi',
@@ -41,12 +210,12 @@ const FinanceTable = ({ finances }: { finances: Finance[] }) => {
}, },
{ {
header: 'Pihak', header: 'Pihak',
accessorFn: (finance: Finance) => finance.transaction_owner.name, accessorFn: (finance: Finance) => finance.party.name,
}, },
{ {
header: 'Tanggal', header: 'Tanggal',
accessorFn: (finance: Finance) => accessorFn: (finance: Finance) =>
formatDate(finance.transaction_date, 'DD MMM YYYY'), formatDate(finance.payment_date, 'DD MMM YYYY'),
}, },
{ {
header: 'Metode Pembayaran', header: 'Metode Pembayaran',
@@ -55,17 +224,16 @@ const FinanceTable = ({ finances }: { finances: Finance[] }) => {
{ {
header: 'Bank', header: 'Bank',
accessorFn: (finance: Finance) => accessorFn: (finance: Finance) =>
`${finance.bank_account.alias} - ${finance.bank_account.account_number} - ${finance.bank_account.owner}`, `${finance.bank.alias} - ${finance.bank.account_number} - ${finance.bank.owner}`,
}, },
{ {
header: 'Pengeluaran (Rp)', header: 'Pengeluaran (Rp)',
accessorFn: (finance: Finance) => accessorFn: (finance: Finance) =>
formatCurrency(finance.balance_amount), formatCurrency(finance.expense_amount),
}, },
{ {
header: 'Pemasukan (Rp)', header: 'Pemasukan (Rp)',
accessorFn: (finance: Finance) => accessorFn: (finance: Finance) => formatCurrency(finance.income_amount),
formatCurrency(finance.transaction_amount),
}, },
{ {
header: 'Aksi', header: 'Aksi',
@@ -88,8 +256,113 @@ const FinanceTable = ({ finances }: { finances: Finance[] }) => {
]; ];
}, []); }, []);
return ( return (
<section className='size-full p-4'> <section className='size-full p-6 flex flex-col gap-6'>
<Table<Finance> data={finances} columns={columns} /> <div className='flex justify-end gap-2'>
<Button color='warning' className='min-w-24'>
Injection Saldo Bank
</Button>
<Button color='info' className='text-white min-w-24'>
Saldo Awal
</Button>
<Button color='primary' className='min-w-24'>
Tambah
</Button>
</div>
<Card
variant='bordered'
className={{
wrapper: 'w-full',
}}
footer={
<div className='flex justify-end gap-2'>
<Button
color='warning'
className='min-w-24'
onClick={resetFilterHandler}
>
Reset
</Button>
<Button
color='primary'
className='min-w-24'
onClick={submitFilterHandler}
>
Cari
</Button>
</div>
}
>
<div className='grid grid-cols-4 gap-6'>
<SelectInput
options={transactionTypeOptions}
label='Jenis Transaksi'
value={selectedTransactionType}
onChange={transactionTypeChangeHandler}
isClearable
/>
{isResponseSuccess(bankRawData) && (
<SelectInput
options={bankOptions.map((bank) => ({
label:
bankRawData.data.find((data) => data.id === bank.value)
?.alias +
' - ' +
bankRawData.data.find((data) => data.id === bank.value)
?.account_number +
' - ' +
bankRawData.data.find((data) => data.id === bank.value)
?.owner,
value: bank.value,
}))}
label='Bank'
value={selectedBank}
onChange={bankChangeHandler}
isClearable
/>
)}
<SelectInput
options={partyTypeOptions}
label='Pihak'
value={selectedPartyType}
onChange={partyTypeChangeHandler}
isClearable
/>
<DebouncedTextInput
name='search'
label='Cari'
placeholder='Cari'
value={pendingFilters.search}
onChange={searchChangeHandler}
/>
<SelectInput
options={sortByOptions}
label='Urutkan Berdasarkan'
value={selectedSortBy}
onChange={sortByChangeHandler}
isClearable
/>
<DateInput
name='startDate'
label='Periode Tanggal (Mulai)'
value={pendingFilters.startDate}
onChange={startDateChangeHandler}
/>
<DateInput
name='endDate'
label='Periode Tanggal (Akhir)'
value={pendingFilters.endDate}
onChange={endDateChangeHandler}
/>
</div>
</Card>
<Table<Finance>
data={isResponseSuccess(finances) ? finances.data : []}
columns={columns}
pageSize={tableFilterState.pageSize}
page={tableFilterState.page}
onPageChange={setPage}
isLoading={isLoading}
/>
</section> </section>
); );
}; };
File diff suppressed because it is too large Load Diff
+7 -16
View File
@@ -1,16 +1,11 @@
/** /**
* Dummy data for Finance[] * Dummy data for Finance[]
* Generated from: finance.json * Generated from: finance_payments.json
* *
* This file is auto-generated. Do not edit manually. * This file is auto-generated. Do not edit manually.
*/ */
import { import { FinanceBank, Finance } from '../../types/api/finance/finance';
FinanceBankAccount,
FinanceTransactionOwner,
FinanceReferences,
Finance,
} from '../../types/api/finance/finance';
import { BaseApiResponse } from '@/types/api/api-general'; import { BaseApiResponse } from '@/types/api/api-general';
import dummyData from './finance.dummy.json'; import dummyData from './finance.dummy.json';
@@ -18,9 +13,7 @@ import dummyData from './finance.dummy.json';
* Get dummy Finance[] data * Get dummy Finance[] data
* @returns Promise with BaseApiResponse containing Finance[] * @returns Promise with BaseApiResponse containing Finance[]
*/ */
export async function getAllDummyFinance(): Promise< export async function getAllFetcher(): Promise<BaseApiResponse<Finance[]>> {
BaseApiResponse<Finance[]>
> {
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
resolve({ resolve({
@@ -33,19 +26,17 @@ export async function getAllDummyFinance(): Promise<
}); });
} }
export async function getSingleDummyFinance( export async function getFetcher(
id: string id: number
): Promise<BaseApiResponse<Finance>> { ): Promise<BaseApiResponse<Finance>> {
console.log(dummyData as unknown as Finance[]);
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
const data = dummyData.find((item) => item.id === id);
resolve({ resolve({
code: 200, code: 200,
status: 'success', status: 'success',
message: 'Data retrieved successfully', message: 'Data retrieved successfully',
data: (dummyData as unknown as Finance[]).find( data: data as unknown as Finance,
(finance) => finance.id === id
) as Finance,
}); });
}, 500); }, 500);
}); });
+4 -8
View File
@@ -4,10 +4,7 @@ import { BaseApiResponse } from '@/types/api/api-general';
import { httpClient } from '@/services/http/client'; import { httpClient } from '@/services/http/client';
import { Finance } from '@/types/api/finance/finance'; import { Finance } from '@/types/api/finance/finance';
// DUMMY_START // DUMMY_START
import { import { getAllFetcher, getFetcher } from '@/dummy/finance/finance.dummy';
getAllDummyFinance,
getSingleDummyFinance,
} from '@/dummy/finance/finance.dummy';
// DUMMY_END // DUMMY_END
export class FinanceApiService extends BaseApiService< export class FinanceApiService extends BaseApiService<
@@ -21,7 +18,7 @@ export class FinanceApiService extends BaseApiService<
async getAllFetcher(): Promise<BaseApiResponse<Finance[]>> { async getAllFetcher(): Promise<BaseApiResponse<Finance[]>> {
// DUMMY_START // DUMMY_START
return await getAllDummyFinance(); return await getAllFetcher();
// DUMMY_END // DUMMY_END
// LIVE_START // LIVE_START
@@ -37,10 +34,9 @@ export class FinanceApiService extends BaseApiService<
// LIVE_END // LIVE_END
} }
async getSingleFetcher(id: string): Promise<BaseApiResponse<Finance>> { async getSingle(id: number): Promise<BaseApiResponse<Finance>> {
// DUMMY_START // DUMMY_START
console.log(id); return await getFetcher(id);
return await getSingleDummyFinance(id);
// DUMMY_END // DUMMY_END
// LIVE_START // LIVE_START
+19 -21
View File
@@ -1,31 +1,29 @@
export interface Finance { export interface Finance {
id: string; id: number;
references_number: string; payment_code: string;
bank_account: FinanceBankAccount; reference_number: string;
transaction_type: string; transaction_type: string;
transaction_owner: FinanceTransactionOwner; party: FinanceParty;
transaction_account_number: string; payment_date: string;
transaction_date: string; created_at: string;
payment_method: string; payment_method: string;
transaction_amount: number; bank: FinanceBank;
balance_amount: number; expense_amount: number;
income_amount: number;
nominal: number;
notes: string; notes: string;
references: FinanceReferences[];
} }
export interface FinanceReferences { export interface FinanceParty {
references_number: string;
total_allocation: number;
}
export interface FinanceTransactionOwner {
id: number; id: number;
name: string; name: string;
} type: string;
account_number: string;
export interface FinanceBankAccount { }
alias: string; export interface FinanceBank {
name: string; id: number;
name: string;
alias: string;
owner: string;
account_number: string; account_number: string;
owner: string;
} }