feat(FE-337): init slicing UI and define data types

This commit is contained in:
randy-ar
2025-12-23 17:38:16 +07:00
parent a7267370a0
commit 36ff6d04ee
19 changed files with 3194 additions and 1 deletions
@@ -0,0 +1,5 @@
const FinanceAddInitialBalance = () => {
return <div>Initial Balance</div>;
};
export default FinanceAddInitialBalance;
+5
View File
@@ -0,0 +1,5 @@
const FinanceAdd = () => {
return <div>Finance Add</div>;
};
export default FinanceAdd;
+5
View File
@@ -0,0 +1,5 @@
const FinanceAdjust = () => {
return <div>Finance Adjust</div>;
};
export default FinanceAdjust;
+11
View File
@@ -0,0 +1,11 @@
import SuspenseHelper from '@/components/helper/SuspenseHelper';
const Layout = ({
children,
}: Readonly<{
children: React.ReactNode;
}>) => {
return <SuspenseHelper>{children}</SuspenseHelper>;
};
export default Layout;
+41
View File
@@ -0,0 +1,41 @@
'use client';
import FinanceDetail from '@/components/pages/finance/FinanceDetail';
import useSWR from 'swr';
import { useRouter, useSearchParams } from 'next/navigation';
import { FinanceApi } from '@/services/api/finance';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
const FinanceDetailPage = () => {
const router = useRouter();
const financeId = useSearchParams().get('financeId');
const { data: finance } = useSWR(financeId, () =>
FinanceApi.getSingleFetcher(financeId as string)
);
if (!financeId) {
router.back();
return (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
);
}
console.log(finance);
// if (!finance || isResponseError(finance)) {
// router.replace('/404');
// return;
// }
return (
<>
{isResponseSuccess(finance) && <FinanceDetail finance={finance.data} />}
</>
);
};
export default FinanceDetailPage;
+32
View File
@@ -0,0 +1,32 @@
'use client';
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 { 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 (
<section className='size-full p-4'>
<h1>Finance</h1>
<FinanceTable
finances={isResponseSuccess(finances) ? finances.data : []}
/>
</section>
);
};
export default Finance;
@@ -0,0 +1,143 @@
import Card from '@/components/Card';
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
import Table from '@/components/Table';
import { formatCurrency } from '@/lib/helper';
import { Finance, FinanceReferences } from '@/types/api/finance/finance';
const FinanceDetail = ({ finance }: { finance: Finance }) => {
const informasiUmum = [
{
label: 'ID',
value: finance.id,
},
{
label: 'Jenis Transaksi',
value: finance.transaction_type,
},
{
label: 'Pihak',
value: finance.transaction_owner.name,
},
{
label: 'Tanggal',
value: finance.transaction_date,
},
{
label: 'Metode Pembayaran',
value: finance.payment_method,
},
];
const informasiTransfer = [
{
label: 'No. Referensi',
value: finance.references_number,
},
{
label: 'Nomor Rekening',
value: `${finance.bank_account.alias} - ${finance.bank_account.account_number} - ${finance.bank_account.owner}`,
},
{
label: 'Rekening Customer',
value: finance.transaction_account_number,
},
{
label: 'Nominal',
value: formatCurrency(finance.transaction_amount),
},
{
label: 'Sisa',
value: formatCurrency(finance.balance_amount),
},
];
return (
<div className='flex flex-col gap-4'>
<Card
title='Detail Keuangan'
className={{
wrapper: 'w-full',
}}
variant='bordered'
>
<div className='grid grid-cols-2 gap-4 mb-6'>
<Table
data={informasiUmum}
columns={[
{
header: '',
id: 'label',
accessorKey: 'label',
},
{
header: '',
id: 'value',
accessorKey: 'value',
},
]}
className={{
headerRowClassName: 'hidden',
paginationClassName: 'hidden',
containerClassName: 'mb-0',
}}
/>
<Table
data={informasiTransfer}
columns={[
{
header: '',
id: 'label',
accessorKey: 'label',
},
{
header: '',
id: 'value',
accessorKey: 'value',
},
]}
className={{
headerRowClassName: 'hidden',
paginationClassName: 'hidden',
containerClassName: 'mb-0',
}}
/>
</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>
</div>
);
};
export default FinanceDetail;
@@ -0,0 +1,97 @@
import Button from '@/components/Button';
import Dropdown from '@/components/dropdown/Dropdown';
import Menu from '@/components/menu/Menu';
import MenuItem from '@/components/menu/MenuItem';
import Table from '@/components/Table';
import Tooltip from '@/components/Tooltip';
import { formatCurrency, formatDate } from '@/lib/helper';
import { Finance } from '@/types/api/finance/finance';
import { Row } from '@tanstack/react-table';
import { useMemo } from 'react';
const FinanceTable = ({ finances }: { finances: Finance[] }) => {
const columns = useMemo(() => {
return [
{
header: 'ID',
accessorKey: 'id',
},
{
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',
accessorKey: 'references_number',
},
{
header: 'Jenis Transaksi',
accessorKey: 'transaction_type',
},
{
header: 'Pihak',
accessorFn: (finance: Finance) => finance.transaction_owner.name,
},
{
header: 'Tanggal',
accessorFn: (finance: Finance) =>
formatDate(finance.transaction_date, 'DD MMM YYYY'),
},
{
header: 'Metode Pembayaran',
accessorKey: 'payment_method',
},
{
header: 'Bank',
accessorFn: (finance: Finance) =>
`${finance.bank_account.alias} - ${finance.bank_account.account_number} - ${finance.bank_account.owner}`,
},
{
header: 'Pengeluaran (Rp)',
accessorFn: (finance: Finance) =>
formatCurrency(finance.balance_amount),
},
{
header: 'Pemasukan (Rp)',
accessorFn: (finance: Finance) =>
formatCurrency(finance.transaction_amount),
},
{
header: 'Aksi',
cell: ({ row }: { row: Row<Finance> }) => (
<Dropdown
trigger={<Button variant='ghost'>...</Button>}
direction='bottom'
align='end'
>
<Menu>
<MenuItem
title='Detail'
href={`/finance/detail?financeId=${row.original.id}`}
className='hover:bg-primary hover:text-primary-content'
/>
</Menu>
</Dropdown>
),
},
];
}, []);
return (
<section className='size-full p-4'>
<Table<Finance> data={finances} columns={columns} />
</section>
);
};
export default FinanceTable;
@@ -0,0 +1,5 @@
const FinanceAdjust = () => {
return <div>Finance Adjust</div>;
};
export default FinanceAdjust;
+5 -1
View File
@@ -35,6 +35,11 @@ export const MAIN_DRAWER_LINKS: SidebarMenuItem[] = [
link: '/marketing',
icon: 'heroicons-outline:currency-dollar',
},
{
text: 'Keuangan',
link: '/finance',
icon: 'heroicons-outline:banknotes',
},
{
text: 'Biaya Operasional',
link: '/expense',
@@ -60,7 +65,6 @@ export const MAIN_DRAWER_LINKS: SidebarMenuItem[] = [
},
],
},
{
text: 'Persediaan',
link: '/inventory',
File diff suppressed because it is too large Load Diff
+52
View File
@@ -0,0 +1,52 @@
/**
* Dummy data for Finance[]
* Generated from: finance.json
*
* This file is auto-generated. Do not edit manually.
*/
import {
FinanceBankAccount,
FinanceTransactionOwner,
FinanceReferences,
Finance,
} from '../../types/api/finance/finance';
import { BaseApiResponse } from '@/types/api/api-general';
import dummyData from './finance.dummy.json';
/**
* Get dummy Finance[] data
* @returns Promise with BaseApiResponse containing Finance[]
*/
export async function getAllDummyFinance(): Promise<
BaseApiResponse<Finance[]>
> {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
code: 200,
status: 'success',
message: 'Data retrieved successfully',
data: dummyData as unknown as Finance[],
});
}, 500);
});
}
export async function getSingleDummyFinance(
id: string
): Promise<BaseApiResponse<Finance>> {
console.log(dummyData as unknown as Finance[]);
return new Promise((resolve) => {
setTimeout(() => {
resolve({
code: 200,
status: 'success',
message: 'Data retrieved successfully',
data: (dummyData as unknown as Finance[]).find(
(finance) => finance.id === id
) as Finance,
});
}, 500);
});
}
+60
View File
@@ -0,0 +1,60 @@
import axios from 'axios';
import { BaseApiService } from '@/services/api/base';
import { BaseApiResponse } from '@/types/api/api-general';
import { httpClient } from '@/services/http/client';
import { Finance } from '@/types/api/finance/finance';
// DUMMY_START
import {
getAllDummyFinance,
getSingleDummyFinance,
} from '@/dummy/finance/finance.dummy';
// DUMMY_END
export class FinanceApiService extends BaseApiService<
Finance,
unknown,
unknown
> {
constructor(basePath: string) {
super(basePath);
}
async getAllFetcher(): Promise<BaseApiResponse<Finance[]>> {
// DUMMY_START
return await getAllDummyFinance();
// DUMMY_END
// LIVE_START
// try {
// const path = `${this.basePath}/`;
// return await httpClient<BaseApiResponse<Finance[]>>(path);
// } catch (error) {
// if (axios.isAxiosError<BaseApiResponse<Finance[]>>(error)) {
// return error.response?.data;
// }
// return undefined;
// }
// LIVE_END
}
async getSingleFetcher(id: string): Promise<BaseApiResponse<Finance>> {
// DUMMY_START
console.log(id);
return await getSingleDummyFinance(id);
// DUMMY_END
// LIVE_START
// try {
// const path = `${this.basePath}/`;
// return await httpClient<BaseApiResponse<Finance[]>>(path);
// } catch (error) {
// if (axios.isAxiosError<BaseApiResponse<Finance[]>>(error)) {
// return error.response?.data;
// }
// return undefined;
// }
// LIVE_END
}
}
export const FinanceApi = new FinanceApiService('/finances');
+31
View File
@@ -0,0 +1,31 @@
export interface Finance {
id: string;
references_number: string;
bank_account: FinanceBankAccount;
transaction_type: string;
transaction_owner: FinanceTransactionOwner;
transaction_account_number: string;
transaction_date: string;
payment_method: string;
transaction_amount: number;
balance_amount: number;
notes: string;
references: FinanceReferences[];
}
export interface FinanceReferences {
references_number: string;
total_allocation: number;
}
export interface FinanceTransactionOwner {
id: number;
name: string;
}
export interface FinanceBankAccount {
alias: string;
name: string;
account_number: string;
owner: string;
}