mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
feat(FE): Add skeleton components for closing pages
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
|
||||
import Table from '@/components/Table';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
const ClosingTabSkeleton = <T extends object>({
|
||||
columns,
|
||||
icon,
|
||||
title,
|
||||
subtitle,
|
||||
}: {
|
||||
columns: ColumnDef<T>[];
|
||||
icon: React.ReactNode;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
}) => {
|
||||
return (
|
||||
<div className='relative size-full'>
|
||||
<Table
|
||||
data={[]}
|
||||
columns={columns as ColumnDef<T>[]}
|
||||
isLoading={true}
|
||||
className={{
|
||||
skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4',
|
||||
headerColumnClassName: 'whitespace-nowrap',
|
||||
containerClassName: 'mb-0 overflow-hidden',
|
||||
tableWrapperClassName: 'overflow-hidden',
|
||||
}}
|
||||
/>
|
||||
<div className='absolute inset-0 flex items-center justify-center'>
|
||||
<DataStateSkeleton icon={icon} title={title} description={subtitle} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClosingTabSkeleton;
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Icon } from '@iconify/react';
|
||||
import Card from '@/components/Card';
|
||||
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
|
||||
|
||||
const FinanceClosingSkeleton = ({
|
||||
title = 'Data Keuangan Belum Tersedia',
|
||||
subtitle = 'Tidak ada data keuangan untuk periode ini.',
|
||||
iconName = 'heroicons:chart-bar',
|
||||
}: {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
iconName?: string;
|
||||
}) => {
|
||||
return (
|
||||
<Card
|
||||
variant='bordered'
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
body: 'p-8',
|
||||
}}
|
||||
>
|
||||
<div className='flex items-center justify-center p-8'>
|
||||
<DataStateSkeleton
|
||||
icon={
|
||||
<Icon
|
||||
icon={iconName}
|
||||
className='text-white'
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
}
|
||||
title={title}
|
||||
description={subtitle}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default FinanceClosingSkeleton;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Icon } from '@iconify/react';
|
||||
import ClosingTabSkeleton from './ClosingTabSkeleton';
|
||||
import { BaseExpeditionCost } from '@/types/api/closing';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
const HppExpeditionClosingSkeleton = ({
|
||||
columns,
|
||||
title = 'Data HPP Ekspedisi Belum Tersedia',
|
||||
subtitle = 'Tidak ada data HPP ekspedisi untuk periode ini.',
|
||||
iconName = 'heroicons:chart-bar',
|
||||
}: {
|
||||
columns?: ColumnDef<BaseExpeditionCost>[];
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
iconName?: string;
|
||||
}) => {
|
||||
const defaultColumns: ColumnDef<BaseExpeditionCost>[] = [
|
||||
{
|
||||
id: 'id',
|
||||
header: 'No',
|
||||
},
|
||||
{
|
||||
id: 'expedition_vendor_name',
|
||||
header: 'Nama Ekspedisi',
|
||||
},
|
||||
{
|
||||
id: 'hpp_amount',
|
||||
header: 'HPP Ekspedisi',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<ClosingTabSkeleton<BaseExpeditionCost>
|
||||
columns={columns || defaultColumns}
|
||||
icon={
|
||||
<Icon icon={iconName} className='text-white' width={20} height={20} />
|
||||
}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default HppExpeditionClosingSkeleton;
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Icon } from '@iconify/react';
|
||||
import ClosingTabSkeleton from './ClosingTabSkeleton';
|
||||
import { Overhead } from '@/types/api/closing';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
const OverheadClosingSkeleton = ({
|
||||
columns,
|
||||
title = 'Data Overhead Belum Tersedia',
|
||||
subtitle = 'Tidak ada data overhead untuk periode ini.',
|
||||
iconName = 'heroicons:chart-bar',
|
||||
}: {
|
||||
columns?: ColumnDef<Overhead>[];
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
iconName?: string;
|
||||
}) => {
|
||||
const defaultColumns: ColumnDef<Overhead>[] = [
|
||||
{
|
||||
id: 'name',
|
||||
header: 'Nama Overhead',
|
||||
},
|
||||
{
|
||||
id: 'budget_quantity',
|
||||
header: 'Budget Pengajuan - Jumlah',
|
||||
},
|
||||
{
|
||||
id: 'budget_unit_price',
|
||||
header: 'Budget Pengajuan - Harga Satuan',
|
||||
},
|
||||
{
|
||||
id: 'budget_total_amount',
|
||||
header: 'Budget Pengajuan - Total',
|
||||
},
|
||||
{
|
||||
id: 'actual_quantity',
|
||||
header: 'Realisasi - Jumlah',
|
||||
},
|
||||
{
|
||||
id: 'actual_unit_price',
|
||||
header: 'Realisasi - Harga Satuan',
|
||||
},
|
||||
{
|
||||
id: 'actual_total_amount',
|
||||
header: 'Realisasi - Total',
|
||||
},
|
||||
{
|
||||
id: 'difference_quantity',
|
||||
header: 'Selisih - Jumlah',
|
||||
},
|
||||
{
|
||||
id: 'difference_unit_price',
|
||||
header: 'Selisih - Harga Satuan',
|
||||
},
|
||||
{
|
||||
id: 'difference_total_amount',
|
||||
header: 'Selisih - Total',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<ClosingTabSkeleton<Overhead>
|
||||
columns={columns || defaultColumns}
|
||||
icon={
|
||||
<Icon icon={iconName} className='text-white' width={20} height={20} />
|
||||
}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default OverheadClosingSkeleton;
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Icon } from '@iconify/react';
|
||||
import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton';
|
||||
|
||||
const ProductionDataClosingSkeleton = ({
|
||||
title = 'Data Produksi Belum Tersedia',
|
||||
subtitle = 'Tidak ada data produksi untuk periode ini.',
|
||||
iconName = 'heroicons:chart-bar',
|
||||
}: {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
iconName?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div className='w-full rounded-xl p-8 shadow-sm'>
|
||||
<div className='flex items-center justify-center p-12'>
|
||||
<DataStateSkeleton
|
||||
icon={
|
||||
<Icon
|
||||
icon={iconName}
|
||||
className='text-white'
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
}
|
||||
title={title}
|
||||
description={subtitle}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductionDataClosingSkeleton;
|
||||
@@ -0,0 +1,84 @@
|
||||
import { Icon } from '@iconify/react';
|
||||
import ClosingTabSkeleton from './ClosingTabSkeleton';
|
||||
import { BaseSales } from '@/types/api/closing';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
const SalesClosingSkeleton = ({
|
||||
columns,
|
||||
title = 'Data Penjualan Belum Tersedia',
|
||||
subtitle = 'Tidak ada data penjualan untuk periode ini.',
|
||||
iconName = 'heroicons:chart-bar',
|
||||
}: {
|
||||
columns?: ColumnDef<BaseSales>[];
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
iconName?: string;
|
||||
}) => {
|
||||
const defaultColumns: ColumnDef<BaseSales>[] = [
|
||||
{
|
||||
id: 'realization_date',
|
||||
header: 'Tanggal Realisasi',
|
||||
},
|
||||
{
|
||||
id: 'age',
|
||||
header: 'Umur',
|
||||
},
|
||||
{
|
||||
id: 'do_number',
|
||||
header: 'No. DO',
|
||||
},
|
||||
{
|
||||
id: 'product',
|
||||
header: 'Produk',
|
||||
},
|
||||
{
|
||||
id: 'customer',
|
||||
header: 'Customer',
|
||||
},
|
||||
{
|
||||
id: 'qty',
|
||||
header: 'Kuantitas',
|
||||
},
|
||||
{
|
||||
id: 'weight',
|
||||
header: 'Kg',
|
||||
},
|
||||
{
|
||||
id: 'avg_weight',
|
||||
header: 'AVG (Kg)',
|
||||
},
|
||||
{
|
||||
id: 'sales_price',
|
||||
header: 'Harga Sales (Rp)',
|
||||
},
|
||||
{
|
||||
id: 'total_sales_price',
|
||||
header: 'Total Sales (Rp)',
|
||||
},
|
||||
{
|
||||
id: 'actual_price',
|
||||
header: 'Harga Act (Rp)',
|
||||
},
|
||||
{
|
||||
id: 'total_actual_price',
|
||||
header: 'Total Act (Rp)',
|
||||
},
|
||||
{
|
||||
id: 'kandang',
|
||||
header: 'Kandang',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<ClosingTabSkeleton<BaseSales>
|
||||
columns={columns || defaultColumns}
|
||||
icon={
|
||||
<Icon icon={iconName} className='text-white' width={20} height={20} />
|
||||
}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SalesClosingSkeleton;
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Icon } from '@iconify/react';
|
||||
import ClosingTabSkeleton from './ClosingTabSkeleton';
|
||||
import { RowSapronakCalculation } from '@/types/api/closing';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
const SapronakCalculationClosingSkeleton = ({
|
||||
columns,
|
||||
title = 'Data Perhitungan Sapronak Belum Tersedia',
|
||||
subtitle = 'Tidak ada data perhitungan sapronak untuk periode ini.',
|
||||
iconName = 'heroicons:chart-bar',
|
||||
}: {
|
||||
columns?: ColumnDef<RowSapronakCalculation>[];
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
iconName?: string;
|
||||
}) => {
|
||||
const defaultColumns: ColumnDef<RowSapronakCalculation>[] = [
|
||||
{
|
||||
id: 'date',
|
||||
header: 'Tanggal',
|
||||
},
|
||||
{
|
||||
id: 'reference_number',
|
||||
header: 'No. Referensi',
|
||||
},
|
||||
{
|
||||
id: 'qty_in',
|
||||
header: 'QTY Masuk',
|
||||
},
|
||||
{
|
||||
id: 'qty_out',
|
||||
header: 'QTY Keluar',
|
||||
},
|
||||
{
|
||||
id: 'qty_used',
|
||||
header: 'QTY Pakai',
|
||||
},
|
||||
{
|
||||
id: 'balance',
|
||||
header: 'Saldo',
|
||||
},
|
||||
{
|
||||
id: 'unit_price_in',
|
||||
header: 'Harga Masuk',
|
||||
},
|
||||
{
|
||||
id: 'unit_price_out',
|
||||
header: 'Harga Keluar',
|
||||
},
|
||||
{
|
||||
id: 'total_price_in',
|
||||
header: 'Total Harga Masuk',
|
||||
},
|
||||
{
|
||||
id: 'total_price_out',
|
||||
header: 'Total Harga Keluar',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<ClosingTabSkeleton<RowSapronakCalculation>
|
||||
columns={columns || defaultColumns}
|
||||
icon={
|
||||
<Icon icon={iconName} className='text-white' width={20} height={20} />
|
||||
}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SapronakCalculationClosingSkeleton;
|
||||
@@ -0,0 +1,126 @@
|
||||
import { Icon } from '@iconify/react';
|
||||
import ClosingTabSkeleton from './ClosingTabSkeleton';
|
||||
import { ClosingIncomingSapronak } from '@/types/api/closing';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
const SapronakClosingSkeleton = ({
|
||||
columns,
|
||||
type = 'incoming',
|
||||
title,
|
||||
subtitle,
|
||||
iconName = 'heroicons:chart-bar',
|
||||
}: {
|
||||
columns?: ColumnDef<ClosingIncomingSapronak>[];
|
||||
type?: 'incoming' | 'outgoing';
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
iconName?: string;
|
||||
}) => {
|
||||
const defaultIncomingColumns: ColumnDef<ClosingIncomingSapronak>[] = [
|
||||
{
|
||||
id: '#',
|
||||
header: '#',
|
||||
},
|
||||
{
|
||||
id: 'date',
|
||||
header: 'Tanggal',
|
||||
},
|
||||
{
|
||||
id: 'reference_number',
|
||||
header: 'No. Referensi',
|
||||
},
|
||||
{
|
||||
id: 'transaction_type',
|
||||
header: 'Jenis Transaksi',
|
||||
},
|
||||
{
|
||||
id: 'product_name',
|
||||
header: 'Produk',
|
||||
},
|
||||
{
|
||||
id: 'product_category',
|
||||
header: 'Kategori Produk',
|
||||
},
|
||||
{
|
||||
id: 'source_warehouse',
|
||||
header: 'Gudang Asal',
|
||||
},
|
||||
{
|
||||
id: 'destination_warehouse',
|
||||
header: 'Gudang Tujuan',
|
||||
},
|
||||
{
|
||||
id: 'quantity',
|
||||
header: 'Kuantitas',
|
||||
},
|
||||
{
|
||||
id: 'notes',
|
||||
header: 'Keterangan',
|
||||
},
|
||||
];
|
||||
|
||||
const defaultOutgoingColumns: ColumnDef<ClosingIncomingSapronak>[] = [
|
||||
{
|
||||
id: '#',
|
||||
header: '#',
|
||||
},
|
||||
{
|
||||
id: 'date',
|
||||
header: 'Tanggal',
|
||||
},
|
||||
{
|
||||
id: 'reference_number',
|
||||
header: 'No. Referensi',
|
||||
},
|
||||
{
|
||||
id: 'transaction_type',
|
||||
header: 'Jenis Transaksi',
|
||||
},
|
||||
{
|
||||
id: 'product_name',
|
||||
header: 'Produk',
|
||||
},
|
||||
{
|
||||
id: 'product_category',
|
||||
header: 'Kategori Produk',
|
||||
},
|
||||
{
|
||||
id: 'source_warehouse',
|
||||
header: 'Gudang Asal',
|
||||
},
|
||||
{
|
||||
id: 'quantity',
|
||||
header: 'Kuantitas',
|
||||
},
|
||||
{
|
||||
id: 'notes',
|
||||
header: 'Keterangan',
|
||||
},
|
||||
];
|
||||
|
||||
const defaultTitle =
|
||||
type === 'incoming'
|
||||
? 'Data Sapronak Masuk Belum Tersedia'
|
||||
: 'Data Sapronak Keluar Belum Tersedia';
|
||||
|
||||
const defaultSubtitle =
|
||||
type === 'incoming'
|
||||
? 'Silakan pilih periode atau filter untuk melihat data sapronak masuk.'
|
||||
: 'Silakan pilih periode atau filter untuk melihat data sapronak keluar.';
|
||||
|
||||
return (
|
||||
<ClosingTabSkeleton<ClosingIncomingSapronak>
|
||||
columns={
|
||||
columns ||
|
||||
(type === 'incoming' ? defaultIncomingColumns : defaultOutgoingColumns)
|
||||
}
|
||||
icon={
|
||||
<Icon icon={iconName} className='text-white' width={20} height={20} />
|
||||
}
|
||||
title={title || defaultTitle}
|
||||
subtitle={subtitle || defaultSubtitle}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SapronakClosingSkeleton;
|
||||
@@ -5,6 +5,7 @@ import useSWR from 'swr';
|
||||
import { ClosingApi } from '@/services/api/closing';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { formatNumber } from '@/lib/helper';
|
||||
import ProductionDataClosingSkeleton from '@/components/pages/closing/skeleton/ProductionDataClosingSkeleton';
|
||||
|
||||
interface ProductionDataClosingTabProps {
|
||||
projectFlockId: number;
|
||||
@@ -22,18 +23,16 @@ const ProductionDataClosingTab = ({
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className='w-full flex justify-center py-8'>
|
||||
<span className='loading loading-spinner loading-lg' />
|
||||
</div>
|
||||
);
|
||||
return <ProductionDataClosingSkeleton />;
|
||||
}
|
||||
|
||||
if (!productionData || !isResponseSuccess(productionData)) {
|
||||
return (
|
||||
<div className='w-full text-center py-8 text-gray-500'>
|
||||
Gagal memuat data produksi.
|
||||
</div>
|
||||
<ProductionDataClosingSkeleton
|
||||
iconName='heroicons:exclamation-circle'
|
||||
title='Gagal Memuat Data Produksi'
|
||||
subtitle='Terjadi kesalahan saat memuat data produksi. Silakan coba lagi.'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { HppItem, ProfitLossItem } from '@/types/api/closing';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useMemo } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import FinanceClosingSkeleton from '@/components/pages/closing/skeleton/FinanceClosingSkeleton';
|
||||
|
||||
const FinanceClosingTable = ({
|
||||
projectFlockId,
|
||||
@@ -82,316 +83,336 @@ const FinanceClosingTable = ({
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-4'>
|
||||
<>
|
||||
<Card
|
||||
variant='bordered'
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<div className='grid grid-cols-2 gap-6'>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<div>Laba Rugi Brutto</div>
|
||||
<div className='text-lg font-bold'>
|
||||
{isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.profit_loss.summary.gross_profit.amount
|
||||
)
|
||||
: '-'}
|
||||
{isLoading ? (
|
||||
<FinanceClosingSkeleton />
|
||||
) : !isResponseSuccess(finance) ? (
|
||||
<FinanceClosingSkeleton
|
||||
iconName='heroicons:chart-bar'
|
||||
title='Data Keuangan Tidak Ditemukan'
|
||||
subtitle='Tidak ada data keuangan untuk periode ini.'
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Card
|
||||
variant='bordered'
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<div className='grid grid-cols-2 gap-6'>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<div>Laba Rugi Brutto</div>
|
||||
<div className='text-lg font-bold'>
|
||||
{isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.profit_loss.summary.gross_profit.amount
|
||||
)
|
||||
: '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<div>Laba Rugi Netto</div>
|
||||
<div className='text-lg font-bold'>
|
||||
{isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.profit_loss.summary.net_profit.amount
|
||||
)
|
||||
: '-'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<div>Laba Rugi Netto</div>
|
||||
<div className='text-lg font-bold'>
|
||||
{isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.profit_loss.summary.net_profit.amount
|
||||
)
|
||||
: '-'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card
|
||||
title='HPP Purchases'
|
||||
variant='bordered'
|
||||
collapsible
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<div className='mt-6 p-0 mb-0'>
|
||||
<Table<HppItem>
|
||||
data={hppTableData}
|
||||
isLoading={isLoading}
|
||||
columns={[
|
||||
{
|
||||
header: 'No.',
|
||||
enableSorting: false,
|
||||
accessorFn: (item, index) => {
|
||||
if (item.code === 'custom_row') return '-';
|
||||
const dataRowsBefore = hppTableData
|
||||
.slice(0, index)
|
||||
.filter((row) => row.code !== 'custom_row').length;
|
||||
return dataRowsBefore + 1;
|
||||
</Card>
|
||||
<Card
|
||||
title='HPP Purchases'
|
||||
variant='bordered'
|
||||
collapsible
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<div className='mt-6 p-0 mb-0'>
|
||||
<Table<HppItem>
|
||||
data={hppTableData}
|
||||
isLoading={isLoading}
|
||||
columns={[
|
||||
{
|
||||
header: 'No.',
|
||||
enableSorting: false,
|
||||
accessorFn: (item, index) => {
|
||||
if (item.code === 'custom_row') return '-';
|
||||
const dataRowsBefore = hppTableData
|
||||
.slice(0, index)
|
||||
.filter((row) => row.code !== 'custom_row').length;
|
||||
return dataRowsBefore + 1;
|
||||
},
|
||||
footer: (props) => {
|
||||
return 'HPP';
|
||||
},
|
||||
},
|
||||
footer: (props) => {
|
||||
return 'HPP';
|
||||
{
|
||||
header: 'Jenis',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) => formatTitleCase(item.label || '-'),
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Jenis',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) => formatTitleCase(item.label || '-'),
|
||||
},
|
||||
{
|
||||
header: 'Budgeting',
|
||||
enableSorting: false,
|
||||
columns: [
|
||||
{
|
||||
header: 'Rp/Ekor',
|
||||
id: 'budgeting_rp_per_bird',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.budgeting?.rp_per_bird || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id === 'budgeting_rp_per_bird' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.budgeting
|
||||
?.rp_per_bird || 0
|
||||
)
|
||||
: '-';
|
||||
{
|
||||
header: 'Budgeting',
|
||||
enableSorting: false,
|
||||
columns: [
|
||||
{
|
||||
header: 'Rp/Ekor',
|
||||
id: 'budgeting_rp_per_bird',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.budgeting?.rp_per_bird || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id === 'budgeting_rp_per_bird' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.budgeting
|
||||
?.rp_per_bird || 0
|
||||
)
|
||||
: '-';
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Rp/Kg',
|
||||
id: 'budgeting_rp_per_kg',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.budgeting?.rp_per_kg || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id === 'budgeting_rp_per_kg' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.budgeting?.rp_per_kg ||
|
||||
0
|
||||
)
|
||||
: '-';
|
||||
{
|
||||
header: 'Rp/Kg',
|
||||
id: 'budgeting_rp_per_kg',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.budgeting?.rp_per_kg || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id === 'budgeting_rp_per_kg' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.budgeting
|
||||
?.rp_per_kg || 0
|
||||
)
|
||||
: '-';
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Jumlah (Rp)',
|
||||
id: 'budgeting_amount',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.budgeting?.amount || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id === 'budgeting_amount' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.budgeting?.amount || 0
|
||||
)
|
||||
: '-';
|
||||
{
|
||||
header: 'Jumlah (Rp)',
|
||||
id: 'budgeting_amount',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.budgeting?.amount || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id === 'budgeting_amount' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.budgeting?.amount || 0
|
||||
)
|
||||
: '-';
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
header: 'Realization',
|
||||
enableSorting: false,
|
||||
columns: [
|
||||
{
|
||||
header: 'Rp/Ekor',
|
||||
id: 'realization_rp_per_bird',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.realization?.rp_per_bird || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id === 'realization_rp_per_bird' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.realization
|
||||
?.rp_per_bird || 0
|
||||
)
|
||||
: '-';
|
||||
],
|
||||
},
|
||||
{
|
||||
header: 'Realization',
|
||||
enableSorting: false,
|
||||
columns: [
|
||||
{
|
||||
header: 'Rp/Ekor',
|
||||
id: 'realization_rp_per_bird',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.realization?.rp_per_bird || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id ===
|
||||
'realization_rp_per_bird' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.realization
|
||||
?.rp_per_bird || 0
|
||||
)
|
||||
: '-';
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Rp/Kg',
|
||||
id: 'realization_rp_per_kg',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.realization?.rp_per_kg || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id === 'realization_rp_per_kg' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.realization
|
||||
?.rp_per_kg || 0
|
||||
)
|
||||
: '-';
|
||||
{
|
||||
header: 'Rp/Kg',
|
||||
id: 'realization_rp_per_kg',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.realization?.rp_per_kg || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id === 'realization_rp_per_kg' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.realization
|
||||
?.rp_per_kg || 0
|
||||
)
|
||||
: '-';
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Jumlah (Rp)',
|
||||
id: 'realization_amount',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.realization?.amount || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id === 'realization_amount' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.realization?.amount || 0
|
||||
)
|
||||
: '-';
|
||||
{
|
||||
header: 'Jumlah (Rp)',
|
||||
id: 'realization_amount',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) =>
|
||||
formatCurrency(item.realization?.amount || 0),
|
||||
footer: (props) => {
|
||||
return props.column.id === 'realization_amount' &&
|
||||
isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.hpp.summary?.realization?.amount ||
|
||||
0
|
||||
)
|
||||
: '-';
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
renderCustomRow={(row) => {
|
||||
const rowData = row.original;
|
||||
if (rowData.code === 'custom_row') {
|
||||
return (
|
||||
<tr
|
||||
key={row.id}
|
||||
className={TABLE_DEFAULT_STYLING.bodyRowClassName}
|
||||
>
|
||||
<td
|
||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||
></td>
|
||||
<td
|
||||
colSpan={7}
|
||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||
],
|
||||
},
|
||||
]}
|
||||
renderCustomRow={(row) => {
|
||||
const rowData = row.original;
|
||||
if (rowData.code === 'custom_row') {
|
||||
return (
|
||||
<tr
|
||||
key={row.id}
|
||||
className={TABLE_DEFAULT_STYLING.bodyRowClassName}
|
||||
>
|
||||
<div className='font-bold'>
|
||||
{formatTitleCase(rowData.label ?? '-')}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
renderFooter={isResponseSuccess(finance)}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
<Card
|
||||
title='Profit/Loss'
|
||||
variant='bordered'
|
||||
collapsible
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<div className='mt-6 p-0 mb-0'>
|
||||
<Table<ProfitLossItem>
|
||||
data={profitLossTableData}
|
||||
isLoading={isLoading}
|
||||
columns={[
|
||||
{
|
||||
header: 'Jenis',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) => item.label,
|
||||
cell: (item) => (
|
||||
<div className=''>
|
||||
{formatTitleCase(item.row.original.label || '-')}
|
||||
</div>
|
||||
),
|
||||
footer: () => (
|
||||
<div className='font-bold uppercase'>LABA RUGI NETTO</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Rp/Ekor',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) => formatCurrency(item.rp_per_bird || 0),
|
||||
footer: () => (
|
||||
<div className='font-bold'>
|
||||
{isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.profit_loss.summary.net_profit
|
||||
.rp_per_bird || 0
|
||||
)
|
||||
: formatCurrency(0)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Rp/Kg',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) => formatCurrency(item.rp_per_kg || 0),
|
||||
footer: () => (
|
||||
<div className='font-bold'>
|
||||
{isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.profit_loss.summary.net_profit
|
||||
.rp_per_kg || 0
|
||||
)
|
||||
: formatCurrency(0)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Jumlah (Rp)',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) => formatCurrency(item.amount || 0),
|
||||
footer: () => (
|
||||
<div className='font-bold'>
|
||||
{isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.profit_loss.summary.net_profit
|
||||
.amount || 0
|
||||
)
|
||||
: formatCurrency(0)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
renderCustomRow={(row) => {
|
||||
const rowData = row.original;
|
||||
if (rowData.code === 'custom_row') {
|
||||
return (
|
||||
<tr
|
||||
key={row.id}
|
||||
className={TABLE_DEFAULT_STYLING.footerRowClassName}
|
||||
>
|
||||
<td className={TABLE_DEFAULT_STYLING.bodyColumnClassName}>
|
||||
<div className='font-bold ps-6 uppercase'>
|
||||
{formatTitleCase(rowData.label ?? '-')}
|
||||
</div>
|
||||
</td>
|
||||
<td className={TABLE_DEFAULT_STYLING.bodyColumnClassName}>
|
||||
<div className='font-bold'>
|
||||
{formatCurrency(rowData.rp_per_bird ?? 0)}
|
||||
</div>
|
||||
</td>
|
||||
<td className={TABLE_DEFAULT_STYLING.bodyColumnClassName}>
|
||||
<div className='font-bold'>
|
||||
{formatCurrency(rowData.rp_per_kg ?? 0)}
|
||||
</div>
|
||||
</td>
|
||||
<td className={TABLE_DEFAULT_STYLING.bodyColumnClassName}>
|
||||
<div className='font-bold'>
|
||||
{formatCurrency(rowData.amount ?? 0)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
className={{
|
||||
paginationClassName: 'hidden',
|
||||
}}
|
||||
renderFooter={isResponseSuccess(finance)}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</>
|
||||
<td
|
||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||
></td>
|
||||
<td
|
||||
colSpan={7}
|
||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||
>
|
||||
<div className='font-bold'>
|
||||
{formatTitleCase(rowData.label ?? '-')}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
renderFooter={isResponseSuccess(finance)}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
<Card
|
||||
title='Profit/Loss'
|
||||
variant='bordered'
|
||||
collapsible
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<div className='mt-6 p-0 mb-0'>
|
||||
<Table<ProfitLossItem>
|
||||
data={profitLossTableData}
|
||||
isLoading={isLoading}
|
||||
columns={[
|
||||
{
|
||||
header: 'Jenis',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) => item.label,
|
||||
cell: (item) => (
|
||||
<div className=''>
|
||||
{formatTitleCase(item.row.original.label || '-')}
|
||||
</div>
|
||||
),
|
||||
footer: () => (
|
||||
<div className='font-bold uppercase'>LABA RUGI NETTO</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Rp/Ekor',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) => formatCurrency(item.rp_per_bird || 0),
|
||||
footer: () => (
|
||||
<div className='font-bold'>
|
||||
{isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.profit_loss.summary.net_profit
|
||||
.rp_per_bird || 0
|
||||
)
|
||||
: formatCurrency(0)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Rp/Kg',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) => formatCurrency(item.rp_per_kg || 0),
|
||||
footer: () => (
|
||||
<div className='font-bold'>
|
||||
{isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.profit_loss.summary.net_profit
|
||||
.rp_per_kg || 0
|
||||
)
|
||||
: formatCurrency(0)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Jumlah (Rp)',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) => formatCurrency(item.amount || 0),
|
||||
footer: () => (
|
||||
<div className='font-bold'>
|
||||
{isResponseSuccess(finance)
|
||||
? formatCurrency(
|
||||
finance.data.profit_loss.summary.net_profit
|
||||
.amount || 0
|
||||
)
|
||||
: formatCurrency(0)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
renderCustomRow={(row) => {
|
||||
const rowData = row.original;
|
||||
if (rowData.code === 'custom_row') {
|
||||
return (
|
||||
<tr
|
||||
key={row.id}
|
||||
className={TABLE_DEFAULT_STYLING.footerRowClassName}
|
||||
>
|
||||
<td
|
||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||
>
|
||||
<div className='font-bold ps-6 uppercase'>
|
||||
{formatTitleCase(rowData.label ?? '-')}
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||
>
|
||||
<div className='font-bold'>
|
||||
{formatCurrency(rowData.rp_per_bird ?? 0)}
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||
>
|
||||
<div className='font-bold'>
|
||||
{formatCurrency(rowData.rp_per_kg ?? 0)}
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||
>
|
||||
<div className='font-bold'>
|
||||
{formatCurrency(rowData.amount ?? 0)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
className={{
|
||||
paginationClassName: 'hidden',
|
||||
}}
|
||||
renderFooter={isResponseSuccess(finance)}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@ import { BaseExpeditionCost } from '@/types/api/closing';
|
||||
import { ClosingApi } from '@/services/api/closing';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
import HppExpeditionClosingSkeleton from '@/components/pages/closing/skeleton/HppExpeditionClosingSkeleton';
|
||||
|
||||
interface HppExpeditionClosingTableProps {
|
||||
projectFlockId: number;
|
||||
@@ -100,28 +101,39 @@ const HppExpeditionClosingTable = ({
|
||||
body: 'p-0',
|
||||
}}
|
||||
>
|
||||
<Table
|
||||
data={costOfRevenueExpeditionData}
|
||||
columns={costOfRevenueExpeditionColumns}
|
||||
isLoading={isLoading}
|
||||
renderFooter={costOfRevenueExpeditionData.length > 0}
|
||||
className={{
|
||||
tableWrapperClassName: 'overflow-x-auto',
|
||||
tableClassName: 'w-full table-auto text-sm',
|
||||
headerRowClassName: 'border-b border-b-gray-200',
|
||||
headerColumnClassName:
|
||||
'px-4 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end whitespace-nowrap',
|
||||
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',
|
||||
bodyColumnClassName:
|
||||
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
||||
tableFooterClassName:
|
||||
'bg-gray-100 font-semibold border border-gray-200',
|
||||
footerRowClassName: 'border-t-2 border-gray-300',
|
||||
footerColumnClassName:
|
||||
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
||||
}}
|
||||
/>
|
||||
{isLoading ? (
|
||||
<HppExpeditionClosingSkeleton
|
||||
columns={costOfRevenueExpeditionColumns}
|
||||
/>
|
||||
) : costOfRevenueExpeditionData.length === 0 ? (
|
||||
<HppExpeditionClosingSkeleton
|
||||
columns={costOfRevenueExpeditionColumns}
|
||||
iconName='heroicons:chart-bar'
|
||||
/>
|
||||
) : (
|
||||
<Table
|
||||
data={costOfRevenueExpeditionData}
|
||||
columns={costOfRevenueExpeditionColumns}
|
||||
isLoading={isLoading}
|
||||
renderFooter={costOfRevenueExpeditionData.length > 0}
|
||||
className={{
|
||||
tableWrapperClassName: 'overflow-x-auto',
|
||||
tableClassName: 'w-full table-auto text-sm',
|
||||
headerRowClassName: 'border-b border-b-gray-200',
|
||||
headerColumnClassName:
|
||||
'px-4 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end whitespace-nowrap',
|
||||
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',
|
||||
bodyColumnClassName:
|
||||
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
||||
tableFooterClassName:
|
||||
'bg-gray-100 font-semibold border border-gray-200',
|
||||
footerRowClassName: 'border-t-2 border-gray-300',
|
||||
footerColumnClassName:
|
||||
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ColumnDef } from '@tanstack/react-table';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useMemo } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import OverheadClosingSkeleton from '@/components/pages/closing/skeleton/OverheadClosingSkeleton';
|
||||
|
||||
interface OverheadClosingTableProps {
|
||||
projectFlockId: number;
|
||||
@@ -209,95 +210,108 @@ const OverheadClosingTable = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card
|
||||
title='Pengeluaran Overhead'
|
||||
collapsible
|
||||
defaultCollapsed={false}
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
body: 'p-4 shadow',
|
||||
}}
|
||||
>
|
||||
<Table<Overhead>
|
||||
data={
|
||||
kandangId
|
||||
? isResponseSuccess(overheadKandang)
|
||||
? (overheadKandang.data?.overheads ?? [])
|
||||
: []
|
||||
: isResponseSuccess(overhead)
|
||||
? (overhead.data?.overheads ?? [])
|
||||
: []
|
||||
}
|
||||
{isLoadingOverhead ? (
|
||||
<OverheadClosingSkeleton columns={columns} />
|
||||
) : !isResponseSuccess(overhead) ||
|
||||
(!kandangId && overhead.data?.overheads.length === 0) ||
|
||||
(kandangId && !isResponseSuccess(overheadKandang)) ? (
|
||||
<OverheadClosingSkeleton
|
||||
columns={columns}
|
||||
className={{
|
||||
containerClassName: 'my-4',
|
||||
headerColumnClassName: cn(
|
||||
TABLE_DEFAULT_STYLING.headerColumnClassName,
|
||||
'whitespace-nowrap'
|
||||
),
|
||||
}}
|
||||
isLoading={isLoadingOverhead}
|
||||
renderFooter={
|
||||
isResponseSuccess(overhead)
|
||||
? overhead.data?.overheads.length > 0
|
||||
: false
|
||||
}
|
||||
iconName='heroicons:chart-bar'
|
||||
title='Data Overhead Tidak Ditemukan'
|
||||
subtitle='Tidak ada data overhead untuk periode ini.'
|
||||
/>
|
||||
{kandangId && (
|
||||
<Card
|
||||
) : (
|
||||
<Card
|
||||
title='Pengeluaran Overhead'
|
||||
collapsible
|
||||
defaultCollapsed={false}
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
body: 'p-4 shadow',
|
||||
}}
|
||||
>
|
||||
<Table<Overhead>
|
||||
data={
|
||||
kandangId
|
||||
? isResponseSuccess(overheadKandang)
|
||||
? (overheadKandang.data?.overheads ?? [])
|
||||
: []
|
||||
: isResponseSuccess(overhead)
|
||||
? (overhead.data?.overheads ?? [])
|
||||
: []
|
||||
}
|
||||
columns={columns}
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
body: 'p-4 shadow-button-soft border border-base-content/10 rounded-lg',
|
||||
containerClassName: 'my-4',
|
||||
headerColumnClassName: cn(
|
||||
TABLE_DEFAULT_STYLING.headerColumnClassName,
|
||||
'whitespace-nowrap'
|
||||
),
|
||||
}}
|
||||
>
|
||||
<div className='flex flex-row gap-3 w-full justify-center items-stretch'>
|
||||
<div className='flex flex-row items-center justify-between'>
|
||||
<h2 className='text-base font-bold'>Pembelian Kandang </h2>
|
||||
</div>
|
||||
<div className='flex flex-col items-center justify-center'>
|
||||
<Icon icon='heroicons:equals' className='inline' />
|
||||
</div>
|
||||
<div className='flex flex-col flex-1 gap-1.5'>
|
||||
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
|
||||
Populasi Akhir KANDANG{' '}
|
||||
<Icon icon='heroicons:x-mark' className='inline' /> Pemakaian
|
||||
Di FARM
|
||||
isLoading={isLoadingOverhead}
|
||||
renderFooter={
|
||||
isResponseSuccess(overhead)
|
||||
? overhead.data?.overheads.length > 0
|
||||
: false
|
||||
}
|
||||
/>
|
||||
{kandangId && (
|
||||
<Card
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
body: 'p-4 shadow-button-soft border border-base-content/10 rounded-lg',
|
||||
}}
|
||||
>
|
||||
<div className='flex flex-row gap-3 w-full justify-center items-stretch'>
|
||||
<div className='flex flex-row items-center justify-between'>
|
||||
<h2 className='text-base font-bold'>Pembelian Kandang </h2>
|
||||
</div>
|
||||
<hr className='w-full h-fit m-0 p-0 text-base-content/65' />
|
||||
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
|
||||
Populasi Akhir Proyek
|
||||
<div className='flex flex-col items-center justify-center'>
|
||||
<Icon icon='heroicons:equals' className='inline' />
|
||||
</div>
|
||||
<div className='flex flex-col flex-1 gap-1.5'>
|
||||
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
|
||||
Populasi Akhir KANDANG{' '}
|
||||
<Icon icon='heroicons:x-mark' className='inline' />{' '}
|
||||
Pemakaian Di FARM
|
||||
</div>
|
||||
<hr className='w-full h-fit m-0 p-0 text-base-content/65' />
|
||||
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
|
||||
Populasi Akhir Proyek
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col items-center justify-center'>
|
||||
<Icon icon='heroicons:equals' className='inline' />
|
||||
</div>
|
||||
<div className='flex flex-col flex-1 gap-1.5'>
|
||||
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
|
||||
{formatNumber(chickinPopulation ?? 0)}
|
||||
<Icon icon='heroicons:x-mark' className='inline' />
|
||||
{formatCurrency(
|
||||
isResponseSuccess(overhead)
|
||||
? overhead.data?.total.actual_total_amount
|
||||
: 0
|
||||
)}
|
||||
</div>
|
||||
<hr className='w-full h-fit m-0 p-0 text-base-content/65' />
|
||||
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
|
||||
{formatNumber(generalInformation?.population ?? 0)}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col items-center justify-center'>
|
||||
<Icon icon='heroicons:equals' className='inline' />
|
||||
</div>
|
||||
<div className='flex flex-row items-center justify-between'>
|
||||
<h2 className='text-base font-bold'>
|
||||
{formatNumber(kandangTotal || 0)}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col items-center justify-center'>
|
||||
<Icon icon='heroicons:equals' className='inline' />
|
||||
</div>
|
||||
<div className='flex flex-col flex-1 gap-1.5'>
|
||||
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
|
||||
{formatNumber(chickinPopulation ?? 0)}
|
||||
<Icon icon='heroicons:x-mark' className='inline' />
|
||||
{formatCurrency(
|
||||
isResponseSuccess(overhead)
|
||||
? overhead.data?.total.actual_total_amount
|
||||
: 0
|
||||
)}
|
||||
</div>
|
||||
<hr className='w-full h-fit m-0 p-0 text-base-content/65' />
|
||||
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
|
||||
{formatNumber(generalInformation?.population ?? 0)}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col items-center justify-center'>
|
||||
<Icon icon='heroicons:equals' className='inline' />
|
||||
</div>
|
||||
<div className='flex flex-row items-center justify-between'>
|
||||
<h2 className='text-base font-bold'>
|
||||
{formatNumber(kandangTotal || 0)}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</Card>
|
||||
</Card>
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import { ClosingApi } from '@/services/api/closing';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
import SalesClosingSkeleton from '@/components/pages/closing/skeleton/SalesClosingSkeleton';
|
||||
|
||||
interface SalesClosingTableProps {
|
||||
projectFlockId: number;
|
||||
@@ -325,27 +326,36 @@ const SalesClosingTable = ({ projectFlockId }: SalesClosingTableProps) => {
|
||||
body: 'p-0',
|
||||
}}
|
||||
>
|
||||
<Table
|
||||
data={salesData}
|
||||
columns={salesColumns}
|
||||
isLoading={isLoading}
|
||||
renderFooter={salesData.length > 0}
|
||||
className={{
|
||||
tableWrapperClassName: 'overflow-x-auto',
|
||||
tableClassName: 'w-full table-auto text-sm',
|
||||
headerColumnClassName:
|
||||
'px-4 py-3 text-xs font-semibold text-gray-500 whitespace-nowrap border-l border-l-gray-200 border-r border-r-gray-200 border-t border-t-gray-200 border-gray-200 border-b-0',
|
||||
bodyRowClassName:
|
||||
'hover:bg-gray-50 transition-colors border-b border-gray-200 first:border-t first:border-t-gray-200 border-l border-l-gray-200 border-r border-r-gray-200',
|
||||
bodyColumnClassName:
|
||||
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
||||
tableFooterClassName:
|
||||
'bg-gray-100 font-semibold border border-gray-200',
|
||||
footerRowClassName: 'border-t-2 border-gray-300',
|
||||
footerColumnClassName:
|
||||
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
||||
}}
|
||||
/>
|
||||
{isLoading ? (
|
||||
<SalesClosingSkeleton columns={salesColumns} />
|
||||
) : salesData.length === 0 ? (
|
||||
<SalesClosingSkeleton
|
||||
columns={salesColumns}
|
||||
iconName='heroicons:chart-bar'
|
||||
/>
|
||||
) : (
|
||||
<Table
|
||||
data={salesData}
|
||||
columns={salesColumns}
|
||||
isLoading={isLoading}
|
||||
renderFooter={salesData.length > 0}
|
||||
className={{
|
||||
tableWrapperClassName: 'overflow-x-auto',
|
||||
tableClassName: 'w-full table-auto text-sm',
|
||||
headerColumnClassName:
|
||||
'px-4 py-3 text-xs font-semibold text-gray-500 whitespace-nowrap border-l border-l-gray-200 border-r border-r-gray-200 border-t border-t-gray-200 border-gray-200 border-b-0',
|
||||
bodyRowClassName:
|
||||
'hover:bg-gray-50 transition-colors border-b border-gray-200 first:border-t first:border-t-gray-200 border-l border-l-gray-200 border-r border-r-gray-200',
|
||||
bodyColumnClassName:
|
||||
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
||||
tableFooterClassName:
|
||||
'bg-gray-100 font-semibold border border-gray-200',
|
||||
footerRowClassName: 'border-t-2 border-gray-300',
|
||||
footerColumnClassName:
|
||||
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ClosingApi } from '@/services/api/closing';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { ClosingGeneralInformation } from '@/types/api/closing';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import SapronakCalculationClosingSkeleton from '@/components/pages/closing/skeleton/SapronakCalculationClosingSkeleton';
|
||||
|
||||
interface SapronakCalculationClosingTableProps {
|
||||
projectFlockId: number;
|
||||
@@ -193,21 +194,32 @@ const SapronakCalculationClosingTable = ({
|
||||
body: 'p-4 shadow',
|
||||
}}
|
||||
>
|
||||
<Table<RowSapronakCalculation>
|
||||
data={
|
||||
isResponseSuccess(sapronakCalculation)
|
||||
? (sapronakCalculation.data?.doc?.rows ?? [])
|
||||
: []
|
||||
}
|
||||
columns={docColumns}
|
||||
className={{
|
||||
containerClassName: 'my-4',
|
||||
}}
|
||||
renderFooter={
|
||||
isResponseSuccess(sapronakCalculation) &&
|
||||
sapronakCalculation.data?.doc?.rows.length > 0
|
||||
}
|
||||
/>
|
||||
{isLoading ? (
|
||||
<SapronakCalculationClosingSkeleton />
|
||||
) : isResponseSuccess(sapronakCalculation) &&
|
||||
sapronakCalculation.data?.doc?.rows?.length === 0 ? (
|
||||
<SapronakCalculationClosingSkeleton
|
||||
iconName='heroicons:chart-bar'
|
||||
title='Data Perhitungan Sapronak Tidak Ditemukan'
|
||||
subtitle='Tidak ada data perhitungan sapronak untuk periode ini.'
|
||||
/>
|
||||
) : (
|
||||
<Table<RowSapronakCalculation>
|
||||
data={
|
||||
isResponseSuccess(sapronakCalculation)
|
||||
? (sapronakCalculation.data?.doc?.rows ?? [])
|
||||
: []
|
||||
}
|
||||
columns={docColumns}
|
||||
className={{
|
||||
containerClassName: 'my-4',
|
||||
}}
|
||||
renderFooter={
|
||||
isResponseSuccess(sapronakCalculation) &&
|
||||
sapronakCalculation.data?.doc?.rows?.length > 0
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
@@ -219,21 +231,32 @@ const SapronakCalculationClosingTable = ({
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<Table<RowSapronakCalculation>
|
||||
data={
|
||||
isResponseSuccess(sapronakCalculation)
|
||||
? (sapronakCalculation.data?.ovk?.rows ?? [])
|
||||
: []
|
||||
}
|
||||
columns={ovkColumns}
|
||||
className={{
|
||||
containerClassName: 'my-4',
|
||||
}}
|
||||
renderFooter={
|
||||
isResponseSuccess(sapronakCalculation) &&
|
||||
sapronakCalculation.data?.ovk?.rows.length > 0
|
||||
}
|
||||
/>
|
||||
{isLoading ? (
|
||||
<SapronakCalculationClosingSkeleton />
|
||||
) : isResponseSuccess(sapronakCalculation) &&
|
||||
sapronakCalculation.data?.ovk?.rows?.length === 0 ? (
|
||||
<SapronakCalculationClosingSkeleton
|
||||
iconName='heroicons:chart-bar'
|
||||
title='Data Perhitungan Sapronak Tidak Ditemukan'
|
||||
subtitle='Tidak ada data perhitungan sapronak untuk periode ini.'
|
||||
/>
|
||||
) : (
|
||||
<Table<RowSapronakCalculation>
|
||||
data={
|
||||
isResponseSuccess(sapronakCalculation)
|
||||
? (sapronakCalculation.data?.ovk?.rows ?? [])
|
||||
: []
|
||||
}
|
||||
columns={ovkColumns}
|
||||
className={{
|
||||
containerClassName: 'my-4',
|
||||
}}
|
||||
renderFooter={
|
||||
isResponseSuccess(sapronakCalculation) &&
|
||||
sapronakCalculation.data?.ovk?.rows?.length > 0
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
@@ -245,21 +268,32 @@ const SapronakCalculationClosingTable = ({
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<Table<RowSapronakCalculation>
|
||||
data={
|
||||
isResponseSuccess(sapronakCalculation)
|
||||
? (sapronakCalculation.data?.pakan?.rows ?? [])
|
||||
: []
|
||||
}
|
||||
columns={pakanColumns}
|
||||
className={{
|
||||
containerClassName: 'my-4',
|
||||
}}
|
||||
renderFooter={
|
||||
isResponseSuccess(sapronakCalculation) &&
|
||||
sapronakCalculation.data?.pakan?.rows.length > 0
|
||||
}
|
||||
/>
|
||||
{isLoading ? (
|
||||
<SapronakCalculationClosingSkeleton />
|
||||
) : isResponseSuccess(sapronakCalculation) &&
|
||||
sapronakCalculation.data?.pakan?.rows?.length === 0 ? (
|
||||
<SapronakCalculationClosingSkeleton
|
||||
iconName='heroicons:chart-bar'
|
||||
title='Data Perhitungan Sapronak Tidak Ditemukan'
|
||||
subtitle='Tidak ada data perhitungan sapronak untuk periode ini.'
|
||||
/>
|
||||
) : (
|
||||
<Table<RowSapronakCalculation>
|
||||
data={
|
||||
isResponseSuccess(sapronakCalculation)
|
||||
? (sapronakCalculation.data?.pakan?.rows ?? [])
|
||||
: []
|
||||
}
|
||||
columns={pakanColumns}
|
||||
className={{
|
||||
containerClassName: 'my-4',
|
||||
}}
|
||||
renderFooter={
|
||||
isResponseSuccess(sapronakCalculation) &&
|
||||
sapronakCalculation.data?.pakan?.rows?.length > 0
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -15,6 +15,7 @@ import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { ClosingApi } from '@/services/api/closing';
|
||||
import { ClosingIncomingSapronakSummary } from '@/types/api/closing';
|
||||
import SapronakClosingSkeleton from '@/components/pages/closing/skeleton/SapronakClosingSkeleton';
|
||||
|
||||
interface ClosingIncomingSapronaksSummaryTableProps {
|
||||
projectFlockId: number;
|
||||
@@ -131,40 +132,52 @@ const ClosingIncomingSapronaksSummaryTable = ({
|
||||
titleClassName='w-full p-0!'
|
||||
>
|
||||
<div className='w-full p-0'>
|
||||
<Table<ClosingIncomingSapronakSummary>
|
||||
data={
|
||||
isResponseSuccess(incomingSapronakSummaries)
|
||||
? incomingSapronakSummaries?.data
|
||||
: []
|
||||
}
|
||||
columns={incomingSapronaksColumns}
|
||||
pageSize={tableFilterState.pageSize}
|
||||
onPageSizeChange={setPageSize}
|
||||
rowOptions={[10, 20, 50, 100]}
|
||||
page={
|
||||
isResponseSuccess(incomingSapronakSummaries)
|
||||
? incomingSapronakSummaries?.meta?.page
|
||||
: 0
|
||||
}
|
||||
totalItems={
|
||||
isResponseSuccess(incomingSapronakSummaries)
|
||||
? incomingSapronakSummaries?.meta?.total_results
|
||||
: 0
|
||||
}
|
||||
onPageChange={setPage}
|
||||
isLoading={isLoadingIncomingSapronakSummaries}
|
||||
sorting={sorting}
|
||||
setSorting={setSorting}
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'w-full mb-20':
|
||||
isResponseSuccess(incomingSapronakSummaries) &&
|
||||
incomingSapronakSummaries?.data?.length === 0,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
{isLoadingIncomingSapronakSummaries ? (
|
||||
<SapronakClosingSkeleton type='incoming' />
|
||||
) : isResponseSuccess(incomingSapronakSummaries) &&
|
||||
incomingSapronakSummaries.data.length === 0 ? (
|
||||
<SapronakClosingSkeleton
|
||||
type='incoming'
|
||||
iconName='heroicons:chart-bar'
|
||||
title='Ringkasan Sapronak Masuk Tidak Ditemukan'
|
||||
subtitle='Tidak ada ringkasan sapronak masuk untuk periode ini.'
|
||||
/>
|
||||
) : (
|
||||
<Table<ClosingIncomingSapronakSummary>
|
||||
data={
|
||||
isResponseSuccess(incomingSapronakSummaries)
|
||||
? incomingSapronakSummaries?.data
|
||||
: []
|
||||
}
|
||||
columns={incomingSapronaksColumns}
|
||||
pageSize={tableFilterState.pageSize}
|
||||
onPageSizeChange={setPageSize}
|
||||
rowOptions={[10, 20, 50, 100]}
|
||||
page={
|
||||
isResponseSuccess(incomingSapronakSummaries)
|
||||
? incomingSapronakSummaries?.meta?.page
|
||||
: 0
|
||||
}
|
||||
totalItems={
|
||||
isResponseSuccess(incomingSapronakSummaries)
|
||||
? incomingSapronakSummaries?.meta?.total_results
|
||||
: 0
|
||||
}
|
||||
onPageChange={setPage}
|
||||
isLoading={isLoadingIncomingSapronakSummaries}
|
||||
sorting={sorting}
|
||||
setSorting={setSorting}
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'w-full mb-20':
|
||||
isResponseSuccess(incomingSapronakSummaries) &&
|
||||
incomingSapronakSummaries?.data?.length === 0,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Collapse>
|
||||
</Card>
|
||||
|
||||
@@ -16,6 +16,7 @@ import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { ClosingApi } from '@/services/api/closing';
|
||||
import { ClosingIncomingSapronak } from '@/types/api/closing';
|
||||
import SapronakClosingSkeleton from '@/components/pages/closing/skeleton/SapronakClosingSkeleton';
|
||||
|
||||
interface ClosingIncomingSapronaksTableProps {
|
||||
projectFlockId: number;
|
||||
@@ -167,40 +168,52 @@ const ClosingIncomingSapronaksTable = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Table<ClosingIncomingSapronak>
|
||||
data={
|
||||
isResponseSuccess(incomingSapronaks)
|
||||
? incomingSapronaks?.data
|
||||
: []
|
||||
}
|
||||
columns={incomingSapronaksColumns}
|
||||
pageSize={tableFilterState.pageSize}
|
||||
onPageSizeChange={setPageSize}
|
||||
rowOptions={[10, 20, 50, 100]}
|
||||
page={
|
||||
isResponseSuccess(incomingSapronaks)
|
||||
? incomingSapronaks?.meta?.page
|
||||
: 0
|
||||
}
|
||||
totalItems={
|
||||
isResponseSuccess(incomingSapronaks)
|
||||
? incomingSapronaks?.meta?.total_results
|
||||
: 0
|
||||
}
|
||||
onPageChange={setPage}
|
||||
isLoading={isLoadingIncomingSapronaks}
|
||||
sorting={sorting}
|
||||
setSorting={setSorting}
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'w-full mb-20':
|
||||
isResponseSuccess(incomingSapronaks) &&
|
||||
incomingSapronaks?.data?.length === 0,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
{isLoadingIncomingSapronaks ? (
|
||||
<SapronakClosingSkeleton type='incoming' />
|
||||
) : isResponseSuccess(incomingSapronaks) &&
|
||||
incomingSapronaks.data.length === 0 ? (
|
||||
<SapronakClosingSkeleton
|
||||
type='incoming'
|
||||
iconName='heroicons:chart-bar'
|
||||
title='Data Sapronak Masuk Tidak Ditemukan'
|
||||
subtitle='Tidak ada data sapronak masuk untuk periode ini.'
|
||||
/>
|
||||
) : (
|
||||
<Table<ClosingIncomingSapronak>
|
||||
data={
|
||||
isResponseSuccess(incomingSapronaks)
|
||||
? incomingSapronaks?.data
|
||||
: []
|
||||
}
|
||||
columns={incomingSapronaksColumns}
|
||||
pageSize={tableFilterState.pageSize}
|
||||
onPageSizeChange={setPageSize}
|
||||
rowOptions={[10, 20, 50, 100]}
|
||||
page={
|
||||
isResponseSuccess(incomingSapronaks)
|
||||
? incomingSapronaks?.meta?.page
|
||||
: 0
|
||||
}
|
||||
totalItems={
|
||||
isResponseSuccess(incomingSapronaks)
|
||||
? incomingSapronaks?.meta?.total_results
|
||||
: 0
|
||||
}
|
||||
onPageChange={setPage}
|
||||
isLoading={isLoadingIncomingSapronaks}
|
||||
sorting={sorting}
|
||||
setSorting={setSorting}
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'w-full mb-20':
|
||||
isResponseSuccess(incomingSapronaks) &&
|
||||
incomingSapronaks?.data?.length === 0,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Collapse>
|
||||
</Card>
|
||||
|
||||
@@ -15,6 +15,7 @@ import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { ClosingApi } from '@/services/api/closing';
|
||||
import { ClosingOutgoingSapronakSummary } from '@/types/api/closing';
|
||||
import SapronakClosingSkeleton from '@/components/pages/closing/skeleton/SapronakClosingSkeleton';
|
||||
|
||||
interface ClosingOutgoingSapronaksSummaryTableProps {
|
||||
projectFlockId: number;
|
||||
@@ -131,40 +132,52 @@ const ClosingOutgoingSapronaksSummaryTable = ({
|
||||
titleClassName='w-full p-0!'
|
||||
>
|
||||
<div className='w-full p-0'>
|
||||
<Table<ClosingOutgoingSapronakSummary>
|
||||
data={
|
||||
isResponseSuccess(outgoingSapronakSummaries)
|
||||
? outgoingSapronakSummaries?.data
|
||||
: []
|
||||
}
|
||||
columns={outgoingSapronaksColumns}
|
||||
pageSize={tableFilterState.pageSize}
|
||||
onPageSizeChange={setPageSize}
|
||||
rowOptions={[10, 20, 50, 100]}
|
||||
page={
|
||||
isResponseSuccess(outgoingSapronakSummaries)
|
||||
? outgoingSapronakSummaries?.meta?.page
|
||||
: 0
|
||||
}
|
||||
totalItems={
|
||||
isResponseSuccess(outgoingSapronakSummaries)
|
||||
? outgoingSapronakSummaries?.meta?.total_results
|
||||
: 0
|
||||
}
|
||||
onPageChange={setPage}
|
||||
isLoading={isLoadingOutgoingSapronakSummaries}
|
||||
sorting={sorting}
|
||||
setSorting={setSorting}
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'w-full mb-20':
|
||||
isResponseSuccess(outgoingSapronakSummaries) &&
|
||||
outgoingSapronakSummaries?.data?.length === 0,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
{isLoadingOutgoingSapronakSummaries ? (
|
||||
<SapronakClosingSkeleton type='outgoing' />
|
||||
) : isResponseSuccess(outgoingSapronakSummaries) &&
|
||||
outgoingSapronakSummaries.data.length === 0 ? (
|
||||
<SapronakClosingSkeleton
|
||||
type='outgoing'
|
||||
iconName='heroicons:chart-bar'
|
||||
title='Ringkasan Sapronak Keluar Tidak Ditemukan'
|
||||
subtitle='Tidak ada ringkasan sapronak keluar untuk periode ini.'
|
||||
/>
|
||||
) : (
|
||||
<Table<ClosingOutgoingSapronakSummary>
|
||||
data={
|
||||
isResponseSuccess(outgoingSapronakSummaries)
|
||||
? outgoingSapronakSummaries?.data
|
||||
: []
|
||||
}
|
||||
columns={outgoingSapronaksColumns}
|
||||
pageSize={tableFilterState.pageSize}
|
||||
onPageSizeChange={setPageSize}
|
||||
rowOptions={[10, 20, 50, 100]}
|
||||
page={
|
||||
isResponseSuccess(outgoingSapronakSummaries)
|
||||
? outgoingSapronakSummaries?.meta?.page
|
||||
: 0
|
||||
}
|
||||
totalItems={
|
||||
isResponseSuccess(outgoingSapronakSummaries)
|
||||
? outgoingSapronakSummaries?.meta?.total_results
|
||||
: 0
|
||||
}
|
||||
onPageChange={setPage}
|
||||
isLoading={isLoadingOutgoingSapronakSummaries}
|
||||
sorting={sorting}
|
||||
setSorting={setSorting}
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'w-full mb-20':
|
||||
isResponseSuccess(outgoingSapronakSummaries) &&
|
||||
outgoingSapronakSummaries?.data?.length === 0,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Collapse>
|
||||
</Card>
|
||||
|
||||
@@ -16,6 +16,7 @@ import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { ClosingApi } from '@/services/api/closing';
|
||||
import { ClosingOutgoingSapronak } from '@/types/api/closing';
|
||||
import SapronakClosingSkeleton from '@/components/pages/closing/skeleton/SapronakClosingSkeleton';
|
||||
|
||||
interface ClosingOutgoingSapronaksTableProps {
|
||||
projectFlockId: number;
|
||||
@@ -167,40 +168,52 @@ const ClosingOutgoingSapronaksTable = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Table<ClosingOutgoingSapronak>
|
||||
data={
|
||||
isResponseSuccess(outgoingSapronaks)
|
||||
? outgoingSapronaks?.data
|
||||
: []
|
||||
}
|
||||
columns={outgoingSapronaksColumns}
|
||||
pageSize={tableFilterState.pageSize}
|
||||
onPageSizeChange={setPageSize}
|
||||
rowOptions={[10, 20, 50, 100]}
|
||||
page={
|
||||
isResponseSuccess(outgoingSapronaks)
|
||||
? outgoingSapronaks?.meta?.page
|
||||
: 0
|
||||
}
|
||||
totalItems={
|
||||
isResponseSuccess(outgoingSapronaks)
|
||||
? outgoingSapronaks?.meta?.total_results
|
||||
: 0
|
||||
}
|
||||
onPageChange={setPage}
|
||||
isLoading={isLoadingOutgoingSapronaks}
|
||||
sorting={sorting}
|
||||
setSorting={setSorting}
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'w-full mb-20':
|
||||
isResponseSuccess(outgoingSapronaks) &&
|
||||
outgoingSapronaks?.data?.length === 0,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
{isLoadingOutgoingSapronaks ? (
|
||||
<SapronakClosingSkeleton type='outgoing' />
|
||||
) : isResponseSuccess(outgoingSapronaks) &&
|
||||
outgoingSapronaks.data.length === 0 ? (
|
||||
<SapronakClosingSkeleton
|
||||
type='outgoing'
|
||||
iconName='heroicons:chart-bar'
|
||||
title='Data Sapronak Keluar Tidak Ditemukan'
|
||||
subtitle='Tidak ada data sapronak keluar untuk periode ini.'
|
||||
/>
|
||||
) : (
|
||||
<Table<ClosingOutgoingSapronak>
|
||||
data={
|
||||
isResponseSuccess(outgoingSapronaks)
|
||||
? outgoingSapronaks?.data
|
||||
: []
|
||||
}
|
||||
columns={outgoingSapronaksColumns}
|
||||
pageSize={tableFilterState.pageSize}
|
||||
onPageSizeChange={setPageSize}
|
||||
rowOptions={[10, 20, 50, 100]}
|
||||
page={
|
||||
isResponseSuccess(outgoingSapronaks)
|
||||
? outgoingSapronaks?.meta?.page
|
||||
: 0
|
||||
}
|
||||
totalItems={
|
||||
isResponseSuccess(outgoingSapronaks)
|
||||
? outgoingSapronaks?.meta?.total_results
|
||||
: 0
|
||||
}
|
||||
onPageChange={setPage}
|
||||
isLoading={isLoadingOutgoingSapronaks}
|
||||
sorting={sorting}
|
||||
setSorting={setSorting}
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'w-full mb-20':
|
||||
isResponseSuccess(outgoingSapronaks) &&
|
||||
outgoingSapronaks?.data?.length === 0,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Collapse>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user