mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 07:45:47 +00:00
feat(FE-333): adding feature overhead closing report
This commit is contained in:
@@ -6,9 +6,147 @@ 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';
|
|
||||||
|
// 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;
|
||||||
@@ -18,20 +156,17 @@ const RequireAuth = ({ children }: RequireAuthProps) => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { setUser, setIsLoadingUser } = useAuth();
|
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,
|
||||||
} = useSWRImmutable<
|
{
|
||||||
GetMeResponse & { ok?: boolean },
|
|
||||||
AxiosError<BaseApiResponse>,
|
|
||||||
SWRHttpKey
|
|
||||||
>('/sso/userinfo', httpClientFetcher, {
|
|
||||||
shouldRetryOnError: false,
|
shouldRetryOnError: false,
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
revalidateOnReconnect: false,
|
revalidateOnReconnect: false,
|
||||||
refreshInterval: 0,
|
refreshInterval: 0,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoadingUser(isLoadingUserResponse);
|
setIsLoadingUser(isLoadingUserResponse);
|
||||||
@@ -40,25 +175,23 @@ const RequireAuth = ({ children }: RequireAuthProps) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isResponseSuccess(userResponse)) {
|
if (isResponseSuccess(userResponse)) {
|
||||||
setUser(userResponse.data);
|
setUser(userResponse.data);
|
||||||
} else if (
|
} else {
|
||||||
isResponseError(userErrorResponse?.response?.data) &&
|
// router.replace(process.env.NEXT_PUBLIC_SSO_LOGIN_URL as string);
|
||||||
typeof window !== 'undefined'
|
// TODO: remove this later, DONT HARDCODE USER DATA
|
||||||
) {
|
setUser(DUMMY_USER);
|
||||||
router.replace(
|
|
||||||
`${process.env.NEXT_PUBLIC_SSO_LOGIN_URL as string}?redirect_url=${window.location.href}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}, [userResponse, userErrorResponse, setIsLoadingUser, setUser]);
|
}, [userResponse, setIsLoadingUser, setUser]);
|
||||||
|
|
||||||
if (isLoadingUserResponse && !userResponse && !userErrorResponse) {
|
// TODO: uncomment this later
|
||||||
return (
|
// if (isLoadingUserResponse && !userResponse) {
|
||||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
// return (
|
||||||
<span className='loading loading-spinner loading-xl' />
|
// <div className='w-full flex flex-row justify-center items-center p-4'>
|
||||||
</div>
|
// <span className='loading loading-spinner loading-xl' />
|
||||||
);
|
// </div>
|
||||||
}
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
return <>{isResponseSuccess(userResponse) && children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RequireAuth;
|
export default RequireAuth;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import ClosingGeneralInformationTable from '@/components/pages/closing/ClosingGe
|
|||||||
import { ClosingGeneralInformation } from '@/types/api/closing';
|
import { ClosingGeneralInformation } from '@/types/api/closing';
|
||||||
import ClosingSapronakTabContent from './ClosingSapronakTabContent';
|
import ClosingSapronakTabContent from './ClosingSapronakTabContent';
|
||||||
import ClosingSapronakCalculationTabContent from '@/components/pages/closing/ClosingSapronakCalculationTabContent';
|
import ClosingSapronakCalculationTabContent from '@/components/pages/closing/ClosingSapronakCalculationTabContent';
|
||||||
|
import ClosingOverheadTabContent from '@/components/pages/closing/ClosingOverheadTabContent';
|
||||||
|
|
||||||
interface ClosingDetailProps {
|
interface ClosingDetailProps {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -39,7 +40,7 @@ const ClosingDetail: React.FC<ClosingDetailProps> = ({ id, initialValue }) => {
|
|||||||
{
|
{
|
||||||
id: 'overhead',
|
id: 'overhead',
|
||||||
label: 'Overhead',
|
label: 'Overhead',
|
||||||
content: 'Overhead',
|
content: <ClosingOverheadTabContent projectFlockId={id} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'hppEkspedisi',
|
id: 'hppEkspedisi',
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import ClosingOverheadTable from '@/components/pages/closing/ClosingOverheadTable';
|
||||||
|
|
||||||
|
interface ClosingOverheadTabContentProps {
|
||||||
|
projectFlockId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ClosingOverheadTabContent = ({
|
||||||
|
projectFlockId,
|
||||||
|
}: ClosingOverheadTabContentProps) => {
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col gap-4'>
|
||||||
|
{projectFlockId && (
|
||||||
|
<ClosingOverheadTable projectFlockId={projectFlockId} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClosingOverheadTabContent;
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
import Card from '@/components/Card';
|
||||||
|
import Table, { TABLE_DEFAULT_STYLING } from '@/components/Table';
|
||||||
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
|
import { cn, formatCurrency, formatDate, formatNumber } from '@/lib/helper';
|
||||||
|
import { ClosingApi } from '@/services/api/closing';
|
||||||
|
import { Overhead, OverheadTotal } from '@/types/api/closing';
|
||||||
|
import { ColumnDef } from '@tanstack/react-table';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import useSWR from 'swr';
|
||||||
|
|
||||||
|
interface ClosingOverheadTableProps {
|
||||||
|
type?: 'detail';
|
||||||
|
projectFlockId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ClosingOverheadTable = ({
|
||||||
|
type,
|
||||||
|
projectFlockId,
|
||||||
|
}: ClosingOverheadTableProps) => {
|
||||||
|
const { data: overhead, isLoading: isLoadingOverhead } = useSWR(
|
||||||
|
`${ClosingApi.basePath}/${projectFlockId}/overhead`,
|
||||||
|
() => ClosingApi.getOverhead(projectFlockId)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Helper function to create columns with footer support
|
||||||
|
const createColumns = (total?: OverheadTotal): ColumnDef<Overhead>[] => [
|
||||||
|
// Group untuk kolom tanpa footer
|
||||||
|
{
|
||||||
|
header: 'Nama Item',
|
||||||
|
accessorFn: (props) => props.item_name,
|
||||||
|
footer: 'Total Pengeluaran Overhead',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Satuan',
|
||||||
|
accessorFn: (props) => props.uom_name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Budget Pengajuan',
|
||||||
|
footer: '',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
id: 'budget_quantity',
|
||||||
|
header: 'Jumlah',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.budget_quantity ? formatNumber(props.budget_quantity) : '-',
|
||||||
|
footer: total ? () => formatNumber(total.budget_quantity) : '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'budget_unit_price',
|
||||||
|
header: 'Harga Satuan',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.budget_unit_price
|
||||||
|
? formatCurrency(props.budget_unit_price)
|
||||||
|
: '-',
|
||||||
|
footer: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'budget_total_amount',
|
||||||
|
header: 'Total',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.budget_total_amount
|
||||||
|
? formatCurrency(props.budget_total_amount)
|
||||||
|
: '-',
|
||||||
|
footer: total ? () => formatCurrency(total.budget_total_amount) : '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Realisai',
|
||||||
|
footer: '',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
id: 'actual_date',
|
||||||
|
header: 'Tanggal',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.actual_date
|
||||||
|
? formatDate(props.actual_date, 'DD MMM, YYYY')
|
||||||
|
: '-',
|
||||||
|
footer: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actual_quantity',
|
||||||
|
header: 'Jumlah',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.actual_quantity ? formatNumber(props.actual_quantity) : '-',
|
||||||
|
footer: total ? () => formatNumber(total.actual_quantity) : '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actual_unit_price',
|
||||||
|
header: 'Harga Satuan',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.actual_unit_price
|
||||||
|
? formatCurrency(props.actual_unit_price)
|
||||||
|
: '-',
|
||||||
|
footer: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actual_total_amount',
|
||||||
|
header: 'Total',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.actual_total_amount
|
||||||
|
? formatCurrency(props.actual_total_amount)
|
||||||
|
: '-',
|
||||||
|
footer: total ? () => formatCurrency(total.actual_total_amount) : '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'cost_per_bird',
|
||||||
|
header: 'Rp/Ekor',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.cost_per_bird ? formatCurrency(props.cost_per_bird) : '-',
|
||||||
|
footer: total ? () => formatCurrency(total.cost_per_bird) : '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() =>
|
||||||
|
isResponseSuccess(overhead)
|
||||||
|
? createColumns(overhead.data?.total)
|
||||||
|
: createColumns(),
|
||||||
|
[overhead]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Card
|
||||||
|
title='Pengeluaran Overhead'
|
||||||
|
collapsible
|
||||||
|
defaultCollapsed={false}
|
||||||
|
className={{
|
||||||
|
wrapper: 'w-full',
|
||||||
|
body: 'p-4 shadow',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Table<Overhead>
|
||||||
|
data={
|
||||||
|
isResponseSuccess(overhead) ? (overhead.data?.overheads ?? []) : []
|
||||||
|
}
|
||||||
|
columns={columns}
|
||||||
|
className={{
|
||||||
|
containerClassName: 'my-4',
|
||||||
|
headerColumnClassName: cn(
|
||||||
|
TABLE_DEFAULT_STYLING.headerColumnClassName,
|
||||||
|
'whitespace-nowrap'
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
renderFooter={
|
||||||
|
isResponseSuccess(overhead)
|
||||||
|
? overhead.data?.overheads.length > 0
|
||||||
|
: false
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClosingOverheadTable;
|
||||||
@@ -82,6 +82,7 @@ import {
|
|||||||
ClosingGeneralInformation,
|
ClosingGeneralInformation,
|
||||||
ClosingIncomingSapronak,
|
ClosingIncomingSapronak,
|
||||||
ClosingOutgoingSapronak,
|
ClosingOutgoingSapronak,
|
||||||
|
ClosingOverhead,
|
||||||
ClosingSapronakCalculation,
|
ClosingSapronakCalculation,
|
||||||
} from '@/types/api/closing';
|
} from '@/types/api/closing';
|
||||||
import { CreatedUser, BaseApiResponse } from '@/types/api/api-general';
|
import { CreatedUser, BaseApiResponse } from '@/types/api/api-general';
|
||||||
@@ -846,6 +847,141 @@ export const dummySapronakCalculation: ClosingSapronakCalculation = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ======================
|
||||||
|
// 💰 Overhead Dummy Data
|
||||||
|
// ======================
|
||||||
|
export const dummyOverhead: ClosingOverhead = {
|
||||||
|
overheads: [
|
||||||
|
{
|
||||||
|
item_name: 'Expedisi DOC',
|
||||||
|
uom_name: 'Ekor',
|
||||||
|
budget_quantity: 500,
|
||||||
|
budget_unit_price: 8000,
|
||||||
|
budget_total_amount: 4000000,
|
||||||
|
actual_date: '',
|
||||||
|
actual_quantity: 0,
|
||||||
|
actual_unit_price: 0,
|
||||||
|
actual_total_amount: 0,
|
||||||
|
cost_per_bird: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item_name: 'Solar',
|
||||||
|
uom_name: 'Liter',
|
||||||
|
budget_quantity: 0,
|
||||||
|
budget_unit_price: 0,
|
||||||
|
budget_total_amount: 0,
|
||||||
|
actual_date: today,
|
||||||
|
actual_quantity: 20,
|
||||||
|
actual_unit_price: 10000,
|
||||||
|
actual_total_amount: 200000,
|
||||||
|
cost_per_bird: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item_name: 'Gaji Karyawan Kandang',
|
||||||
|
uom_name: 'Orang',
|
||||||
|
budget_quantity: 3,
|
||||||
|
budget_unit_price: 3000000,
|
||||||
|
budget_total_amount: 9000000,
|
||||||
|
actual_date: today,
|
||||||
|
actual_quantity: 3,
|
||||||
|
actual_unit_price: 3200000,
|
||||||
|
actual_total_amount: 9600000,
|
||||||
|
cost_per_bird: 640,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item_name: 'Listrik Kandang',
|
||||||
|
uom_name: 'Bulan',
|
||||||
|
budget_quantity: 1,
|
||||||
|
budget_unit_price: 2500000,
|
||||||
|
budget_total_amount: 2500000,
|
||||||
|
actual_date: today,
|
||||||
|
actual_quantity: 1,
|
||||||
|
actual_unit_price: 2800000,
|
||||||
|
actual_total_amount: 2800000,
|
||||||
|
cost_per_bird: 187,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item_name: 'Air Bersih',
|
||||||
|
uom_name: 'Bulan',
|
||||||
|
budget_quantity: 1,
|
||||||
|
budget_unit_price: 500000,
|
||||||
|
budget_total_amount: 500000,
|
||||||
|
actual_date: today,
|
||||||
|
actual_quantity: 1,
|
||||||
|
actual_unit_price: 450000,
|
||||||
|
actual_total_amount: 450000,
|
||||||
|
cost_per_bird: 30,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item_name: 'Perbaikan Kandang',
|
||||||
|
uom_name: 'Paket',
|
||||||
|
budget_quantity: 1,
|
||||||
|
budget_unit_price: 3000000,
|
||||||
|
budget_total_amount: 3000000,
|
||||||
|
actual_date: yesterday,
|
||||||
|
actual_quantity: 1,
|
||||||
|
actual_unit_price: 3500000,
|
||||||
|
actual_total_amount: 3500000,
|
||||||
|
cost_per_bird: 233,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item_name: 'Service Peralatan',
|
||||||
|
uom_name: 'Kali',
|
||||||
|
budget_quantity: 2,
|
||||||
|
budget_unit_price: 500000,
|
||||||
|
budget_total_amount: 1000000,
|
||||||
|
actual_date: lastWeek,
|
||||||
|
actual_quantity: 2,
|
||||||
|
actual_unit_price: 550000,
|
||||||
|
actual_total_amount: 1100000,
|
||||||
|
cost_per_bird: 73,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item_name: 'ATK & Supplies',
|
||||||
|
uom_name: 'Paket',
|
||||||
|
budget_quantity: 1,
|
||||||
|
budget_unit_price: 500000,
|
||||||
|
budget_total_amount: 500000,
|
||||||
|
actual_date: today,
|
||||||
|
actual_quantity: 1,
|
||||||
|
actual_unit_price: 450000,
|
||||||
|
actual_total_amount: 450000,
|
||||||
|
cost_per_bird: 30,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item_name: 'Biaya Komunikasi',
|
||||||
|
uom_name: 'Bulan',
|
||||||
|
budget_quantity: 1,
|
||||||
|
budget_unit_price: 300000,
|
||||||
|
budget_total_amount: 300000,
|
||||||
|
actual_date: today,
|
||||||
|
actual_quantity: 1,
|
||||||
|
actual_unit_price: 320000,
|
||||||
|
actual_total_amount: 320000,
|
||||||
|
cost_per_bird: 21,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item_name: 'BBM Kendaraan Operasional',
|
||||||
|
uom_name: 'Liter',
|
||||||
|
budget_quantity: 200,
|
||||||
|
budget_unit_price: 10000,
|
||||||
|
budget_total_amount: 2000000,
|
||||||
|
actual_date: today,
|
||||||
|
actual_quantity: 220,
|
||||||
|
actual_unit_price: 10500,
|
||||||
|
actual_total_amount: 2310000,
|
||||||
|
cost_per_bird: 154,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
total: {
|
||||||
|
budget_quantity: 710,
|
||||||
|
budget_total_amount: 23300000,
|
||||||
|
actual_quantity: 250,
|
||||||
|
actual_total_amount: 24530000,
|
||||||
|
cost_per_bird: 1568,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// ======================
|
// ======================
|
||||||
// 🔧 Dummy API Response Functions
|
// 🔧 Dummy API Response Functions
|
||||||
// ======================
|
// ======================
|
||||||
@@ -982,3 +1118,19 @@ export const dummyGetPerhitunganSapronak = async (
|
|||||||
data: dummySapronakCalculation,
|
data: dummySapronakCalculation,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy implementation for getOverhead
|
||||||
|
* Returns overhead data
|
||||||
|
*/
|
||||||
|
export const dummyGetOverhead = async (
|
||||||
|
id: number
|
||||||
|
): Promise<BaseApiResponse<ClosingOverhead> | undefined> => {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 400));
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
status: 'success',
|
||||||
|
message: 'Data overhead berhasil diambil',
|
||||||
|
data: dummyOverhead,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
ClosingGeneralInformation,
|
ClosingGeneralInformation,
|
||||||
ClosingIncomingSapronak,
|
ClosingIncomingSapronak,
|
||||||
ClosingOutgoingSapronak,
|
ClosingOutgoingSapronak,
|
||||||
|
ClosingOverhead,
|
||||||
ClosingSapronakCalculation,
|
ClosingSapronakCalculation,
|
||||||
} from '@/types/api/closing';
|
} from '@/types/api/closing';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
@@ -16,6 +17,7 @@ import {
|
|||||||
dummyGetAllOutgoingSapronakFetcher,
|
dummyGetAllOutgoingSapronakFetcher,
|
||||||
dummyGetGeneralInfo,
|
dummyGetGeneralInfo,
|
||||||
dummyGetPerhitunganSapronak,
|
dummyGetPerhitunganSapronak,
|
||||||
|
dummyGetOverhead,
|
||||||
} from '@/dummy/closing.dummy';
|
} from '@/dummy/closing.dummy';
|
||||||
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
||||||
|
|
||||||
@@ -73,12 +75,12 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
|||||||
endpoint: string
|
endpoint: string
|
||||||
): Promise<BaseApiResponse<ClosingOutgoingSapronak[]>> {
|
): Promise<BaseApiResponse<ClosingOutgoingSapronak[]>> {
|
||||||
// TODO: Remove this block when backend is ready
|
// TODO: Remove this block when backend is ready
|
||||||
// return await dummyGetAllOutgoingSapronakFetcher();
|
return await dummyGetAllOutgoingSapronakFetcher();
|
||||||
|
|
||||||
// Uncomment this when backend is ready
|
// Uncomment this when backend is ready
|
||||||
return await httpClientFetcher<BaseApiResponse<ClosingOutgoingSapronak[]>>(
|
// return await httpClientFetcher<BaseApiResponse<ClosingOutgoingSapronak[]>>(
|
||||||
endpoint
|
// endpoint
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGeneralInfo(
|
async getGeneralInfo(
|
||||||
@@ -147,6 +149,33 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getOverhead(
|
||||||
|
id: number
|
||||||
|
): Promise<BaseApiResponse<ClosingOverhead> | undefined> {
|
||||||
|
// TODO: Remove this block when backend is ready
|
||||||
|
// try {
|
||||||
|
// return await dummyGetOverhead(id);
|
||||||
|
// } catch (error) {
|
||||||
|
// if (axios.isAxiosError<BaseApiResponse<ClosingOverhead>>(error)) {
|
||||||
|
// return error.response?.data;
|
||||||
|
// }
|
||||||
|
// return undefined;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Uncomment this when backend is ready
|
||||||
|
try {
|
||||||
|
const path = `${this.basePath}/${id}/overhead`;
|
||||||
|
return await httpClient<BaseApiResponse<ClosingOverhead>>(path, {
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError<BaseApiResponse<ClosingOverhead>>(error)) {
|
||||||
|
return error.response?.data;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ClosingApi = new ClosingApiService('/closings');
|
export const ClosingApi = new ClosingApiService('/closings');
|
||||||
|
|||||||
Vendored
+27
@@ -89,3 +89,30 @@ export type ClosingSapronakCalculation = {
|
|||||||
ovk: ClosingSapronakCalculationItem;
|
ovk: ClosingSapronakCalculationItem;
|
||||||
pakan: ClosingSapronakCalculationItem;
|
pakan: ClosingSapronakCalculationItem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ====== OVERHEAD ======
|
||||||
|
export type ClosingOverhead = {
|
||||||
|
overheads: Overhead[];
|
||||||
|
total: OverheadTotal;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Overhead = {
|
||||||
|
item_name: string;
|
||||||
|
uom_name: string;
|
||||||
|
budget_quantity: number;
|
||||||
|
budget_unit_price: number;
|
||||||
|
budget_total_amount: number;
|
||||||
|
actual_date: string;
|
||||||
|
actual_quantity: number;
|
||||||
|
actual_unit_price: number;
|
||||||
|
actual_total_amount: number;
|
||||||
|
cost_per_bird: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OverheadTotal = {
|
||||||
|
budget_quantity: number;
|
||||||
|
budget_total_amount: number;
|
||||||
|
actual_quantity: number;
|
||||||
|
actual_total_amount: number;
|
||||||
|
cost_per_bird: number;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user