From d085b18788e2b745d6062916335bb3ed8aa36ff8 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 18 Feb 2026 15:40:06 +0700 Subject: [PATCH 01/35] refactor(FE): Refactor folder structure for closing-related components --- src/components/pages/closing/ClosingDetail.tsx | 12 ++++++------ src/components/pages/closing/ClosingsTable.tsx | 2 +- .../closing/{ => finance}/ClosingFinanceTable.tsx | 0 .../closing/{ => overhead}/ClosingOverheadTable.tsx | 0 .../ClosingSapronakCalculationTable.tsx | 0 .../ClosingIncomingSapronaksSummaryTable.tsx | 0 .../{ => sapronak}/ClosingIncomingSapronaksTable.tsx | 0 .../ClosingOutgoingSapronaksSummaryTable.tsx | 0 .../{ => sapronak}/ClosingOutgoingSapronaksTable.tsx | 0 .../closing/{ => tab}/ClosingFinanceTabContent.tsx | 2 +- .../closing/{ => tab}/ClosingOverheadTabContent.tsx | 2 +- .../{ => tab}/ClosingProductionDataTabContent.tsx | 0 .../ClosingSapronakCalculationTabContent.tsx | 2 +- .../closing/{ => tab}/ClosingSapronakTabContent.tsx | 8 ++++---- 14 files changed, 14 insertions(+), 14 deletions(-) rename src/components/pages/closing/{ => finance}/ClosingFinanceTable.tsx (100%) rename src/components/pages/closing/{ => overhead}/ClosingOverheadTable.tsx (100%) rename src/components/pages/closing/{ => sapronak-calculation}/ClosingSapronakCalculationTable.tsx (100%) rename src/components/pages/closing/{ => sapronak}/ClosingIncomingSapronaksSummaryTable.tsx (100%) rename src/components/pages/closing/{ => sapronak}/ClosingIncomingSapronaksTable.tsx (100%) rename src/components/pages/closing/{ => sapronak}/ClosingOutgoingSapronaksSummaryTable.tsx (100%) rename src/components/pages/closing/{ => sapronak}/ClosingOutgoingSapronaksTable.tsx (100%) rename src/components/pages/closing/{ => tab}/ClosingFinanceTabContent.tsx (77%) rename src/components/pages/closing/{ => tab}/ClosingOverheadTabContent.tsx (89%) rename src/components/pages/closing/{ => tab}/ClosingProductionDataTabContent.tsx (100%) rename src/components/pages/closing/{ => tab}/ClosingSapronakCalculationTabContent.tsx (92%) rename src/components/pages/closing/{ => tab}/ClosingSapronakTabContent.tsx (77%) diff --git a/src/components/pages/closing/ClosingDetail.tsx b/src/components/pages/closing/ClosingDetail.tsx index c3c91a5a..51f7618d 100644 --- a/src/components/pages/closing/ClosingDetail.tsx +++ b/src/components/pages/closing/ClosingDetail.tsx @@ -6,17 +6,17 @@ import { Icon } from '@iconify/react'; import Button from '@/components/Button'; import Tabs from '@/components/Tabs'; import ClosingGeneralInformationTable from '@/components/pages/closing/ClosingGeneralInformationTable'; -import ClosingSapronakTabContent from '@/components/pages/closing/ClosingSapronakTabContent'; -import ClosingProductionDataTabContent from '@/components/pages/closing/ClosingProductionDataTabContent'; +import ClosingSapronakTabContent from '@/components/pages/closing/tab/ClosingSapronakTabContent'; +import ClosingProductionDataTabContent from '@/components/pages/closing/tab/ClosingProductionDataTabContent'; import { ClosingGeneralInformation, BaseClosingSales, ClosingHppExpedition, } from '@/types/api/closing'; -import ClosingSapronakCalculationTabContent from '@/components/pages/closing/ClosingSapronakCalculationTabContent'; -import ClosingOverheadTabContent from '@/components/pages/closing/ClosingOverheadTabContent'; -import ClosingFinanceTabContent from '@/components/pages/closing/ClosingFinanceTabContent'; +import ClosingSapronakCalculationTabContent from '@/components/pages/closing/tab/ClosingSapronakCalculationTabContent'; +import ClosingOverheadTabContent from '@/components/pages/closing/tab/ClosingOverheadTabContent'; +import ClosingFinanceTabContent from '@/components/pages/closing/tab/ClosingFinanceTabContent'; import SalesReportTable from '@/components/pages/closing/sale/SalesReportTable'; import HppExpeditionReportTable from './hpp-ekspedisi/HppExpeditionReportTable'; import ClosingKandangList from '@/components/pages/closing/ClosingKandangList'; @@ -96,7 +96,7 @@ const ClosingDetail: React.FC = ({ return ( <> -
+
diff --git a/src/components/pages/closing/ClosingGeneralInformationTable.tsx b/src/components/pages/closing/table/ClosingGeneralInformationTable.tsx similarity index 100% rename from src/components/pages/closing/ClosingGeneralInformationTable.tsx rename to src/components/pages/closing/table/ClosingGeneralInformationTable.tsx diff --git a/src/stores/closing/closing-tab.store.ts b/src/stores/closing/closing-tab.store.ts new file mode 100644 index 00000000..1f81c26a --- /dev/null +++ b/src/stores/closing/closing-tab.store.ts @@ -0,0 +1,21 @@ +'use client'; + +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { + createClosingTabSlice, + ClosingTabSlice, +} from '@/stores/closing/slices/closing-tab.slice'; + +export type ClosingTabStore = ClosingTabSlice; + +export const useClosingTabStore = create()( + devtools( + (...args) => ({ + ...createClosingTabSlice(...args), + }), + { + name: 'ClosingTabStore', + } + ) +); diff --git a/src/stores/closing/slices/closing-tab.slice.ts b/src/stores/closing/slices/closing-tab.slice.ts new file mode 100644 index 00000000..cd47bbdc --- /dev/null +++ b/src/stores/closing/slices/closing-tab.slice.ts @@ -0,0 +1,37 @@ +import { ReactNode } from 'react'; +import { StateCreator } from 'zustand'; + +export type ClosingTabSlice = { + // State - actions per tab ID + tabActions: Record; + + // Actions + setTabActions: (tabId: string, actions: ReactNode) => void; + clearTabActions: (tabId: string) => void; + clearAllTabActions: () => void; +}; + +export const createClosingTabSlice: StateCreator< + ClosingTabSlice, + [], + [], + ClosingTabSlice +> = (set) => ({ + tabActions: {}, + + setTabActions: (tabId, actions) => + set((state) => ({ + tabActions: { + ...state.tabActions, + [tabId]: actions, + }, + })), + + clearTabActions: (tabId) => + set((state) => { + const { [tabId]: _, ...rest } = state.tabActions; + return { tabActions: rest }; + }), + + clearAllTabActions: () => set({ tabActions: {} }), +}); From 32354e3c2df7cb1ce66b4573675bf9d22c19dac0 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 18 Feb 2026 16:32:26 +0700 Subject: [PATCH 05/35] refactor(FE): Adjust padding on tab header wrapper in ClosingDetailTabs --- src/components/pages/closing/ClosingDetailTabs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/pages/closing/ClosingDetailTabs.tsx b/src/components/pages/closing/ClosingDetailTabs.tsx index 2ec3fe1e..2a5fa638 100644 --- a/src/components/pages/closing/ClosingDetailTabs.tsx +++ b/src/components/pages/closing/ClosingDetailTabs.tsx @@ -136,7 +136,7 @@ const ClosingDetail: React.FC = ({ variant='boxed' className={{ tabHeaderWrapper: - 'justify-between items-center p-3 border-b border-base-content/10', + 'justify-between items-center py-3 border-b border-base-content/10', tab: 'w-fit', content: 'p-0 m-0', }} From 0235494d46c28ca408061243a564d34d1b13d479 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 09:29:01 +0700 Subject: [PATCH 06/35] refactor(FE): Refactor HPP Expedition handling in ClosingDetailPage --- src/app/closing/detail/page.tsx | 21 --------- .../pages/closing/ClosingDetailTabs.tsx | 9 +--- .../closing/tab/HppExpeditionClosingTab.tsx | 19 ++++++++ .../table/HppExpeditionClosingTable.tsx | 43 ++++++++++++++----- 4 files changed, 54 insertions(+), 38 deletions(-) create mode 100644 src/components/pages/closing/tab/HppExpeditionClosingTab.tsx diff --git a/src/app/closing/detail/page.tsx b/src/app/closing/detail/page.tsx index 8f164f44..d081951c 100644 --- a/src/app/closing/detail/page.tsx +++ b/src/app/closing/detail/page.tsx @@ -46,21 +46,6 @@ const ClosingDetailPage = () => { : ClosingApi.getPenjualan(Number(closingId)) ); - const { data: hppEkspedisiData, isLoading: isLoadingHppEkspedisi } = useSWR( - kandangId - ? `hpp-ekspedisi-${closingId}-${kandangId}` - : closingId - ? `hpp-ekspedisi-${closingId}` - : null, - () => - kandangId - ? ClosingApi.getHppEkspedisiByKandang( - Number(closingId), - Number(kandangId) - ) - : ClosingApi.getHppEkspedisi(Number(closingId)) - ); - if (!closingId) { router.back(); @@ -79,7 +64,6 @@ const ClosingDetailPage = () => { const isLoading = isLoadingClosing || isLoadingSales || - isLoadingHppEkspedisi || isLoadingProject || isLoadingKandang; @@ -92,11 +76,6 @@ const ClosingDetailPage = () => { id={Number(closingId)} initialValue={closing.data} salesData={isResponseSuccess(salesData) ? salesData.data : undefined} - hppExpeditionData={ - isResponseSuccess(hppEkspedisiData) - ? hppEkspedisiData.data - : undefined - } projectData={ isResponseSuccess(projectData) ? projectData.data : undefined } diff --git a/src/components/pages/closing/ClosingDetailTabs.tsx b/src/components/pages/closing/ClosingDetailTabs.tsx index 2a5fa638..94340283 100644 --- a/src/components/pages/closing/ClosingDetailTabs.tsx +++ b/src/components/pages/closing/ClosingDetailTabs.tsx @@ -12,13 +12,12 @@ import ProductionDataClosingTab from '@/components/pages/closing/tab/ProductionD import { ClosingGeneralInformation, BaseClosingSales, - ClosingHppExpedition, } from '@/types/api/closing'; import SapronakCalculationClosingTab from '@/components/pages/closing/tab/SapronakCalculationClosingTab'; import OverheadClosingTab from '@/components/pages/closing/tab/OverheadClosingTab'; import FinanceClosingTab from '@/components/pages/closing/tab/FinanceClosingTab'; import SalesClosingTable from '@/components/pages/closing/table/SalesClosingTable'; -import HppExpeditionClosingTable from './table/HppExpeditionClosingTable'; +import HppExpeditionClosingTab from '@/components/pages/closing/tab/HppExpeditionClosingTab'; import ClosingKandangList from '@/components/pages/closing/ClosingKandangList'; import { ProjectFlock } from '@/types/api/production/project-flock'; import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang'; @@ -27,7 +26,6 @@ interface ClosingDetailProps { id: number; initialValue?: ClosingGeneralInformation; salesData?: BaseClosingSales; - hppExpeditionData?: ClosingHppExpedition; projectData?: ProjectFlock; kandangData?: ProjectFlockKandang; } @@ -36,7 +34,6 @@ const ClosingDetail: React.FC = ({ id, initialValue, salesData, - hppExpeditionData, projectData, kandangData, }) => { @@ -79,9 +76,7 @@ const ClosingDetail: React.FC = ({ { id: 'hppEkspedisi', label: 'HPP Ekspedisi', - content: ( - - ), + content: , }, { id: 'dataProduksi', diff --git a/src/components/pages/closing/tab/HppExpeditionClosingTab.tsx b/src/components/pages/closing/tab/HppExpeditionClosingTab.tsx new file mode 100644 index 00000000..ad7f0ec1 --- /dev/null +++ b/src/components/pages/closing/tab/HppExpeditionClosingTab.tsx @@ -0,0 +1,19 @@ +import HppExpeditionClosingTable from '@/components/pages/closing/table/HppExpeditionClosingTable'; + +interface HppExpeditionClosingTabProps { + projectFlockId: number; +} + +const HppExpeditionClosingTab = ({ + projectFlockId, +}: HppExpeditionClosingTabProps) => { + return ( +
+ {projectFlockId && ( + + )} +
+ ); +}; + +export default HppExpeditionClosingTab; diff --git a/src/components/pages/closing/table/HppExpeditionClosingTable.tsx b/src/components/pages/closing/table/HppExpeditionClosingTable.tsx index 2229180e..6c19c2a8 100644 --- a/src/components/pages/closing/table/HppExpeditionClosingTable.tsx +++ b/src/components/pages/closing/table/HppExpeditionClosingTable.tsx @@ -5,27 +5,49 @@ import { ColumnDef } from '@tanstack/react-table'; import Table from '@/components/Table'; import Card from '@/components/Card'; import { formatCurrency } from '@/lib/helper'; -import { BaseHppExpedition, BaseExpeditionCost } from '@/types/api/closing'; +import { isResponseSuccess } from '@/lib/api-helper'; +import { BaseExpeditionCost } from '@/types/api/closing'; +import { ClosingApi } from '@/services/api/closing'; +import { useSearchParams } from 'next/navigation'; +import useSWR from 'swr'; interface HppExpeditionClosingTableProps { - type?: 'detail'; - initialValues?: BaseHppExpedition; + projectFlockId: number; } const HppExpeditionClosingTable = ({ - initialValues, + projectFlockId, }: HppExpeditionClosingTableProps) => { + const searchParams = useSearchParams(); + const kandangId = searchParams.get('kandangId'); + + const { data: hppExpedition, isLoading } = useSWR( + kandangId + ? `/closing/hpp-expedition/${projectFlockId}/${kandangId}` + : `/closing/hpp-expedition/${projectFlockId}`, + () => + kandangId + ? ClosingApi.getHppEkspedisiByKandang(projectFlockId, Number(kandangId)) + : ClosingApi.getHppEkspedisi(projectFlockId) + ); + const costOfRevenueExpeditionData: BaseExpeditionCost[] = useMemo(() => { - return initialValues?.expedition_costs || []; - }, [initialValues]); + if (isResponseSuccess(hppExpedition)) { + return hppExpedition.data.expedition_costs || []; + } + return []; + }, [hppExpedition]); const totals = useMemo(() => { - const totalHpp = initialValues?.total_hpp_amount || 0; - + if (isResponseSuccess(hppExpedition)) { + return { + totalHpp: hppExpedition.data.total_hpp_amount || 0, + }; + } return { - totalHpp, + totalHpp: 0, }; - }, [initialValues]); + }, [hppExpedition]); const costOfRevenueExpeditionColumns: ColumnDef[] = useMemo( @@ -81,6 +103,7 @@ const HppExpeditionClosingTable = ({ 0} className={{ tableWrapperClassName: 'overflow-x-auto', From d9bd73d8c1e88eee02fad0bc1a2954bdbf6daa24 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 09:32:33 +0700 Subject: [PATCH 07/35] refactor(FE): Refactor sales data fetching and component structure --- src/app/closing/detail/page.tsx | 18 +------- .../pages/closing/ClosingDetailTabs.tsx | 9 ++-- .../pages/closing/tab/SalesClosingTab.tsx | 19 +++++++++ .../pages/closing/table/SalesClosingTable.tsx | 42 +++++++++++++++---- 4 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 src/components/pages/closing/tab/SalesClosingTab.tsx diff --git a/src/app/closing/detail/page.tsx b/src/app/closing/detail/page.tsx index d081951c..c8d5c47e 100644 --- a/src/app/closing/detail/page.tsx +++ b/src/app/closing/detail/page.tsx @@ -34,18 +34,6 @@ const ClosingDetailPage = () => { () => ProjectFlockKandangApi.getSingle(Number(kandangId)) ); - const { data: salesData, isLoading: isLoadingSales } = useSWR( - kandangId - ? `sales-${closingId}-${kandangId}` - : closingId - ? `sales-${closingId}` - : null, - () => - kandangId - ? ClosingApi.getPenjualanByKandang(Number(closingId), Number(kandangId)) - : ClosingApi.getPenjualan(Number(closingId)) - ); - if (!closingId) { router.back(); @@ -62,10 +50,7 @@ const ClosingDetailPage = () => { } const isLoading = - isLoadingClosing || - isLoadingSales || - isLoadingProject || - isLoadingKandang; + isLoadingClosing || isLoadingProject || isLoadingKandang; return (
@@ -75,7 +60,6 @@ const ClosingDetailPage = () => { = ({ id, initialValue, - salesData, projectData, kandangData, }) => { @@ -60,7 +57,7 @@ const ClosingDetail: React.FC = ({ { id: 'penjualan', label: 'Penjualan', - content: , + content: , }, { id: 'overhead', @@ -91,7 +88,7 @@ const ClosingDetail: React.FC = ({ ]; return validTabs; - }, [initialValue, salesData, kandangData, id]); + }, [initialValue, kandangData, id]); return ( <> diff --git a/src/components/pages/closing/tab/SalesClosingTab.tsx b/src/components/pages/closing/tab/SalesClosingTab.tsx new file mode 100644 index 00000000..23ec720b --- /dev/null +++ b/src/components/pages/closing/tab/SalesClosingTab.tsx @@ -0,0 +1,19 @@ +import SalesClosingTable from '@/components/pages/closing/table/SalesClosingTable'; + +interface SalesClosingTabProps { + projectFlockId: number; +} + +const SalesClosingTab = ({ + projectFlockId, +}: SalesClosingTabProps) => { + return ( +
+ {projectFlockId && ( + + )} +
+ ); +}; + +export default SalesClosingTab; diff --git a/src/components/pages/closing/table/SalesClosingTable.tsx b/src/components/pages/closing/table/SalesClosingTable.tsx index b30cfb4a..6ad716b2 100644 --- a/src/components/pages/closing/table/SalesClosingTable.tsx +++ b/src/components/pages/closing/table/SalesClosingTable.tsx @@ -5,6 +5,7 @@ import { ColumnDef } from '@tanstack/react-table'; import Table from '@/components/Table'; import Card from '@/components/Card'; import { formatCurrency, formatNumber, formatDate } from '@/lib/helper'; +import { isResponseSuccess } from '@/lib/api-helper'; import { BaseClosingSales, BaseSales, @@ -13,20 +14,46 @@ import { import { Product } from '@/types/api/master-data/product'; import { Customer } from '@/types/api/master-data/customer'; import { Kandang } from '@/types/api/master-data/kandang'; +import { ClosingApi } from '@/services/api/closing'; +import { useSearchParams } from 'next/navigation'; +import useSWR from 'swr'; interface SalesClosingTableProps { - type?: 'detail'; - initialValues?: BaseClosingSales; + projectFlockId: number; } -const SalesClosingTable = ({ initialValues }: SalesClosingTableProps) => { +const SalesClosingTable = ({ + projectFlockId, +}: SalesClosingTableProps) => { + const searchParams = useSearchParams(); + const kandangId = searchParams.get('kandangId'); + + const { data: sales, isLoading } = useSWR( + kandangId + ? `/closing/sales/${projectFlockId}/${kandangId}` + : `/closing/sales/${projectFlockId}`, + () => + kandangId + ? ClosingApi.getPenjualanByKandang( + projectFlockId, + Number(kandangId) + ) + : ClosingApi.getPenjualan(projectFlockId) + ); + const salesData: BaseSales[] = useMemo(() => { - return initialValues?.sales || []; - }, [initialValues]); + if (isResponseSuccess(sales)) { + return sales.data.sales || []; + } + return []; + }, [sales]); const summary: ClosingSalesSummary | undefined = useMemo(() => { - return initialValues?.summary; - }, [initialValues]); + if (isResponseSuccess(sales)) { + return sales.data.summary; + } + return undefined; + }, [sales]); const totals = useMemo(() => { if (salesData.length === 0) { @@ -306,6 +333,7 @@ const SalesClosingTable = ({ initialValues }: SalesClosingTableProps) => {
0} className={{ tableWrapperClassName: 'overflow-x-auto', From 1fe722cb81362f371d3e251f2fdf991a993b88ea Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 09:33:31 +0700 Subject: [PATCH 08/35] refactor(FE): Refactor code formatting for consistency and readability --- src/app/closing/detail/page.tsx | 3 +-- src/components/pages/closing/ClosingDetailTabs.tsx | 4 +--- src/components/pages/closing/tab/SalesClosingTab.tsx | 8 ++------ src/components/pages/closing/table/SalesClosingTable.tsx | 9 ++------- 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/app/closing/detail/page.tsx b/src/app/closing/detail/page.tsx index c8d5c47e..96487258 100644 --- a/src/app/closing/detail/page.tsx +++ b/src/app/closing/detail/page.tsx @@ -49,8 +49,7 @@ const ClosingDetailPage = () => { return; } - const isLoading = - isLoadingClosing || isLoadingProject || isLoadingKandang; + const isLoading = isLoadingClosing || isLoadingProject || isLoadingKandang; return (
diff --git a/src/components/pages/closing/ClosingDetailTabs.tsx b/src/components/pages/closing/ClosingDetailTabs.tsx index 3b9e7dce..59fea1ba 100644 --- a/src/components/pages/closing/ClosingDetailTabs.tsx +++ b/src/components/pages/closing/ClosingDetailTabs.tsx @@ -9,9 +9,7 @@ import ClosingGeneralInformationTable from '@/components/pages/closing/table/Clo import SapronakClosingTab from '@/components/pages/closing/tab/SapronakClosingTab'; import ProductionDataClosingTab from '@/components/pages/closing/tab/ProductionDataClosingTab'; -import { - ClosingGeneralInformation, -} from '@/types/api/closing'; +import { ClosingGeneralInformation } from '@/types/api/closing'; import SapronakCalculationClosingTab from '@/components/pages/closing/tab/SapronakCalculationClosingTab'; import OverheadClosingTab from '@/components/pages/closing/tab/OverheadClosingTab'; import FinanceClosingTab from '@/components/pages/closing/tab/FinanceClosingTab'; diff --git a/src/components/pages/closing/tab/SalesClosingTab.tsx b/src/components/pages/closing/tab/SalesClosingTab.tsx index 23ec720b..ee343da0 100644 --- a/src/components/pages/closing/tab/SalesClosingTab.tsx +++ b/src/components/pages/closing/tab/SalesClosingTab.tsx @@ -4,14 +4,10 @@ interface SalesClosingTabProps { projectFlockId: number; } -const SalesClosingTab = ({ - projectFlockId, -}: SalesClosingTabProps) => { +const SalesClosingTab = ({ projectFlockId }: SalesClosingTabProps) => { return (
- {projectFlockId && ( - - )} + {projectFlockId && }
); }; diff --git a/src/components/pages/closing/table/SalesClosingTable.tsx b/src/components/pages/closing/table/SalesClosingTable.tsx index 6ad716b2..0d8a31ba 100644 --- a/src/components/pages/closing/table/SalesClosingTable.tsx +++ b/src/components/pages/closing/table/SalesClosingTable.tsx @@ -22,9 +22,7 @@ interface SalesClosingTableProps { projectFlockId: number; } -const SalesClosingTable = ({ - projectFlockId, -}: SalesClosingTableProps) => { +const SalesClosingTable = ({ projectFlockId }: SalesClosingTableProps) => { const searchParams = useSearchParams(); const kandangId = searchParams.get('kandangId'); @@ -34,10 +32,7 @@ const SalesClosingTable = ({ : `/closing/sales/${projectFlockId}`, () => kandangId - ? ClosingApi.getPenjualanByKandang( - projectFlockId, - Number(kandangId) - ) + ? ClosingApi.getPenjualanByKandang(projectFlockId, Number(kandangId)) : ClosingApi.getPenjualan(projectFlockId) ); From c53430fa1fec86f2186db2227b23ae7780159072 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 09:35:42 +0700 Subject: [PATCH 09/35] refactor(FE): Organize Sapronak tables into a dedicated folder --- src/components/pages/closing/tab/SapronakClosingTab.tsx | 8 ++++---- .../{ => sapronak}/IncomingSapronaksSummaryTable.tsx | 0 .../table/{ => sapronak}/IncomingSapronaksTable.tsx | 0 .../{ => sapronak}/OutgoingSapronaksSummaryTable.tsx | 0 .../table/{ => sapronak}/OutgoingSapronaksTable.tsx | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename src/components/pages/closing/table/{ => sapronak}/IncomingSapronaksSummaryTable.tsx (100%) rename src/components/pages/closing/table/{ => sapronak}/IncomingSapronaksTable.tsx (100%) rename src/components/pages/closing/table/{ => sapronak}/OutgoingSapronaksSummaryTable.tsx (100%) rename src/components/pages/closing/table/{ => sapronak}/OutgoingSapronaksTable.tsx (100%) diff --git a/src/components/pages/closing/tab/SapronakClosingTab.tsx b/src/components/pages/closing/tab/SapronakClosingTab.tsx index 8f931671..1a5d26dd 100644 --- a/src/components/pages/closing/tab/SapronakClosingTab.tsx +++ b/src/components/pages/closing/tab/SapronakClosingTab.tsx @@ -1,9 +1,9 @@ 'use client'; -import IncomingSapronaksTable from '@/components/pages/closing/table/IncomingSapronaksTable'; -import OutgoingSapronaksTable from '@/components/pages/closing/table/OutgoingSapronaksTable'; -import IncomingSapronaksSummaryTable from '@/components/pages/closing/table/IncomingSapronaksSummaryTable'; -import ClosingOutgoingSapronaksSummaryTable from '../table/OutgoingSapronaksSummaryTable'; +import IncomingSapronaksTable from '@/components/pages/closing/table/sapronak/IncomingSapronaksTable'; +import OutgoingSapronaksTable from '@/components/pages/closing/table/sapronak/OutgoingSapronaksTable'; +import IncomingSapronaksSummaryTable from '@/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable'; +import ClosingOutgoingSapronaksSummaryTable from '../table/sapronak/OutgoingSapronaksSummaryTable'; interface SapronakClosingTabProps { projectFlockId?: number; diff --git a/src/components/pages/closing/table/IncomingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx similarity index 100% rename from src/components/pages/closing/table/IncomingSapronaksSummaryTable.tsx rename to src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx diff --git a/src/components/pages/closing/table/IncomingSapronaksTable.tsx b/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx similarity index 100% rename from src/components/pages/closing/table/IncomingSapronaksTable.tsx rename to src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx diff --git a/src/components/pages/closing/table/OutgoingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx similarity index 100% rename from src/components/pages/closing/table/OutgoingSapronaksSummaryTable.tsx rename to src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx diff --git a/src/components/pages/closing/table/OutgoingSapronaksTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx similarity index 100% rename from src/components/pages/closing/table/OutgoingSapronaksTable.tsx rename to src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx From 9c953ca3825a873aace5a54deade8f1967d8fd5d Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 09:37:54 +0700 Subject: [PATCH 10/35] refactor(FE): Fix incorrect import and component usage in SapronakClosingTab --- src/components/pages/closing/tab/SapronakClosingTab.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/pages/closing/tab/SapronakClosingTab.tsx b/src/components/pages/closing/tab/SapronakClosingTab.tsx index 1a5d26dd..21bb3b3f 100644 --- a/src/components/pages/closing/tab/SapronakClosingTab.tsx +++ b/src/components/pages/closing/tab/SapronakClosingTab.tsx @@ -3,7 +3,7 @@ import IncomingSapronaksTable from '@/components/pages/closing/table/sapronak/IncomingSapronaksTable'; import OutgoingSapronaksTable from '@/components/pages/closing/table/sapronak/OutgoingSapronaksTable'; import IncomingSapronaksSummaryTable from '@/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable'; -import ClosingOutgoingSapronaksSummaryTable from '../table/sapronak/OutgoingSapronaksSummaryTable'; +import OutgoingSapronaksSummaryTable from '@/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable'; interface SapronakClosingTabProps { projectFlockId?: number; @@ -20,9 +20,7 @@ const SapronakClosingTab = ({ projectFlockId }: SapronakClosingTabProps) => { - + )}
From 8fe19feaac1039f6e47ce8c6a4d0d6bdbbbf81f4 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 10:03:25 +0700 Subject: [PATCH 11/35] feat(FE): Add skeleton components for closing pages --- .../closing/skeleton/ClosingTabSkeleton.tsx | 36 + .../skeleton/FinanceClosingSkeleton.tsx | 40 ++ .../skeleton/HppExpeditionClosingSkeleton.tsx | 44 ++ .../skeleton/OverheadClosingSkeleton.tsx | 72 ++ .../ProductionDataClosingSkeleton.tsx | 33 + .../closing/skeleton/SalesClosingSkeleton.tsx | 84 +++ .../SapronakCalculationClosingSkeleton.tsx | 72 ++ .../skeleton/SapronakClosingSkeleton.tsx | 126 ++++ .../closing/tab/ProductionDataClosingTab.tsx | 15 +- .../closing/table/FinanceClosingTable.tsx | 619 +++++++++--------- .../table/HppExpeditionClosingTable.tsx | 56 +- .../closing/table/OverheadClosingTable.tsx | 178 ++--- .../pages/closing/table/SalesClosingTable.tsx | 52 +- .../table/SapronakCalculationClosingTable.tsx | 124 ++-- .../IncomingSapronaksSummaryTable.tsx | 81 ++- .../table/sapronak/IncomingSapronaksTable.tsx | 81 ++- .../OutgoingSapronaksSummaryTable.tsx | 81 ++- .../table/sapronak/OutgoingSapronaksTable.tsx | 81 ++- 18 files changed, 1262 insertions(+), 613 deletions(-) create mode 100644 src/components/pages/closing/skeleton/ClosingTabSkeleton.tsx create mode 100644 src/components/pages/closing/skeleton/FinanceClosingSkeleton.tsx create mode 100644 src/components/pages/closing/skeleton/HppExpeditionClosingSkeleton.tsx create mode 100644 src/components/pages/closing/skeleton/OverheadClosingSkeleton.tsx create mode 100644 src/components/pages/closing/skeleton/ProductionDataClosingSkeleton.tsx create mode 100644 src/components/pages/closing/skeleton/SalesClosingSkeleton.tsx create mode 100644 src/components/pages/closing/skeleton/SapronakCalculationClosingSkeleton.tsx create mode 100644 src/components/pages/closing/skeleton/SapronakClosingSkeleton.tsx diff --git a/src/components/pages/closing/skeleton/ClosingTabSkeleton.tsx b/src/components/pages/closing/skeleton/ClosingTabSkeleton.tsx new file mode 100644 index 00000000..821ce72a --- /dev/null +++ b/src/components/pages/closing/skeleton/ClosingTabSkeleton.tsx @@ -0,0 +1,36 @@ +import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton'; +import Table from '@/components/Table'; +import { ColumnDef } from '@tanstack/react-table'; + +const ClosingTabSkeleton = ({ + columns, + icon, + title, + subtitle, +}: { + columns: ColumnDef[]; + icon: React.ReactNode; + title: string; + subtitle: string; +}) => { + return ( +
+
[]} + 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', + }} + /> +
+ +
+ + ); +}; + +export default ClosingTabSkeleton; diff --git a/src/components/pages/closing/skeleton/FinanceClosingSkeleton.tsx b/src/components/pages/closing/skeleton/FinanceClosingSkeleton.tsx new file mode 100644 index 00000000..1168710c --- /dev/null +++ b/src/components/pages/closing/skeleton/FinanceClosingSkeleton.tsx @@ -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 ( + +
+ + } + title={title} + description={subtitle} + /> +
+
+ ); +}; + +export default FinanceClosingSkeleton; diff --git a/src/components/pages/closing/skeleton/HppExpeditionClosingSkeleton.tsx b/src/components/pages/closing/skeleton/HppExpeditionClosingSkeleton.tsx new file mode 100644 index 00000000..490839e1 --- /dev/null +++ b/src/components/pages/closing/skeleton/HppExpeditionClosingSkeleton.tsx @@ -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[]; + title?: string; + subtitle?: string; + iconName?: string; +}) => { + const defaultColumns: ColumnDef[] = [ + { + id: 'id', + header: 'No', + }, + { + id: 'expedition_vendor_name', + header: 'Nama Ekspedisi', + }, + { + id: 'hpp_amount', + header: 'HPP Ekspedisi', + }, + ]; + + return ( + + columns={columns || defaultColumns} + icon={ + + } + title={title} + subtitle={subtitle} + /> + ); +}; + +export default HppExpeditionClosingSkeleton; diff --git a/src/components/pages/closing/skeleton/OverheadClosingSkeleton.tsx b/src/components/pages/closing/skeleton/OverheadClosingSkeleton.tsx new file mode 100644 index 00000000..0ac2f4f3 --- /dev/null +++ b/src/components/pages/closing/skeleton/OverheadClosingSkeleton.tsx @@ -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[]; + title?: string; + subtitle?: string; + iconName?: string; +}) => { + const defaultColumns: ColumnDef[] = [ + { + 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 ( + + columns={columns || defaultColumns} + icon={ + + } + title={title} + subtitle={subtitle} + /> + ); +}; + +export default OverheadClosingSkeleton; diff --git a/src/components/pages/closing/skeleton/ProductionDataClosingSkeleton.tsx b/src/components/pages/closing/skeleton/ProductionDataClosingSkeleton.tsx new file mode 100644 index 00000000..e0031394 --- /dev/null +++ b/src/components/pages/closing/skeleton/ProductionDataClosingSkeleton.tsx @@ -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 ( +
+
+ + } + title={title} + description={subtitle} + /> +
+
+ ); +}; + +export default ProductionDataClosingSkeleton; diff --git a/src/components/pages/closing/skeleton/SalesClosingSkeleton.tsx b/src/components/pages/closing/skeleton/SalesClosingSkeleton.tsx new file mode 100644 index 00000000..c0ae3a28 --- /dev/null +++ b/src/components/pages/closing/skeleton/SalesClosingSkeleton.tsx @@ -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[]; + title?: string; + subtitle?: string; + iconName?: string; +}) => { + const defaultColumns: ColumnDef[] = [ + { + 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 ( + + columns={columns || defaultColumns} + icon={ + + } + title={title} + subtitle={subtitle} + /> + ); +}; + +export default SalesClosingSkeleton; diff --git a/src/components/pages/closing/skeleton/SapronakCalculationClosingSkeleton.tsx b/src/components/pages/closing/skeleton/SapronakCalculationClosingSkeleton.tsx new file mode 100644 index 00000000..6c1b9f3c --- /dev/null +++ b/src/components/pages/closing/skeleton/SapronakCalculationClosingSkeleton.tsx @@ -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[]; + title?: string; + subtitle?: string; + iconName?: string; +}) => { + const defaultColumns: ColumnDef[] = [ + { + 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 ( + + columns={columns || defaultColumns} + icon={ + + } + title={title} + subtitle={subtitle} + /> + ); +}; + +export default SapronakCalculationClosingSkeleton; diff --git a/src/components/pages/closing/skeleton/SapronakClosingSkeleton.tsx b/src/components/pages/closing/skeleton/SapronakClosingSkeleton.tsx new file mode 100644 index 00000000..e3ea211b --- /dev/null +++ b/src/components/pages/closing/skeleton/SapronakClosingSkeleton.tsx @@ -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[]; + type?: 'incoming' | 'outgoing'; + title?: string; + subtitle?: string; + iconName?: string; +}) => { + const defaultIncomingColumns: ColumnDef[] = [ + { + 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[] = [ + { + 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 ( + + columns={ + columns || + (type === 'incoming' ? defaultIncomingColumns : defaultOutgoingColumns) + } + icon={ + + } + title={title || defaultTitle} + subtitle={subtitle || defaultSubtitle} + /> + ); +}; + +export default SapronakClosingSkeleton; diff --git a/src/components/pages/closing/tab/ProductionDataClosingTab.tsx b/src/components/pages/closing/tab/ProductionDataClosingTab.tsx index eebccae3..3647dc1f 100644 --- a/src/components/pages/closing/tab/ProductionDataClosingTab.tsx +++ b/src/components/pages/closing/tab/ProductionDataClosingTab.tsx @@ -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 ( -
- -
- ); + return ; } if (!productionData || !isResponseSuccess(productionData)) { return ( -
- Gagal memuat data produksi. -
+ ); } diff --git a/src/components/pages/closing/table/FinanceClosingTable.tsx b/src/components/pages/closing/table/FinanceClosingTable.tsx index 8dce38de..de4d6e47 100644 --- a/src/components/pages/closing/table/FinanceClosingTable.tsx +++ b/src/components/pages/closing/table/FinanceClosingTable.tsx @@ -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 (
- <> - -
-
-
Laba Rugi Brutto
-
- {isResponseSuccess(finance) - ? formatCurrency( - finance.data.profit_loss.summary.gross_profit.amount - ) - : '-'} + {isLoading ? ( + + ) : !isResponseSuccess(finance) ? ( + + ) : ( + <> + +
+
+
Laba Rugi Brutto
+
+ {isResponseSuccess(finance) + ? formatCurrency( + finance.data.profit_loss.summary.gross_profit.amount + ) + : '-'} +
+
+
+
Laba Rugi Netto
+
+ {isResponseSuccess(finance) + ? formatCurrency( + finance.data.profit_loss.summary.net_profit.amount + ) + : '-'} +
-
-
Laba Rugi Netto
-
- {isResponseSuccess(finance) - ? formatCurrency( - finance.data.profit_loss.summary.net_profit.amount - ) - : '-'} -
-
-
- - -
- - 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; + + +
+ + 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 ( -
- - -
- {formatTitleCase(rowData.label ?? '-')} -
- - - ); - } - return null; - }} - renderFooter={isResponseSuccess(finance)} - /> - - - -
- - data={profitLossTableData} - isLoading={isLoading} - columns={[ - { - header: 'Jenis', - enableSorting: false, - accessorFn: (item) => item.label, - cell: (item) => ( -
- {formatTitleCase(item.row.original.label || '-')} -
- ), - footer: () => ( -
LABA RUGI NETTO
- ), - }, - { - header: 'Rp/Ekor', - enableSorting: false, - accessorFn: (item) => formatCurrency(item.rp_per_bird || 0), - footer: () => ( -
- {isResponseSuccess(finance) - ? formatCurrency( - finance.data.profit_loss.summary.net_profit - .rp_per_bird || 0 - ) - : formatCurrency(0)} -
- ), - }, - { - header: 'Rp/Kg', - enableSorting: false, - accessorFn: (item) => formatCurrency(item.rp_per_kg || 0), - footer: () => ( -
- {isResponseSuccess(finance) - ? formatCurrency( - finance.data.profit_loss.summary.net_profit - .rp_per_kg || 0 - ) - : formatCurrency(0)} -
- ), - }, - { - header: 'Jumlah (Rp)', - enableSorting: false, - accessorFn: (item) => formatCurrency(item.amount || 0), - footer: () => ( -
- {isResponseSuccess(finance) - ? formatCurrency( - finance.data.profit_loss.summary.net_profit - .amount || 0 - ) - : formatCurrency(0)} -
- ), - }, - ]} - renderCustomRow={(row) => { - const rowData = row.original; - if (rowData.code === 'custom_row') { - return ( -
- - - - - - ); - } - return null; - }} - className={{ - paginationClassName: 'hidden', - }} - renderFooter={isResponseSuccess(finance)} - /> - - - + + + + ); + } + return null; + }} + renderFooter={isResponseSuccess(finance)} + /> + + + +
+ + data={profitLossTableData} + isLoading={isLoading} + columns={[ + { + header: 'Jenis', + enableSorting: false, + accessorFn: (item) => item.label, + cell: (item) => ( +
+ {formatTitleCase(item.row.original.label || '-')} +
+ ), + footer: () => ( +
LABA RUGI NETTO
+ ), + }, + { + header: 'Rp/Ekor', + enableSorting: false, + accessorFn: (item) => formatCurrency(item.rp_per_bird || 0), + footer: () => ( +
+ {isResponseSuccess(finance) + ? formatCurrency( + finance.data.profit_loss.summary.net_profit + .rp_per_bird || 0 + ) + : formatCurrency(0)} +
+ ), + }, + { + header: 'Rp/Kg', + enableSorting: false, + accessorFn: (item) => formatCurrency(item.rp_per_kg || 0), + footer: () => ( +
+ {isResponseSuccess(finance) + ? formatCurrency( + finance.data.profit_loss.summary.net_profit + .rp_per_kg || 0 + ) + : formatCurrency(0)} +
+ ), + }, + { + header: 'Jumlah (Rp)', + enableSorting: false, + accessorFn: (item) => formatCurrency(item.amount || 0), + footer: () => ( +
+ {isResponseSuccess(finance) + ? formatCurrency( + finance.data.profit_loss.summary.net_profit + .amount || 0 + ) + : formatCurrency(0)} +
+ ), + }, + ]} + renderCustomRow={(row) => { + const rowData = row.original; + if (rowData.code === 'custom_row') { + return ( +
+ + + + + + ); + } + return null; + }} + className={{ + paginationClassName: 'hidden', + }} + renderFooter={isResponseSuccess(finance)} + /> + + + + )} ); }; diff --git a/src/components/pages/closing/table/HppExpeditionClosingTable.tsx b/src/components/pages/closing/table/HppExpeditionClosingTable.tsx index 6c19c2a8..a1ce4a94 100644 --- a/src/components/pages/closing/table/HppExpeditionClosingTable.tsx +++ b/src/components/pages/closing/table/HppExpeditionClosingTable.tsx @@ -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', }} > -
{ + const rowData = row.original; + if (rowData.code === 'custom_row') { + return ( +
-
- {formatTitleCase(rowData.label ?? '-')} -
-
-
- {formatCurrency(rowData.rp_per_bird ?? 0)} -
-
-
- {formatCurrency(rowData.rp_per_kg ?? 0)} -
-
-
- {formatCurrency(rowData.amount ?? 0)} -
-
+
+ {formatTitleCase(rowData.label ?? '-')} +
+
+
+ {formatTitleCase(rowData.label ?? '-')} +
+
+
+ {formatCurrency(rowData.rp_per_bird ?? 0)} +
+
+
+ {formatCurrency(rowData.rp_per_kg ?? 0)} +
+
+
+ {formatCurrency(rowData.amount ?? 0)} +
+
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 ? ( + + ) : costOfRevenueExpeditionData.length === 0 ? ( + + ) : ( +
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', + }} + /> + )} diff --git a/src/components/pages/closing/table/OverheadClosingTable.tsx b/src/components/pages/closing/table/OverheadClosingTable.tsx index f082697a..b1f64f64 100644 --- a/src/components/pages/closing/table/OverheadClosingTable.tsx +++ b/src/components/pages/closing/table/OverheadClosingTable.tsx @@ -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 ( <> - - - data={ - kandangId - ? isResponseSuccess(overheadKandang) - ? (overheadKandang.data?.overheads ?? []) - : [] - : isResponseSuccess(overhead) - ? (overhead.data?.overheads ?? []) - : [] - } + {isLoadingOverhead ? ( + + ) : !isResponseSuccess(overhead) || + (!kandangId && overhead.data?.overheads.length === 0) || + (kandangId && !isResponseSuccess(overheadKandang)) ? ( + 0 - : false - } + iconName='heroicons:chart-bar' + title='Data Overhead Tidak Ditemukan' + subtitle='Tidak ada data overhead untuk periode ini.' /> - {kandangId && ( - + + 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' + ), }} - > -
-
-

Pembelian Kandang

-
-
- -
-
-
- Populasi Akhir KANDANG{' '} - Pemakaian - Di FARM + isLoading={isLoadingOverhead} + renderFooter={ + isResponseSuccess(overhead) + ? overhead.data?.overheads.length > 0 + : false + } + /> + {kandangId && ( + +
+
+

Pembelian Kandang

-
-
- Populasi Akhir Proyek +
+ +
+
+
+ Populasi Akhir KANDANG{' '} + {' '} + Pemakaian Di FARM +
+
+
+ Populasi Akhir Proyek +
+
+
+ +
+
+
+ {formatNumber(chickinPopulation ?? 0)} + + {formatCurrency( + isResponseSuccess(overhead) + ? overhead.data?.total.actual_total_amount + : 0 + )} +
+
+
+ {formatNumber(generalInformation?.population ?? 0)} +
+
+
+ +
+
+

+ {formatNumber(kandangTotal || 0)} +

-
- -
-
-
- {formatNumber(chickinPopulation ?? 0)} - - {formatCurrency( - isResponseSuccess(overhead) - ? overhead.data?.total.actual_total_amount - : 0 - )} -
-
-
- {formatNumber(generalInformation?.population ?? 0)} -
-
-
- -
-
-

- {formatNumber(kandangTotal || 0)} -

-
-
-
- )} - + + )} + + )} ); }; diff --git a/src/components/pages/closing/table/SalesClosingTable.tsx b/src/components/pages/closing/table/SalesClosingTable.tsx index 0d8a31ba..fc3d4c55 100644 --- a/src/components/pages/closing/table/SalesClosingTable.tsx +++ b/src/components/pages/closing/table/SalesClosingTable.tsx @@ -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', }} > -
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 ? ( + + ) : salesData.length === 0 ? ( + + ) : ( +
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', + }} + /> + )} diff --git a/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx b/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx index ba92c3d0..53174a71 100644 --- a/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx +++ b/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx @@ -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', }} > - - data={ - isResponseSuccess(sapronakCalculation) - ? (sapronakCalculation.data?.doc?.rows ?? []) - : [] - } - columns={docColumns} - className={{ - containerClassName: 'my-4', - }} - renderFooter={ - isResponseSuccess(sapronakCalculation) && - sapronakCalculation.data?.doc?.rows.length > 0 - } - /> + {isLoading ? ( + + ) : isResponseSuccess(sapronakCalculation) && + sapronakCalculation.data?.doc?.rows?.length === 0 ? ( + + ) : ( + + data={ + isResponseSuccess(sapronakCalculation) + ? (sapronakCalculation.data?.doc?.rows ?? []) + : [] + } + columns={docColumns} + className={{ + containerClassName: 'my-4', + }} + renderFooter={ + isResponseSuccess(sapronakCalculation) && + sapronakCalculation.data?.doc?.rows?.length > 0 + } + /> + )} - - data={ - isResponseSuccess(sapronakCalculation) - ? (sapronakCalculation.data?.ovk?.rows ?? []) - : [] - } - columns={ovkColumns} - className={{ - containerClassName: 'my-4', - }} - renderFooter={ - isResponseSuccess(sapronakCalculation) && - sapronakCalculation.data?.ovk?.rows.length > 0 - } - /> + {isLoading ? ( + + ) : isResponseSuccess(sapronakCalculation) && + sapronakCalculation.data?.ovk?.rows?.length === 0 ? ( + + ) : ( + + data={ + isResponseSuccess(sapronakCalculation) + ? (sapronakCalculation.data?.ovk?.rows ?? []) + : [] + } + columns={ovkColumns} + className={{ + containerClassName: 'my-4', + }} + renderFooter={ + isResponseSuccess(sapronakCalculation) && + sapronakCalculation.data?.ovk?.rows?.length > 0 + } + /> + )} - - data={ - isResponseSuccess(sapronakCalculation) - ? (sapronakCalculation.data?.pakan?.rows ?? []) - : [] - } - columns={pakanColumns} - className={{ - containerClassName: 'my-4', - }} - renderFooter={ - isResponseSuccess(sapronakCalculation) && - sapronakCalculation.data?.pakan?.rows.length > 0 - } - /> + {isLoading ? ( + + ) : isResponseSuccess(sapronakCalculation) && + sapronakCalculation.data?.pakan?.rows?.length === 0 ? ( + + ) : ( + + data={ + isResponseSuccess(sapronakCalculation) + ? (sapronakCalculation.data?.pakan?.rows ?? []) + : [] + } + columns={pakanColumns} + className={{ + containerClassName: 'my-4', + }} + renderFooter={ + isResponseSuccess(sapronakCalculation) && + sapronakCalculation.data?.pakan?.rows?.length > 0 + } + /> + )} ); diff --git a/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx index 49e4f108..d9f4f33a 100644 --- a/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx +++ b/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx @@ -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!' >
- - 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 ? ( + + ) : isResponseSuccess(incomingSapronakSummaries) && + incomingSapronakSummaries.data.length === 0 ? ( + + ) : ( + + 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, + }), + }} + /> + )}
diff --git a/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx b/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx index 3d3a9d70..e8e88582 100644 --- a/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx +++ b/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx @@ -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 = ({ - - 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 ? ( + + ) : isResponseSuccess(incomingSapronaks) && + incomingSapronaks.data.length === 0 ? ( + + ) : ( + + 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, + }), + }} + /> + )} diff --git a/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx index 42fcb588..7acc6d67 100644 --- a/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx +++ b/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx @@ -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!' >
- - 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 ? ( + + ) : isResponseSuccess(outgoingSapronakSummaries) && + outgoingSapronakSummaries.data.length === 0 ? ( + + ) : ( + + 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, + }), + }} + /> + )}
diff --git a/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx index acbbc52d..e6ab67db 100644 --- a/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx +++ b/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx @@ -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 = ({ - - 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 ? ( + + ) : isResponseSuccess(outgoingSapronaks) && + outgoingSapronaks.data.length === 0 ? ( + + ) : ( + + 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, + }), + }} + /> + )} From befc1c12174a4a7f1bf419649e7d83a34039a168 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 10:14:28 +0700 Subject: [PATCH 12/35] refactor(FE): Refactor skeleton components to remove default columns --- .../closing/skeleton/ClosingTabSkeleton.tsx | 4 +- .../skeleton/HppExpeditionClosingSkeleton.tsx | 19 +--- .../skeleton/OverheadClosingSkeleton.tsx | 47 +-------- .../closing/skeleton/SalesClosingSkeleton.tsx | 59 +---------- .../SapronakCalculationClosingSkeleton.tsx | 47 +-------- .../skeleton/SapronakClosingSkeleton.tsx | 98 ++----------------- .../closing/table/OverheadClosingTable.tsx | 50 +++++----- .../table/SapronakCalculationClosingTable.tsx | 9 +- .../IncomingSapronaksSummaryTable.tsx | 3 +- .../table/sapronak/IncomingSapronaksTable.tsx | 3 +- .../OutgoingSapronaksSummaryTable.tsx | 3 +- .../table/sapronak/OutgoingSapronaksTable.tsx | 3 +- 12 files changed, 55 insertions(+), 290 deletions(-) diff --git a/src/components/pages/closing/skeleton/ClosingTabSkeleton.tsx b/src/components/pages/closing/skeleton/ClosingTabSkeleton.tsx index 821ce72a..44defca8 100644 --- a/src/components/pages/closing/skeleton/ClosingTabSkeleton.tsx +++ b/src/components/pages/closing/skeleton/ClosingTabSkeleton.tsx @@ -8,7 +8,7 @@ const ClosingTabSkeleton = ({ title, subtitle, }: { - columns: ColumnDef[]; + columns: ColumnDef[]; icon: React.ReactNode; title: string; subtitle: string; @@ -17,7 +17,7 @@ const ClosingTabSkeleton = ({
[]} + columns={columns} isLoading={true} className={{ skeletonCellClassName: 'animate-none w-full h-5 bg-base-content/4', diff --git a/src/components/pages/closing/skeleton/HppExpeditionClosingSkeleton.tsx b/src/components/pages/closing/skeleton/HppExpeditionClosingSkeleton.tsx index 490839e1..d9be9971 100644 --- a/src/components/pages/closing/skeleton/HppExpeditionClosingSkeleton.tsx +++ b/src/components/pages/closing/skeleton/HppExpeditionClosingSkeleton.tsx @@ -9,29 +9,14 @@ const HppExpeditionClosingSkeleton = ({ subtitle = 'Tidak ada data HPP ekspedisi untuk periode ini.', iconName = 'heroicons:chart-bar', }: { - columns?: ColumnDef[]; + columns: ColumnDef[]; title?: string; subtitle?: string; iconName?: string; }) => { - const defaultColumns: ColumnDef[] = [ - { - id: 'id', - header: 'No', - }, - { - id: 'expedition_vendor_name', - header: 'Nama Ekspedisi', - }, - { - id: 'hpp_amount', - header: 'HPP Ekspedisi', - }, - ]; - return ( - columns={columns || defaultColumns} + columns={columns} icon={ } diff --git a/src/components/pages/closing/skeleton/OverheadClosingSkeleton.tsx b/src/components/pages/closing/skeleton/OverheadClosingSkeleton.tsx index 0ac2f4f3..7404f5d2 100644 --- a/src/components/pages/closing/skeleton/OverheadClosingSkeleton.tsx +++ b/src/components/pages/closing/skeleton/OverheadClosingSkeleton.tsx @@ -9,57 +9,14 @@ const OverheadClosingSkeleton = ({ subtitle = 'Tidak ada data overhead untuk periode ini.', iconName = 'heroicons:chart-bar', }: { - columns?: ColumnDef[]; + columns: ColumnDef[]; title?: string; subtitle?: string; iconName?: string; }) => { - const defaultColumns: ColumnDef[] = [ - { - 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 ( - columns={columns || defaultColumns} + columns={columns} icon={ } diff --git a/src/components/pages/closing/skeleton/SalesClosingSkeleton.tsx b/src/components/pages/closing/skeleton/SalesClosingSkeleton.tsx index c0ae3a28..a9ec35aa 100644 --- a/src/components/pages/closing/skeleton/SalesClosingSkeleton.tsx +++ b/src/components/pages/closing/skeleton/SalesClosingSkeleton.tsx @@ -9,69 +9,14 @@ const SalesClosingSkeleton = ({ subtitle = 'Tidak ada data penjualan untuk periode ini.', iconName = 'heroicons:chart-bar', }: { - columns?: ColumnDef[]; + columns: ColumnDef[]; title?: string; subtitle?: string; iconName?: string; }) => { - const defaultColumns: ColumnDef[] = [ - { - 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 ( - columns={columns || defaultColumns} + columns={columns} icon={ } diff --git a/src/components/pages/closing/skeleton/SapronakCalculationClosingSkeleton.tsx b/src/components/pages/closing/skeleton/SapronakCalculationClosingSkeleton.tsx index 6c1b9f3c..97d4a56c 100644 --- a/src/components/pages/closing/skeleton/SapronakCalculationClosingSkeleton.tsx +++ b/src/components/pages/closing/skeleton/SapronakCalculationClosingSkeleton.tsx @@ -9,57 +9,14 @@ const SapronakCalculationClosingSkeleton = ({ subtitle = 'Tidak ada data perhitungan sapronak untuk periode ini.', iconName = 'heroicons:chart-bar', }: { - columns?: ColumnDef[]; + columns: ColumnDef[]; title?: string; subtitle?: string; iconName?: string; }) => { - const defaultColumns: ColumnDef[] = [ - { - 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 ( - columns={columns || defaultColumns} + columns={columns} icon={ } diff --git a/src/components/pages/closing/skeleton/SapronakClosingSkeleton.tsx b/src/components/pages/closing/skeleton/SapronakClosingSkeleton.tsx index e3ea211b..130cd846 100644 --- a/src/components/pages/closing/skeleton/SapronakClosingSkeleton.tsx +++ b/src/components/pages/closing/skeleton/SapronakClosingSkeleton.tsx @@ -1,103 +1,20 @@ import { Icon } from '@iconify/react'; import ClosingTabSkeleton from './ClosingTabSkeleton'; -import { ClosingIncomingSapronak } from '@/types/api/closing'; import { ColumnDef } from '@tanstack/react-table'; -const SapronakClosingSkeleton = ({ +const SapronakClosingSkeleton = ({ columns, type = 'incoming', title, subtitle, iconName = 'heroicons:chart-bar', }: { - columns?: ColumnDef[]; + columns: ColumnDef[]; type?: 'incoming' | 'outgoing'; title?: string; subtitle?: string; iconName?: string; }) => { - const defaultIncomingColumns: ColumnDef[] = [ - { - 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[] = [ - { - 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' @@ -105,15 +22,12 @@ const SapronakClosingSkeleton = ({ const defaultSubtitle = type === 'incoming' - ? 'Silakan pilih periode atau filter untuk melihat data sapronak masuk.' - : 'Silakan pilih periode atau filter untuk melihat data sapronak keluar.'; + ? 'Tidak ada data sapronak masuk untuk periode ini.' + : 'Tidak ada data sapronak keluar untuk periode ini.'; return ( - - columns={ - columns || - (type === 'incoming' ? defaultIncomingColumns : defaultOutgoingColumns) - } + + columns={columns} icon={ } diff --git a/src/components/pages/closing/table/OverheadClosingTable.tsx b/src/components/pages/closing/table/OverheadClosingTable.tsx index b1f64f64..152c588c 100644 --- a/src/components/pages/closing/table/OverheadClosingTable.tsx +++ b/src/components/pages/closing/table/OverheadClosingTable.tsx @@ -210,27 +210,27 @@ const OverheadClosingTable = ({ return ( <> - {isLoadingOverhead ? ( - - ) : !isResponseSuccess(overhead) || - (!kandangId && overhead.data?.overheads.length === 0) || - (kandangId && !isResponseSuccess(overheadKandang)) ? ( - - ) : ( - + + {isLoadingOverhead ? ( + + ) : !isResponseSuccess(overhead) || + (!kandangId && overhead.data?.overheads.length === 0) || + (kandangId && !isResponseSuccess(overheadKandang)) ? ( + + ) : ( data={ kandangId @@ -256,7 +256,8 @@ const OverheadClosingTable = ({ : false } /> - {kandangId && ( + )} + {kandangId && !isLoadingOverhead && isResponseSuccess(overhead) && ( - )} - - )} + )} + ); }; diff --git a/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx b/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx index 53174a71..1e2f7534 100644 --- a/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx +++ b/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx @@ -195,10 +195,11 @@ const SapronakCalculationClosingTable = ({ }} > {isLoading ? ( - + ) : isResponseSuccess(sapronakCalculation) && sapronakCalculation.data?.doc?.rows?.length === 0 ? ( {isLoading ? ( - + ) : isResponseSuccess(sapronakCalculation) && sapronakCalculation.data?.ovk?.rows?.length === 0 ? ( {isLoading ? ( - + ) : isResponseSuccess(sapronakCalculation) && sapronakCalculation.data?.pakan?.rows?.length === 0 ? (
{isLoadingIncomingSapronakSummaries ? ( - + ) : isResponseSuccess(incomingSapronakSummaries) && incomingSapronakSummaries.data.length === 0 ? ( {isLoadingIncomingSapronaks ? ( - + ) : isResponseSuccess(incomingSapronaks) && incomingSapronaks.data.length === 0 ? (
{isLoadingOutgoingSapronakSummaries ? ( - + ) : isResponseSuccess(outgoingSapronakSummaries) && outgoingSapronakSummaries.data.length === 0 ? ( {isLoadingOutgoingSapronaks ? ( - + ) : isResponseSuccess(outgoingSapronaks) && outgoingSapronaks.data.length === 0 ? ( Date: Thu, 19 Feb 2026 10:15:20 +0700 Subject: [PATCH 13/35] refactor(FE): Refactor Card and SapronakClosingSkeleton components for readability --- .../closing/table/OverheadClosingTable.tsx | 98 +++++++++---------- .../IncomingSapronaksSummaryTable.tsx | 5 +- .../table/sapronak/IncomingSapronaksTable.tsx | 5 +- .../OutgoingSapronaksSummaryTable.tsx | 5 +- .../table/sapronak/OutgoingSapronaksTable.tsx | 5 +- 5 files changed, 65 insertions(+), 53 deletions(-) diff --git a/src/components/pages/closing/table/OverheadClosingTable.tsx b/src/components/pages/closing/table/OverheadClosingTable.tsx index 152c588c..fef1edc6 100644 --- a/src/components/pages/closing/table/OverheadClosingTable.tsx +++ b/src/components/pages/closing/table/OverheadClosingTable.tsx @@ -258,58 +258,58 @@ const OverheadClosingTable = ({ /> )} {kandangId && !isLoadingOverhead && isResponseSuccess(overhead) && ( - -
-
-

Pembelian Kandang

+ +
+
+

Pembelian Kandang

+
+
+ +
+
+
+ Populasi Akhir KANDANG{' '} + Pemakaian + Di FARM
-
- -
-
-
- Populasi Akhir KANDANG{' '} - {' '} - Pemakaian Di FARM -
-
-
- Populasi Akhir Proyek -
-
-
- -
-
-
- {formatNumber(chickinPopulation ?? 0)} - - {formatCurrency( - isResponseSuccess(overhead) - ? overhead.data?.total.actual_total_amount - : 0 - )} -
-
-
- {formatNumber(generalInformation?.population ?? 0)} -
-
-
- -
-
-

- {formatNumber(kandangTotal || 0)} -

+
+
+ Populasi Akhir Proyek
- +
+ +
+
+
+ {formatNumber(chickinPopulation ?? 0)} + + {formatCurrency( + isResponseSuccess(overhead) + ? overhead.data?.total.actual_total_amount + : 0 + )} +
+
+
+ {formatNumber(generalInformation?.population ?? 0)} +
+
+
+ +
+
+

+ {formatNumber(kandangTotal || 0)} +

+
+
+ )} diff --git a/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx index 0e54a9de..d4e01bd2 100644 --- a/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx +++ b/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx @@ -133,7 +133,10 @@ const ClosingIncomingSapronaksSummaryTable = ({ >
{isLoadingIncomingSapronakSummaries ? ( - + ) : isResponseSuccess(incomingSapronakSummaries) && incomingSapronakSummaries.data.length === 0 ? ( {isLoadingIncomingSapronaks ? ( - + ) : isResponseSuccess(incomingSapronaks) && incomingSapronaks.data.length === 0 ? (
{isLoadingOutgoingSapronakSummaries ? ( - + ) : isResponseSuccess(outgoingSapronakSummaries) && outgoingSapronakSummaries.data.length === 0 ? ( {isLoadingOutgoingSapronaks ? ( - + ) : isResponseSuccess(outgoingSapronaks) && outgoingSapronaks.data.length === 0 ? ( Date: Thu, 19 Feb 2026 10:43:37 +0700 Subject: [PATCH 14/35] refactor(FE): Refactor table components to improve styling and structure --- .../closing/table/FinanceClosingTable.tsx | 63 +++++- .../table/HppExpeditionClosingTable.tsx | 93 ++++----- .../closing/table/OverheadClosingTable.tsx | 32 ++- .../pages/closing/table/SalesClosingTable.tsx | 88 +++++---- .../table/SapronakCalculationClosingTable.tsx | 70 ++++++- .../IncomingSapronaksSummaryTable.tsx | 165 +++++++--------- .../table/sapronak/IncomingSapronaksTable.tsx | 185 ++++++++--------- .../OutgoingSapronaksSummaryTable.tsx | 166 +++++++--------- .../table/sapronak/OutgoingSapronaksTable.tsx | 186 ++++++++---------- 9 files changed, 539 insertions(+), 509 deletions(-) diff --git a/src/components/pages/closing/table/FinanceClosingTable.tsx b/src/components/pages/closing/table/FinanceClosingTable.tsx index de4d6e47..734e7a7b 100644 --- a/src/components/pages/closing/table/FinanceClosingTable.tsx +++ b/src/components/pages/closing/table/FinanceClosingTable.tsx @@ -82,7 +82,7 @@ const FinanceClosingTable = ({ }, [finance]); return ( -
+
{isLoading ? ( ) : !isResponseSuccess(finance) ? ( @@ -96,10 +96,13 @@ const FinanceClosingTable = ({ -
+
Laba Rugi Brutto
@@ -127,10 +130,13 @@ const FinanceClosingTable = ({ variant='bordered' collapsible className={{ - wrapper: 'w-full', + wrapper: 'w-full rounded-lg border-none', + body: 'p-0', + title: 'px-2 py-1.5 font-normal text-sm bg-primary text-white', + collapsible: 'rounded-lg', }} > -
+
data={hppTableData} isLoading={isLoading} @@ -263,6 +269,24 @@ const FinanceClosingTable = ({ ], }, ]} + className={{ + containerClassName: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + 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', + }} renderCustomRow={(row) => { const rowData = row.original; if (rowData.code === 'custom_row') { @@ -296,10 +320,13 @@ const FinanceClosingTable = ({ variant='bordered' collapsible className={{ - wrapper: 'w-full', + wrapper: 'w-full rounded-lg border-none', + body: 'p-0', + title: 'px-2 py-1.5 font-normal text-sm bg-primary text-white', + collapsible: 'rounded-lg', }} > -
+
data={profitLossTableData} isLoading={isLoading} @@ -363,6 +390,25 @@ const FinanceClosingTable = ({ ), }, ]} + className={{ + containerClassName: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + 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', + paginationClassName: 'hidden', + }} renderCustomRow={(row) => { const rowData = row.original; if (rowData.code === 'custom_row') { @@ -404,9 +450,6 @@ const FinanceClosingTable = ({ } return null; }} - className={{ - paginationClassName: 'hidden', - }} renderFooter={isResponseSuccess(finance)} />
diff --git a/src/components/pages/closing/table/HppExpeditionClosingTable.tsx b/src/components/pages/closing/table/HppExpeditionClosingTable.tsx index a1ce4a94..20bd556d 100644 --- a/src/components/pages/closing/table/HppExpeditionClosingTable.tsx +++ b/src/components/pages/closing/table/HppExpeditionClosingTable.tsx @@ -91,53 +91,56 @@ const HppExpeditionClosingTable = ({ ); return ( - <> -
-
-

HPP Ekspedisi

- + + {isLoading ? ( + + ) : costOfRevenueExpeditionData.length === 0 ? ( + + ) : ( +
0} className={{ - wrapper: 'w-full bg-base-100', - body: 'p-0', + containerClassName: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + 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 ? ( - - ) : costOfRevenueExpeditionData.length === 0 ? ( - - ) : ( -
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', - }} - /> - )} - - - - + /> + )} + + ); }; diff --git a/src/components/pages/closing/table/OverheadClosingTable.tsx b/src/components/pages/closing/table/OverheadClosingTable.tsx index fef1edc6..7ed62c0e 100644 --- a/src/components/pages/closing/table/OverheadClosingTable.tsx +++ b/src/components/pages/closing/table/OverheadClosingTable.tsx @@ -209,15 +209,18 @@ const OverheadClosingTable = ({ ); return ( - <> +
{isLoadingOverhead ? ( @@ -243,11 +246,24 @@ const OverheadClosingTable = ({ } columns={columns} className={{ - containerClassName: 'my-4', + containerClassName: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', headerColumnClassName: cn( - TABLE_DEFAULT_STYLING.headerColumnClassName, + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', '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={isLoadingOverhead} renderFooter={ @@ -312,7 +328,7 @@ const OverheadClosingTable = ({ )} - +
); }; diff --git a/src/components/pages/closing/table/SalesClosingTable.tsx b/src/components/pages/closing/table/SalesClosingTable.tsx index fc3d4c55..e6ded656 100644 --- a/src/components/pages/closing/table/SalesClosingTable.tsx +++ b/src/components/pages/closing/table/SalesClosingTable.tsx @@ -316,50 +316,54 @@ const SalesClosingTable = ({ projectFlockId }: SalesClosingTableProps) => { ); return ( - <> -
-
-

Penjualan

- + + {isLoading ? ( + + ) : salesData.length === 0 ? ( + + ) : ( +
0} className={{ - wrapper: 'w-full bg-base-100', - body: 'p-0', + containerClassName: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + 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 ? ( - - ) : salesData.length === 0 ? ( - - ) : ( -
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', - }} - /> - )} - - - - + /> + )} + + ); }; diff --git a/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx b/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx index 1e2f7534..1ad4d3d7 100644 --- a/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx +++ b/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx @@ -179,7 +179,7 @@ const SapronakCalculationClosingTable = ({ ); return ( -
+
{/* Table DOC jika kategori Project Flock Growing */} {isLoading ? ( @@ -213,7 +216,22 @@ const SapronakCalculationClosingTable = ({ } columns={docColumns} className={{ - containerClassName: 'my-4', + containerClassName: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + 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', }} renderFooter={ isResponseSuccess(sapronakCalculation) && @@ -229,7 +247,10 @@ const SapronakCalculationClosingTable = ({ collapsible defaultCollapsed={true} className={{ - wrapper: 'w-full', + wrapper: 'w-full rounded-lg border-none', + body: 'p-0', + title: 'px-2 py-1.5 font-normal text-sm bg-primary text-white', + collapsible: 'rounded-lg', }} > {isLoading ? ( @@ -251,7 +272,22 @@ const SapronakCalculationClosingTable = ({ } columns={ovkColumns} className={{ - containerClassName: 'my-4', + containerClassName: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + 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', }} renderFooter={ isResponseSuccess(sapronakCalculation) && @@ -267,7 +303,10 @@ const SapronakCalculationClosingTable = ({ collapsible defaultCollapsed={true} className={{ - wrapper: 'w-full', + wrapper: 'w-full rounded-lg border-none', + body: 'p-0', + title: 'px-2 py-1.5 font-normal text-sm bg-primary text-white', + collapsible: 'rounded-lg', }} > {isLoading ? ( @@ -289,7 +328,22 @@ const SapronakCalculationClosingTable = ({ } columns={pakanColumns} className={{ - containerClassName: 'my-4', + containerClassName: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + 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', }} renderFooter={ isResponseSuccess(sapronakCalculation) && diff --git a/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx index d4e01bd2..05fbebd2 100644 --- a/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx +++ b/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx @@ -5,12 +5,10 @@ import { useSearchParams } from 'next/navigation'; import useSWR from 'swr'; import { ColumnDef, SortingState } from '@tanstack/react-table'; -import { Icon } from '@iconify/react'; import Table from '@/components/Table'; import Card from '@/components/Card'; -import Collapse from '@/components/Collapse'; -import { cn, formatNumber } from '@/lib/helper'; +import { formatNumber } from '@/lib/helper'; import { isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; import { ClosingApi } from '@/services/api/closing'; @@ -56,8 +54,6 @@ const ClosingIncomingSapronaksSummaryTable = ({ } ); - const [open, setOpen] = useState(true); - const [sorting, setSorting] = useState([]); const [rowSelection, setRowSelection] = useState>({}); @@ -94,97 +90,78 @@ const ClosingIncomingSapronaksSummaryTable = ({ } }, [sorting, updateFilter]); - useEffect(() => { - if (!open) { - setOpen( - isResponseSuccess(incomingSapronakSummaries) - ? incomingSapronakSummaries.data.length > 0 - : false - ); - } - }, [incomingSapronakSummaries, isResponseSuccess]); - return ( - - -
Ringkasan Sapronak Masuk
- - -
- } - className='w-full!' - titleClassName='w-full p-0!' +
+ -
- {isLoadingIncomingSapronakSummaries ? ( - - ) : isResponseSuccess(incomingSapronakSummaries) && - incomingSapronakSummaries.data.length === 0 ? ( - - ) : ( - - 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 ? ( + + ) : isResponseSuccess(incomingSapronakSummaries) && + incomingSapronakSummaries.data.length === 0 ? ( + + ) : ( + + 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: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + 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', + }} + /> + )} + +
); }; diff --git a/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx b/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx index 268827b7..8e345441 100644 --- a/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx +++ b/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx @@ -5,13 +5,11 @@ import { useSearchParams } from 'next/navigation'; import useSWR from 'swr'; import { ColumnDef, SortingState } from '@tanstack/react-table'; -import { Icon } from '@iconify/react'; import Table from '@/components/Table'; import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import Card from '@/components/Card'; -import Collapse from '@/components/Collapse'; -import { cn, formatDate, formatNumber } from '@/lib/helper'; +import { formatDate, formatNumber } from '@/lib/helper'; import { isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; import { ClosingApi } from '@/services/api/closing'; @@ -52,8 +50,6 @@ const ClosingIncomingSapronaksTable = ({ ClosingApi.getAllIncomingSapronakFetcher ); - const [open, setOpen] = useState(true); - const [sorting, setSorting] = useState([]); const [rowSelection, setRowSelection] = useState>({}); @@ -118,109 +114,90 @@ const ClosingIncomingSapronaksTable = ({ } }, [sorting, updateFilter]); - useEffect(() => { - if (!open) { - setOpen( - isResponseSuccess(incomingSapronaks) - ? incomingSapronaks.data.length > 0 - : false - ); - } - }, [incomingSapronaks, isResponseSuccess]); - return ( - - -
Sapronak Masuk
- - -
- } - className='w-full!' - titleClassName='w-full p-0!' +
+ -
-
-
- -
+
+
+
- - {isLoadingIncomingSapronaks ? ( - - ) : isResponseSuccess(incomingSapronaks) && - incomingSapronaks.data.length === 0 ? ( - - ) : ( - - 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 ? ( + + ) : isResponseSuccess(incomingSapronaks) && + incomingSapronaks.data.length === 0 ? ( + + ) : ( + + 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: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + 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', + }} + /> + )} + +
); }; diff --git a/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx index b5ce3f9b..e5c2eea5 100644 --- a/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx +++ b/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx @@ -5,12 +5,10 @@ import { useSearchParams } from 'next/navigation'; import useSWR from 'swr'; import { ColumnDef, SortingState } from '@tanstack/react-table'; -import { Icon } from '@iconify/react'; import Table from '@/components/Table'; import Card from '@/components/Card'; -import Collapse from '@/components/Collapse'; -import { cn, formatNumber } from '@/lib/helper'; +import { formatNumber } from '@/lib/helper'; import { isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; import { ClosingApi } from '@/services/api/closing'; @@ -56,8 +54,6 @@ const ClosingOutgoingSapronaksSummaryTable = ({ } ); - const [open, setOpen] = useState(true); - const [sorting, setSorting] = useState([]); const [rowSelection, setRowSelection] = useState>({}); @@ -94,97 +90,79 @@ const ClosingOutgoingSapronaksSummaryTable = ({ } }, [sorting, updateFilter]); - useEffect(() => { - if (!open) { - setOpen( - isResponseSuccess(outgoingSapronakSummaries) - ? outgoingSapronakSummaries.data.length > 0 - : false - ); - } - }, [outgoingSapronakSummaries, isResponseSuccess]); - return ( - - -
Ringkasan Sapronak Keluar
- - -
- } - className='w-full!' - titleClassName='w-full p-0!' +
+ -
- {isLoadingOutgoingSapronakSummaries ? ( - - ) : isResponseSuccess(outgoingSapronakSummaries) && - outgoingSapronakSummaries.data.length === 0 ? ( - - ) : ( - - 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 ? ( + + ) : isResponseSuccess(outgoingSapronakSummaries) && + outgoingSapronakSummaries.data.length === 0 ? ( + + ) : ( + + 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: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: + 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + 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', + }} + /> + )} + +
); }; diff --git a/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx index dfb0178a..02ddc1bf 100644 --- a/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx +++ b/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx @@ -5,13 +5,11 @@ import { useSearchParams } from 'next/navigation'; import useSWR from 'swr'; import { ColumnDef, SortingState } from '@tanstack/react-table'; -import { Icon } from '@iconify/react'; import Table from '@/components/Table'; import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import Card from '@/components/Card'; -import Collapse from '@/components/Collapse'; -import { cn, formatDate, formatNumber } from '@/lib/helper'; +import { formatDate, formatNumber } from '@/lib/helper'; import { isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; import { ClosingApi } from '@/services/api/closing'; @@ -52,8 +50,6 @@ const ClosingOutgoingSapronaksTable = ({ ClosingApi.getAllOutgoingSapronakFetcher ); - const [open, setOpen] = useState(true); - const [sorting, setSorting] = useState([]); const [rowSelection, setRowSelection] = useState>({}); @@ -118,109 +114,91 @@ const ClosingOutgoingSapronaksTable = ({ } }, [sorting, updateFilter]); - useEffect(() => { - if (!open) { - setOpen( - isResponseSuccess(outgoingSapronaks) - ? outgoingSapronaks.data.length > 0 - : false - ); - } - }, [outgoingSapronaks, isResponseSuccess]); - return ( - - -
Sapronak Keluar
- - -
- } - className='w-full!' - titleClassName='w-full p-0!' +
+ -
-
-
- -
+
+
+
- - {isLoadingOutgoingSapronaks ? ( - - ) : isResponseSuccess(outgoingSapronaks) && - outgoingSapronaks.data.length === 0 ? ( - - ) : ( - - 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 ? ( + + ) : isResponseSuccess(outgoingSapronaks) && + outgoingSapronaks.data.length === 0 ? ( + + ) : ( + + 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: 'w-full mb-0!', + tableWrapperClassName: + 'overflow-x-auto rounded-tr-none rounded-tl-none', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: + 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + 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', + }} + /> + )} + +
); }; From 350ff0fbbea150a8211b3a14099707fa7f529813 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 10:46:03 +0700 Subject: [PATCH 15/35] refactor(FE): Simplify headerRowClassName formatting in tables --- .../closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx | 3 +-- .../pages/closing/table/sapronak/OutgoingSapronaksTable.tsx | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx index e5c2eea5..60e4e310 100644 --- a/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx +++ b/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx @@ -150,8 +150,7 @@ const ClosingOutgoingSapronaksSummaryTable = ({ tableWrapperClassName: 'overflow-x-auto rounded-tr-none rounded-tl-none', tableClassName: 'w-full table-auto text-sm', - headerRowClassName: - 'border-b border-b-gray-200 bg-gray-50', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', headerColumnClassName: 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', bodyRowClassName: diff --git a/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx index 02ddc1bf..46f1a46f 100644 --- a/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx +++ b/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx @@ -186,8 +186,7 @@ const ClosingOutgoingSapronaksTable = ({ tableWrapperClassName: 'overflow-x-auto rounded-tr-none rounded-tl-none', tableClassName: 'w-full table-auto text-sm', - headerRowClassName: - 'border-b border-b-gray-200 bg-gray-50', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', headerColumnClassName: 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', bodyRowClassName: From 4c1f11d859db77fbfa4d7d44fd3a4c0d9d70cafa Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 10:50:52 +0700 Subject: [PATCH 16/35] refactor(FE): Update table column headers from '#' to 'No' --- .../closing/table/sapronak/IncomingSapronaksSummaryTable.tsx | 2 +- .../pages/closing/table/sapronak/IncomingSapronaksTable.tsx | 2 +- .../closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx | 2 +- .../pages/closing/table/sapronak/OutgoingSapronaksTable.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx index 05fbebd2..ca16d143 100644 --- a/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx +++ b/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx @@ -60,7 +60,7 @@ const ClosingIncomingSapronaksSummaryTable = ({ const incomingSapronaksColumns: ColumnDef[] = [ { - header: '#', + header: 'No', cell: (props) => props.row.index + 1, }, { diff --git a/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx b/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx index 8e345441..c8f225d9 100644 --- a/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx +++ b/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx @@ -55,7 +55,7 @@ const ClosingIncomingSapronaksTable = ({ const incomingSapronaksColumns: ColumnDef[] = [ { - header: '#', + header: 'No', cell: (props) => props.row.index + 1, }, { diff --git a/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx index 60e4e310..e1c41b30 100644 --- a/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx +++ b/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx @@ -60,7 +60,7 @@ const ClosingOutgoingSapronaksSummaryTable = ({ const outgoingSapronaksColumns: ColumnDef[] = [ { - header: '#', + header: 'No', cell: (props) => props.row.index + 1, }, { diff --git a/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx index 46f1a46f..d2179fb3 100644 --- a/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx +++ b/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx @@ -55,7 +55,7 @@ const ClosingOutgoingSapronaksTable = ({ const outgoingSapronaksColumns: ColumnDef[] = [ { - header: '#', + header: 'No', cell: (props) => props.row.index + 1, }, { From c3a69bc66aa465be6689f246b930555f68c45362 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 10:55:07 +0700 Subject: [PATCH 17/35] refactor(FE): Add Icon component and update table styles --- .../pages/closing/table/FinanceClosingTable.tsx | 3 ++- .../sapronak/IncomingSapronaksSummaryTable.tsx | 1 + .../table/sapronak/IncomingSapronaksTable.tsx | 15 ++++++++++++++- .../sapronak/OutgoingSapronaksSummaryTable.tsx | 1 + .../table/sapronak/OutgoingSapronaksTable.tsx | 15 ++++++++++++++- 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/components/pages/closing/table/FinanceClosingTable.tsx b/src/components/pages/closing/table/FinanceClosingTable.tsx index 734e7a7b..1e1195fe 100644 --- a/src/components/pages/closing/table/FinanceClosingTable.tsx +++ b/src/components/pages/closing/table/FinanceClosingTable.tsx @@ -151,7 +151,7 @@ const FinanceClosingTable = ({ .filter((row) => row.code !== 'custom_row').length; return dataRowsBefore + 1; }, - footer: (props) => { + footer: () => { return 'HPP'; }, }, @@ -286,6 +286,7 @@ const FinanceClosingTable = ({ footerRowClassName: 'border-t-2 border-gray-300', footerColumnClassName: 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap', + paginationClassName: 'hidden', }} renderCustomRow={(row) => { const rowData = row.original; diff --git a/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx index ca16d143..9c43675c 100644 --- a/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx +++ b/src/components/pages/closing/table/sapronak/IncomingSapronaksSummaryTable.tsx @@ -5,6 +5,7 @@ import { useSearchParams } from 'next/navigation'; import useSWR from 'swr'; import { ColumnDef, SortingState } from '@tanstack/react-table'; +import { Icon } from '@iconify/react'; import Table from '@/components/Table'; import Card from '@/components/Card'; diff --git a/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx b/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx index c8f225d9..0bbad454 100644 --- a/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx +++ b/src/components/pages/closing/table/sapronak/IncomingSapronaksTable.tsx @@ -5,6 +5,7 @@ import { useSearchParams } from 'next/navigation'; import useSWR from 'swr'; import { ColumnDef, SortingState } from '@tanstack/react-table'; +import { Icon } from '@iconify/react'; import Table from '@/components/Table'; import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import Card from '@/components/Card'; @@ -135,7 +136,19 @@ const ClosingIncomingSapronaksTable = ({ placeholder='Cari Sapronak Masuk' value={tableFilterState.search} onChange={searchChangeHandler} - className={{ wrapper: 'sm:max-w-3xs' }} + startAdornment={ + + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', + }} />
diff --git a/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx index e1c41b30..591b38d6 100644 --- a/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx +++ b/src/components/pages/closing/table/sapronak/OutgoingSapronaksSummaryTable.tsx @@ -5,6 +5,7 @@ import { useSearchParams } from 'next/navigation'; import useSWR from 'swr'; import { ColumnDef, SortingState } from '@tanstack/react-table'; +import { Icon } from '@iconify/react'; import Table from '@/components/Table'; import Card from '@/components/Card'; diff --git a/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx b/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx index d2179fb3..652b3f60 100644 --- a/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx +++ b/src/components/pages/closing/table/sapronak/OutgoingSapronaksTable.tsx @@ -5,6 +5,7 @@ import { useSearchParams } from 'next/navigation'; import useSWR from 'swr'; import { ColumnDef, SortingState } from '@tanstack/react-table'; +import { Icon } from '@iconify/react'; import Table from '@/components/Table'; import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import Card from '@/components/Card'; @@ -135,7 +136,19 @@ const ClosingOutgoingSapronaksTable = ({ placeholder='Cari Sapronak Keluar' value={tableFilterState.search} onChange={searchChangeHandler} - className={{ wrapper: 'sm:max-w-3xs' }} + startAdornment={ + + } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', + }} /> From 8a1e0f080f0cf0183f92275ea29d1b622da47394 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 11:14:39 +0700 Subject: [PATCH 18/35] refactor(FE): Refactor table components for consistent styling and cleanup --- .../table/SapronakCalculationClosingTable.tsx | 2 +- .../sapronak/IncomingSapronaksSummaryTable.tsx | 18 ++++++------------ .../table/sapronak/IncomingSapronaksTable.tsx | 13 ++++++------- .../sapronak/OutgoingSapronaksSummaryTable.tsx | 16 +++++----------- .../table/sapronak/OutgoingSapronaksTable.tsx | 13 ++++++------- 5 files changed, 24 insertions(+), 38 deletions(-) diff --git a/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx b/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx index 1ad4d3d7..0f0a7857 100644 --- a/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx +++ b/src/components/pages/closing/table/SapronakCalculationClosingTable.tsx @@ -179,7 +179,7 @@ const SapronakCalculationClosingTable = ({ ); return ( -
+
{/* Table DOC jika kategori Project Flock Growing */} = (e) => { - updateFilter('search', e.target.value); - }; - // track sorting useEffect(() => { const isNameSorted = sorting.find((sortItem) => sortItem.id === 'name'); @@ -92,10 +87,10 @@ const ClosingIncomingSapronaksSummaryTable = ({ }, [sorting, updateFilter]); return ( -
+
+
-
+
= (e) => { - updateFilter('search', e.target.value); - }; - // track sorting useEffect(() => { const isNameSorted = sorting.find((sortItem) => sortItem.id === 'name'); @@ -92,7 +87,7 @@ const ClosingOutgoingSapronaksSummaryTable = ({ }, [sorting, updateFilter]); return ( -
+
+
-
+
Date: Thu, 19 Feb 2026 11:27:24 +0700 Subject: [PATCH 19/35] refactor(FE): Refactor table and tab styles for consistent spacing and layout --- src/components/pages/closing/ClosingDetailTabs.tsx | 2 +- src/components/pages/closing/ClosingsTable.tsx | 2 +- src/components/pages/closing/table/FinanceClosingTable.tsx | 2 +- .../pages/closing/table/HppExpeditionClosingTable.tsx | 2 +- src/components/pages/closing/table/OverheadClosingTable.tsx | 2 +- src/components/pages/closing/table/SalesClosingTable.tsx | 2 +- .../pages/closing/table/SapronakCalculationClosingTable.tsx | 2 +- .../pages/closing/table/sapronak/IncomingSapronaksTable.tsx | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/pages/closing/ClosingDetailTabs.tsx b/src/components/pages/closing/ClosingDetailTabs.tsx index 59fea1ba..d5dc996c 100644 --- a/src/components/pages/closing/ClosingDetailTabs.tsx +++ b/src/components/pages/closing/ClosingDetailTabs.tsx @@ -126,7 +126,7 @@ const ClosingDetail: React.FC = ({ variant='boxed' className={{ tabHeaderWrapper: - 'justify-between items-center py-3 border-b border-base-content/10', + 'relative justify-between items-center py-3 after:absolute after:bottom-0 after:left-0 after:right-0 after:-mx-4 after:border-b after:border-base-content/10', tab: 'w-fit', content: 'p-0 m-0', }} diff --git a/src/components/pages/closing/ClosingsTable.tsx b/src/components/pages/closing/ClosingsTable.tsx index 2716932f..5a8aab17 100644 --- a/src/components/pages/closing/ClosingsTable.tsx +++ b/src/components/pages/closing/ClosingsTable.tsx @@ -17,7 +17,7 @@ import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; import RequirePermission from '@/components/helper/RequirePermission'; -import { cn, formatCurrency, formatDate } from '@/lib/helper'; +import { cn, formatDate } from '@/lib/helper'; import { isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; import { LocationApi } from '@/services/api/master-data'; diff --git a/src/components/pages/closing/table/FinanceClosingTable.tsx b/src/components/pages/closing/table/FinanceClosingTable.tsx index 1e1195fe..82e8b309 100644 --- a/src/components/pages/closing/table/FinanceClosingTable.tsx +++ b/src/components/pages/closing/table/FinanceClosingTable.tsx @@ -82,7 +82,7 @@ const FinanceClosingTable = ({ }, [finance]); return ( -
+
{isLoading ? ( ) : !isResponseSuccess(finance) ? ( diff --git a/src/components/pages/closing/table/HppExpeditionClosingTable.tsx b/src/components/pages/closing/table/HppExpeditionClosingTable.tsx index 20bd556d..5389e3d5 100644 --- a/src/components/pages/closing/table/HppExpeditionClosingTable.tsx +++ b/src/components/pages/closing/table/HppExpeditionClosingTable.tsx @@ -91,7 +91,7 @@ const HppExpeditionClosingTable = ({ ); return ( -
+
+
{ ); return ( -
+
+
{/* Table DOC jika kategori Project Flock Growing */} +
Date: Thu, 19 Feb 2026 11:38:34 +0700 Subject: [PATCH 20/35] refactor(FE): Refactor UI and improve conditional rendering in closing pages --- .../pages/closing/ClosingKandangList.tsx | 7 +++---- .../closing/table/OverheadClosingTable.tsx | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/pages/closing/ClosingKandangList.tsx b/src/components/pages/closing/ClosingKandangList.tsx index dd3083a7..b324c512 100644 --- a/src/components/pages/closing/ClosingKandangList.tsx +++ b/src/components/pages/closing/ClosingKandangList.tsx @@ -10,18 +10,17 @@ const ClosingKandangList = ({ projectData?: ProjectFlock; }) => { return ( -
+
-

Kandang

-
+
{projectData?.kandangs?.map((kandang) => ( diff --git a/src/components/pages/closing/table/OverheadClosingTable.tsx b/src/components/pages/closing/table/OverheadClosingTable.tsx index 2d3faaec..a6c31e6c 100644 --- a/src/components/pages/closing/table/OverheadClosingTable.tsx +++ b/src/components/pages/closing/table/OverheadClosingTable.tsx @@ -224,15 +224,28 @@ const OverheadClosingTable = ({ > {isLoadingOverhead ? ( - ) : !isResponseSuccess(overhead) || - (!kandangId && overhead.data?.overheads.length === 0) || - (kandangId && !isResponseSuccess(overheadKandang)) ? ( + ) : !isResponseSuccess(overhead) ? ( + ) : kandangId && !isResponseSuccess(overheadKandang) ? ( + + ) : (!kandangId && overhead.data?.overheads.length === 0) || + (kandangId && + isResponseSuccess(overheadKandang) && + overheadKandang.data?.overheads.length === 0) ? ( + ) : ( data={ From 82975219a89773067f3d0d852238c7f1e8bd5ec7 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 11:43:46 +0700 Subject: [PATCH 21/35] refactor(FE): Update border styles for ClosingDetailTabs and ClosingKandangList --- src/components/pages/closing/ClosingDetailTabs.tsx | 2 +- src/components/pages/closing/ClosingKandangList.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/pages/closing/ClosingDetailTabs.tsx b/src/components/pages/closing/ClosingDetailTabs.tsx index d5dc996c..dc8bd6f8 100644 --- a/src/components/pages/closing/ClosingDetailTabs.tsx +++ b/src/components/pages/closing/ClosingDetailTabs.tsx @@ -126,7 +126,7 @@ const ClosingDetail: React.FC = ({ variant='boxed' className={{ tabHeaderWrapper: - 'relative justify-between items-center py-3 after:absolute after:bottom-0 after:left-0 after:right-0 after:-mx-4 after:border-b after:border-base-content/10', + 'relative justify-between items-center py-3 before:absolute before:top-0 before:left-0 before:right-0 before:-mx-4 before:border-t before:border-base-content/10 after:absolute after:bottom-0 after:left-0 after:right-0 after:-mx-4 after:border-b after:border-base-content/10', tab: 'w-fit', content: 'p-0 m-0', }} diff --git a/src/components/pages/closing/ClosingKandangList.tsx b/src/components/pages/closing/ClosingKandangList.tsx index b324c512..bd2823c4 100644 --- a/src/components/pages/closing/ClosingKandangList.tsx +++ b/src/components/pages/closing/ClosingKandangList.tsx @@ -10,7 +10,7 @@ const ClosingKandangList = ({ projectData?: ProjectFlock; }) => { return ( -
+
From a0af934002aef3d0b6a4c0554abbc826b709871d Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 11:52:57 +0700 Subject: [PATCH 22/35] feat(FE): Add headings and improve layout for financial tables --- .../pages/closing/ClosingKandangList.tsx | 1 + .../closing/table/FinanceClosingTable.tsx | 99 +++++++++++++------ 2 files changed, 72 insertions(+), 28 deletions(-) diff --git a/src/components/pages/closing/ClosingKandangList.tsx b/src/components/pages/closing/ClosingKandangList.tsx index bd2823c4..4ecf607f 100644 --- a/src/components/pages/closing/ClosingKandangList.tsx +++ b/src/components/pages/closing/ClosingKandangList.tsx @@ -14,6 +14,7 @@ const ClosingKandangList = ({
+

Kandang

{projectData?.kandangs?.map((kandang) => (
- - data={isResponseSuccess(closings) ? closings?.data : []} - columns={closingsColumns} - pageSize={tableFilterState.pageSize} - onPageSizeChange={setPageSize} - rowOptions={[10, 20, 50, 100]} - page={isResponseSuccess(closings) ? closings?.meta?.page : 0} - totalItems={ - isResponseSuccess(closings) ? closings?.meta?.total_results : 0 - } - onPageChange={setPage} - isLoading={isLoadingClosings} - sorting={sorting} - setSorting={setSorting} - rowSelection={rowSelection} - setRowSelection={setRowSelection} - className={{ - containerClassName: cn({ - 'w-full mb-0': - isResponseSuccess(closings) && closings?.data?.length === 0, - }), - }} - /> + {isLoadingClosings ? ( +
+ +
+ ) : data.length === 0 ? ( + + } + title='Data Closing Belum Tersedia' + subtitle='Tidak ada data closing untuk saat ini.' + /> + ) : ( + + data={data} + columns={closingsColumns} + pageSize={tableFilterState.pageSize} + onPageSizeChange={setPageSize} + rowOptions={[10, 20, 50, 100]} + page={tableFilterState.page} + totalItems={ + isResponseSuccess(closings) ? closings?.meta?.total_results : 0 + } + onPageChange={setPage} + isLoading={isLoadingClosings} + sorting={sorting} + setSorting={setSorting} + rowSelection={rowSelection} + setRowSelection={setRowSelection} + className={{ + containerClassName: cn({ + 'w-full mb-0': data.length === 0, + }), + }} + /> + )}
+ + {/* Filter Modal */} + + {/* Modal Header */} +
+
+ +

Filter Data

+
+ +
+
+
+ { + if (!Array.isArray(val)) { + formik.setFieldValue( + 'location_id', + val?.value ? String(val.value) : null + ); + } + }} + onInputChange={setLocationInputValue} + isLoading={isLoadingLocationOptions} + isClearable + onMenuScrollToBottom={loadMoreLocations} + className={{ wrapper: 'w-full' }} + /> + + { + if (!Array.isArray(val)) { + formik.setFieldValue('project_status', val?.value || null); + } + }} + className={{ wrapper: 'w-full' }} + isClearable={true} + /> +
+ + {/* Modal Footer */} +
+ + +
+ +
); }; diff --git a/src/components/pages/closing/filter/ClosingFilter.ts b/src/components/pages/closing/filter/ClosingFilter.ts new file mode 100644 index 00000000..77f0c9d2 --- /dev/null +++ b/src/components/pages/closing/filter/ClosingFilter.ts @@ -0,0 +1,13 @@ +import * as yup from 'yup'; + +export type ClosingFilterType = { + location_id: string | null; + project_status: string | null; +}; + +export const ClosingFilterSchema = yup.object({ + location_id: yup.string().nullable(), + project_status: yup.string().nullable(), +}); + +export type ClosingFilterValues = yup.InferType; diff --git a/src/components/pages/closing/skeleton/ClosingTableSkeleton.tsx b/src/components/pages/closing/skeleton/ClosingTableSkeleton.tsx new file mode 100644 index 00000000..4b59510a --- /dev/null +++ b/src/components/pages/closing/skeleton/ClosingTableSkeleton.tsx @@ -0,0 +1,37 @@ +import DataStateSkeleton from '@/components/helper/skeleton/DataStateSkeleton'; +import Table from '@/components/Table'; +import { Closing } from '@/types/api/closing'; +import { ColumnDef } from '@tanstack/react-table'; + +const ClosingTableSkeleton = ({ + columns, + icon, + title, + subtitle, +}: { + columns: ColumnDef[]; + icon: React.ReactNode; + title: string; + subtitle: string; +}) => { + return ( +
+
+
+ +
+ + ); +}; + +export default ClosingTableSkeleton; From e9784bd5ed2a9964f82cc868481e4e97f373b59e Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 14:33:35 +0700 Subject: [PATCH 28/35] refactor(FE): Refactor ClosingsTable component and update styles --- src/app/closing/page.tsx | 2 +- .../pages/closing/ClosingsTable.tsx | 216 +++++++++++------- 2 files changed, 133 insertions(+), 85 deletions(-) diff --git a/src/app/closing/page.tsx b/src/app/closing/page.tsx index acaa3ee8..0717c350 100644 --- a/src/app/closing/page.tsx +++ b/src/app/closing/page.tsx @@ -2,7 +2,7 @@ import ClosingsTable from '@/components/pages/closing/ClosingsTable'; const Closing = () => { return ( -
+
); diff --git a/src/components/pages/closing/ClosingsTable.tsx b/src/components/pages/closing/ClosingsTable.tsx index 294106ff..ad402829 100644 --- a/src/components/pages/closing/ClosingsTable.tsx +++ b/src/components/pages/closing/ClosingsTable.tsx @@ -3,15 +3,15 @@ import { ChangeEventHandler, useEffect, useState, useMemo } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; +import { useRouter } from 'next/navigation'; import { Icon } from '@iconify/react'; import Table from '@/components/Table'; import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import Button from '@/components/Button'; import SelectInput, { useSelect } from '@/components/input/SelectInput'; -import RowDropdownOptions from '@/components/table/RowDropdownOptions'; -import RowCollapseOptions from '@/components/table/RowCollapseOptions'; -import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; +import PopoverButton from '@/components/popover/PopoverButton'; +import PopoverContent from '@/components/popover/PopoverContent'; import RequirePermission from '@/components/helper/RequirePermission'; import Modal, { useModal } from '@/components/Modal'; import SelectInputRadio from '@/components/input/SelectInputRadio'; @@ -31,32 +31,66 @@ import { import ClosingTableSkeleton from '@/components/pages/closing/skeleton/ClosingTableSkeleton'; const RowOptionsMenu = ({ - type = 'dropdown', props, + popoverPosition = 'bottom', + detailClickHandler, }: { - type: 'dropdown' | 'collapse'; props: CellContext; + popoverPosition: 'bottom' | 'top'; + detailClickHandler: (id: number) => void; }) => { + const popoverId = `closing#${props.row.original.id}`; + const popoverAnchorName = `--anchor-closing#${props.row.original.id}`; + + const closePopover = () => { + document.getElementById(popoverId)?.hidePopover(); + }; + + const detailClickHandlerWrapper = () => { + detailClickHandler(props.row.original.id); + closePopover(); + }; + return ( - -
- - - -
-
+
+ + + + + +
+ + + +
+
+
); }; const ClosingsTable = () => { + // ===== ROUTER ===== + const router = useRouter(); + // ===== FILTER MODAL STATE ===== const filterModal = useModal(); @@ -170,22 +204,18 @@ const ClosingsTable = () => { const currentRowRelativeIndex = currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 3; + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; + + const detailClickHandler = (id: number) => { + router.push(`/closing/detail/?closingId=${id}`); + }; return ( - <> - {currentPageSize > 3 && ( - - - - )} - - {currentPageSize <= 3 && ( - - - - )} - + ); }, }, @@ -268,16 +298,32 @@ const ClosingsTable = () => { return ( <> -
-
-
-
+
+
+
+
+ {/* Space for action buttons if needed in the future */} +
+ +
+ } + className={{ + wrapper: 'w-full min-w-24 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', + }} />
-
- {isLoadingClosings ? ( -
- -
- ) : data.length === 0 ? ( - - } - title='Data Closing Belum Tersedia' - subtitle='Tidak ada data closing untuk saat ini.' - /> - ) : ( - - data={data} - columns={closingsColumns} - pageSize={tableFilterState.pageSize} - onPageSizeChange={setPageSize} - rowOptions={[10, 20, 50, 100]} - page={tableFilterState.page} - totalItems={ - isResponseSuccess(closings) ? closings?.meta?.total_results : 0 - } - onPageChange={setPage} - isLoading={isLoadingClosings} - sorting={sorting} - setSorting={setSorting} - rowSelection={rowSelection} - setRowSelection={setRowSelection} - className={{ - containerClassName: cn({ - 'w-full mb-0': data.length === 0, - }), - }} - /> - )} + {isLoadingClosings ? ( +
+ +
+ ) : data.length === 0 ? ( + + } + title='Data Closing Belum Tersedia' + subtitle='Tidak ada data closing untuk saat ini.' + /> + ) : ( + + data={isResponseSuccess(closings) ? closings?.data : []} + columns={closingsColumns} + pageSize={tableFilterState.pageSize} + onPageSizeChange={setPageSize} + rowOptions={[10, 20, 50, 100]} + page={isResponseSuccess(closings) ? closings?.meta?.page : 0} + totalItems={ + isResponseSuccess(closings) ? closings?.meta?.total_results : 0 + } + onPageChange={setPage} + isLoading={isLoadingClosings} + sorting={sorting} + setSorting={setSorting} + rowSelection={rowSelection} + setRowSelection={setRowSelection} + className={{ + containerClassName: cn('mt-3', { + 'w-full mb-0': + isResponseSuccess(closings) && closings?.data?.length === 0, + }), + headerColumnClassName: 'text-nowrap', + }} + /> + )} +
{/* Filter Modal */} From 60e360537e92e2074c21851823b0ccd078a1929a Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 14:37:20 +0700 Subject: [PATCH 29/35] refactor(FE): Refactor ClosingsTable header for improved styling --- src/components/pages/closing/ClosingsTable.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/pages/closing/ClosingsTable.tsx b/src/components/pages/closing/ClosingsTable.tsx index ad402829..22b4dd52 100644 --- a/src/components/pages/closing/ClosingsTable.tsx +++ b/src/components/pages/closing/ClosingsTable.tsx @@ -300,11 +300,7 @@ const ClosingsTable = () => { <>
-
-
- {/* Space for action buttons if needed in the future */} -
- +
Date: Thu, 19 Feb 2026 14:41:21 +0700 Subject: [PATCH 30/35] feat(FE): Add StatusBadge to display project status in ClosingsTable --- .../pages/closing/ClosingsTable.tsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/components/pages/closing/ClosingsTable.tsx b/src/components/pages/closing/ClosingsTable.tsx index 22b4dd52..12114110 100644 --- a/src/components/pages/closing/ClosingsTable.tsx +++ b/src/components/pages/closing/ClosingsTable.tsx @@ -13,6 +13,7 @@ import SelectInput, { useSelect } from '@/components/input/SelectInput'; import PopoverButton from '@/components/popover/PopoverButton'; import PopoverContent from '@/components/popover/PopoverContent'; import RequirePermission from '@/components/helper/RequirePermission'; +import StatusBadge from '@/components/helper/StatusBadge'; import Modal, { useModal } from '@/components/Modal'; import SelectInputRadio from '@/components/input/SelectInputRadio'; import { useFormik } from 'formik'; @@ -24,6 +25,7 @@ import { LocationApi } from '@/services/api/master-data'; import { Location } from '@/types/api/master-data/location'; import { ClosingApi } from '@/services/api/closing'; import { Closing } from '@/types/api/closing'; +import { Color } from '@/types/theme'; import { ClosingFilterSchema, ClosingFilterType, @@ -91,6 +93,21 @@ const ClosingsTable = () => { // ===== ROUTER ===== const router = useRouter(); + // ===== STATUS BADGE COLOR HELPER ===== + const getProjectStatusBadgeColor = (status: string): Color => { + const normalizedValue = status.toLowerCase(); + + if (normalizedValue === 'aktif') { + return 'success'; + } + + if (normalizedValue === 'pengajuan') { + return 'neutral'; + } + + return 'neutral'; + }; + // ===== FILTER MODAL STATE ===== const filterModal = useModal(); @@ -195,6 +212,19 @@ const ClosingsTable = () => { { accessorKey: 'project_status', header: 'Status', + cell: (props) => { + const status = props.row.original.project_status; + const badgeColor = getProjectStatusBadgeColor(status); + return ( + + ); + }, }, { header: 'Aksi', From a4ff92520ab12020e9ccbc242aa1a82a93e535e8 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 14:50:43 +0700 Subject: [PATCH 31/35] refactor(FE): Update toast message for Project Flock approval/rejection --- .../pages/production/project-flock/ProjectFlockTable.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/pages/production/project-flock/ProjectFlockTable.tsx b/src/components/pages/production/project-flock/ProjectFlockTable.tsx index e8280fa8..8ec79009 100644 --- a/src/components/pages/production/project-flock/ProjectFlockTable.tsx +++ b/src/components/pages/production/project-flock/ProjectFlockTable.tsx @@ -271,7 +271,11 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { ); if (isResponseSuccess(approveProjectFlockRes)) { - toast.success('Project Flock berhasil di-approve!'); + const successMessage = + approvalAction === 'APPROVED' + ? 'Project Flock berhasil di-approve!' + : 'Project Flock berhasil di-reject!'; + toast.success(successMessage); confirmModal.closeModal(); } if (isResponseError(approveProjectFlockRes)) { From 6ac903313c766c9797563d17b1fb198c86ac6fe9 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 15:22:28 +0700 Subject: [PATCH 32/35] refactor(FE): Refactor chickin approval modal logic into Zustand store --- .../chickin/form/tabs/ChickLogsView.tsx | 62 ++++++------------- .../project-flock/ProjectFlockTable.tsx | 51 +++++++++++++++ .../production/chickin/chickin.store.ts | 19 ++++++ .../chickin/slices/chickin-approval.slice.ts | 58 +++++++++++++++++ 4 files changed, 147 insertions(+), 43 deletions(-) create mode 100644 src/stores/production/chickin/chickin.store.ts create mode 100644 src/stores/production/chickin/slices/chickin-approval.slice.ts diff --git a/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx b/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx index e800ee68..bdffda33 100644 --- a/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx +++ b/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx @@ -2,8 +2,6 @@ import Alert from '@/components/Alert'; import Button from '@/components/Button'; import Card from '@/components/Card'; import RequirePermission from '@/components/helper/RequirePermission'; -import { useModal } from '@/components/Modal'; -import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; import PillBadge from '@/components/PillBadge'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { formatDate, formatNumber } from '@/lib/helper'; @@ -13,6 +11,7 @@ import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandan import { Icon } from '@iconify/react'; import { useState } from 'react'; import toast from 'react-hot-toast'; +import { useChickinStore } from '@/stores/production/chickin/chickin.store'; const ChickinLogsView = ({ initialValues, @@ -23,32 +22,26 @@ const ChickinLogsView = ({ afterSubmit?: () => void; rawDataApprovals: BaseApproval[]; }) => { - const confirmModal = useModal(); - const [isApproveLoading, setIsApproveLoading] = useState(false); const [chickinErrorMessage, setChickinErrorMessage] = useState(''); + const { openChickinApproveModal } = useChickinStore(); const handleClickApprove = () => { - confirmModal.openModal(); - }; - - const confirmationModalApproveClickHandler = async (notes?: string) => { - setChickinErrorMessage(''); - setIsApproveLoading(true); - const approveChickinRes = await ChickinApi.singleApproval( - initialValues?.id as number, - 'APPROVED', - notes - ); - if (isResponseSuccess(approveChickinRes)) { - toast.success(approveChickinRes?.message as string); - } - if (isResponseError(approveChickinRes)) { - toast.error(approveChickinRes?.message as string); - setChickinErrorMessage(approveChickinRes?.message as string); - } - confirmModal.closeModal(); - setIsApproveLoading(false); - afterSubmit && afterSubmit(); + openChickinApproveModal(initialValues, async (notes?: string) => { + setChickinErrorMessage(''); + const approveChickinRes = await ChickinApi.singleApproval( + initialValues?.id as number, + 'APPROVED', + notes + ); + if (isResponseSuccess(approveChickinRes)) { + toast.success(approveChickinRes?.message as string); + } + if (isResponseError(approveChickinRes)) { + toast.error(approveChickinRes?.message as string); + setChickinErrorMessage(approveChickinRes?.message as string); + } + afterSubmit && afterSubmit(); + }); }; return ( @@ -83,7 +76,7 @@ const ChickinLogsView = ({ key={chickin.id || index} variant='bordered' className={{ - wrapper: 'w-full', + wrapper: 'w-full mt-3', body: 'p-3', }} > @@ -176,23 +169,6 @@ const ChickinLogsView = ({
)}
- - { - confirmationModalApproveClickHandler(notes); - }, - isLoading: isApproveLoading, - }} - /> ); }; diff --git a/src/components/pages/production/project-flock/ProjectFlockTable.tsx b/src/components/pages/production/project-flock/ProjectFlockTable.tsx index 8ec79009..040948ff 100644 --- a/src/components/pages/production/project-flock/ProjectFlockTable.tsx +++ b/src/components/pages/production/project-flock/ProjectFlockTable.tsx @@ -36,6 +36,7 @@ import PopoverContent from '@/components/popover/PopoverContent'; import ProjectFlockConfirmationModal from './ProjectFlockConfirmationModal'; import { useProjectFlockStore } from '@/stores/production/project-flock/project-flock.store'; import { ProjectFlockFormValues } from './form/ProjectFlockForm.schema'; +import { useChickinStore } from '@/stores/production/chickin/chickin.store'; const RowOptionsMenu = ({ props, @@ -193,6 +194,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { const deleteModal = useModal(); const confirmModal = useModal(); const successModal = useModal(); + const chickinApproveModal = useModal(); const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>( 'APPROVED' ); @@ -200,6 +202,13 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { const [isApproveLoading, setIsApproveLoading] = useState(false); const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] = useState(false); + const { + isChickinApproveModalOpen, + isChickinApproveLoading, + chickinApproveCallback, + closeChickinApproveModal, + setChickinApproveLoading, + } = useChickinStore(); // ===== Fetch Data ===== const { @@ -292,6 +301,14 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { refreshProjectFlocks(); }, [refresh]); + useEffect(() => { + if (isChickinApproveModalOpen) { + chickinApproveModal.openModal(); + } else { + chickinApproveModal.closeModal(); + } + }, [isChickinApproveModalOpen, chickinApproveModal]); + useEffect(() => { if (isSuccess) { successModal.openModal(); @@ -974,6 +991,40 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { onClose={handleSuccessModalClose} secondaryButton={undefined} /> + + {/* Chickin Approval Modal */} + { + closeChickinApproveModal(); + chickinApproveModal.closeModal(); + }, + }} + primaryButton={{ + text: 'Ya', + color: 'success', + onClick: async (notes) => { + if (chickinApproveCallback) { + setChickinApproveLoading(true); + try { + await chickinApproveCallback(notes); + } finally { + setChickinApproveLoading(false); + closeChickinApproveModal(); + chickinApproveModal.closeModal(); + } + } + }, + isLoading: isChickinApproveLoading, + }} + /> ); }; diff --git a/src/stores/production/chickin/chickin.store.ts b/src/stores/production/chickin/chickin.store.ts new file mode 100644 index 00000000..697b1de4 --- /dev/null +++ b/src/stores/production/chickin/chickin.store.ts @@ -0,0 +1,19 @@ +'use client'; + +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { createChickinApprovalSlice } from '@/stores/production/chickin/slices/chickin-approval.slice'; +import { ChickinApprovalSlice } from '@/stores/production/chickin/slices/chickin-approval.slice'; + +export type ChickinStore = ChickinApprovalSlice; + +export const useChickinStore = create()( + devtools( + (...args) => ({ + ...createChickinApprovalSlice(...args), + }), + { + name: 'ChickinStore', + } + ) +); diff --git a/src/stores/production/chickin/slices/chickin-approval.slice.ts b/src/stores/production/chickin/slices/chickin-approval.slice.ts new file mode 100644 index 00000000..30f0a857 --- /dev/null +++ b/src/stores/production/chickin/slices/chickin-approval.slice.ts @@ -0,0 +1,58 @@ +import { StateCreator } from 'zustand'; +import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang'; + +export type ChickinApprovalSlice = { + // State + isChickinApproveModalOpen: boolean; + selectedChickinForApproval: ProjectFlockKandang | null; + isChickinApproveLoading: boolean; + chickinApproveCallback: ((notes?: string) => Promise) | null; + + // Actions + openChickinApproveModal: ( + data: ProjectFlockKandang, + callback: (notes?: string) => Promise + ) => void; + closeChickinApproveModal: () => void; + setChickinApproveLoading: (loading: boolean) => void; + resetChickinApproval: () => void; +}; + +export const createChickinApprovalSlice: StateCreator< + ChickinApprovalSlice, + [], + [], + ChickinApprovalSlice +> = (set) => ({ + // Initial state + isChickinApproveModalOpen: false, + selectedChickinForApproval: null, + isChickinApproveLoading: false, + chickinApproveCallback: null, + + // Actions + openChickinApproveModal: (data, callback) => + set({ + isChickinApproveModalOpen: true, + selectedChickinForApproval: data, + chickinApproveCallback: callback, + }), + + closeChickinApproveModal: () => + set({ + isChickinApproveModalOpen: false, + selectedChickinForApproval: null, + chickinApproveCallback: null, + }), + + setChickinApproveLoading: (loading) => + set({ isChickinApproveLoading: loading }), + + resetChickinApproval: () => + set({ + isChickinApproveModalOpen: false, + selectedChickinForApproval: null, + isChickinApproveLoading: false, + chickinApproveCallback: null, + }), +}); From e22f95cc58fe77ba5666f5baf972b34e6af69f10 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 15:28:27 +0700 Subject: [PATCH 33/35] refactor(FE): Remove unused variable `approval.step_name` in RecordingTable --- src/components/pages/production/recording/RecordingTable.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/pages/production/recording/RecordingTable.tsx b/src/components/pages/production/recording/RecordingTable.tsx index a67f44f9..13068563 100644 --- a/src/components/pages/production/recording/RecordingTable.tsx +++ b/src/components/pages/production/recording/RecordingTable.tsx @@ -852,8 +852,7 @@ const RecordingTable = () => { const status = approval.action; const statusColor = getStatusBadgeColor(status); - - const statusText = approval.step_name || getStatusText(status); + const statusText = getStatusText(status); return ( Date: Thu, 19 Feb 2026 15:35:08 +0700 Subject: [PATCH 34/35] refactor(FE): Update status mappings for "CREATED" to "Pengajuan" --- .../pages/production/recording/RecordingTable.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/pages/production/recording/RecordingTable.tsx b/src/components/pages/production/recording/RecordingTable.tsx index 13068563..29f2a82a 100644 --- a/src/components/pages/production/recording/RecordingTable.tsx +++ b/src/components/pages/production/recording/RecordingTable.tsx @@ -41,7 +41,8 @@ const statusTextMap: Record = { Disetujui: 'Disetujui', REJECTED: 'Ditolak', Ditolak: 'Ditolak', - CREATED: 'Dibuat', + CREATED: 'Pengajuan', + Pengajuan: 'Pengajuan', UPDATED: 'Diperbarui', }; @@ -59,13 +60,11 @@ const statusBadgeColorMap: Record = { rejected: 'error', ditolak: 'error', CREATED: 'neutral', - Dibuat: 'neutral', + Pengajuan: 'neutral', created: 'neutral', - dibuat: 'neutral', + pengajuan: 'neutral', UPDATED: 'warning', - Diperbarui: 'warning', updated: 'warning', - diperbarui: 'warning', }; const getStatusBadgeColor = (status: string): Color => { From 1a137e7500d638fcb4bb786b53dbf5194a8cbc4e Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 19 Feb 2026 15:37:46 +0700 Subject: [PATCH 35/35] refactor(FE): Normalize status keys to uppercase in status utilities --- .../production/recording/RecordingTable.tsx | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/components/pages/production/recording/RecordingTable.tsx b/src/components/pages/production/recording/RecordingTable.tsx index 29f2a82a..65e658f9 100644 --- a/src/components/pages/production/recording/RecordingTable.tsx +++ b/src/components/pages/production/recording/RecordingTable.tsx @@ -38,37 +38,26 @@ import { Color } from '@/types/theme'; // ===== STATUS BADGE UTILITIES ===== const statusTextMap: Record = { APPROVED: 'Disetujui', - Disetujui: 'Disetujui', REJECTED: 'Ditolak', - Ditolak: 'Ditolak', CREATED: 'Pengajuan', - Pengajuan: 'Pengajuan', UPDATED: 'Diperbarui', }; const getStatusText = (status: string): string => { - return statusTextMap[status] || status; + const normalizedStatus = status.toUpperCase(); + return statusTextMap[normalizedStatus] || status; }; const statusBadgeColorMap: Record = { APPROVED: 'success', - Disetujui: 'success', - approved: 'success', - disetujui: 'success', REJECTED: 'error', - Ditolak: 'error', - rejected: 'error', - ditolak: 'error', CREATED: 'neutral', - Pengajuan: 'neutral', - created: 'neutral', - pengajuan: 'neutral', UPDATED: 'warning', - updated: 'warning', }; const getStatusBadgeColor = (status: string): Color => { - return statusBadgeColorMap[status] || 'neutral'; + const normalizedStatus = status.toUpperCase(); + return statusBadgeColorMap[normalizedStatus] || 'neutral'; }; const RowOptionsMenu = ({