From 5c00893ea3fbf4db448fe34b8d2812e1a0f471e3 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Fri, 13 Feb 2026 09:24:42 +0700 Subject: [PATCH] refactor(FE): Refactor production result components and improve UI --- src/app/report/production-result/page.tsx | 4 +- ...oductionResultProjectFlockKandangTable.tsx | 137 +-- .../ProductionResultTabs.tsx | 39 + .../filter/ProductionResultFilter.ts | 41 +- .../skeleton/ProductionResultSkeleton.tsx | 15 +- .../tab/ProductionResultTab.tsx | 919 +++++++++++------- 6 files changed, 682 insertions(+), 473 deletions(-) create mode 100644 src/components/pages/report/production-result/ProductionResultTabs.tsx diff --git a/src/app/report/production-result/page.tsx b/src/app/report/production-result/page.tsx index fb8f2a0c..4c9ea02b 100644 --- a/src/app/report/production-result/page.tsx +++ b/src/app/report/production-result/page.tsx @@ -1,9 +1,9 @@ -import ProductionResultContent from '@/components/pages/report/production-result/tab/ProductionResultTab'; +import ProductionResultTabs from '@/components/pages/report/production-result/ProductionResultTabs'; const ProductionResultReportPage = () => { return (
- +
); }; diff --git a/src/components/pages/report/production-result/ProductionResultProjectFlockKandangTable.tsx b/src/components/pages/report/production-result/ProductionResultProjectFlockKandangTable.tsx index e1dfd515..ded97d02 100644 --- a/src/components/pages/report/production-result/ProductionResultProjectFlockKandangTable.tsx +++ b/src/components/pages/report/production-result/ProductionResultProjectFlockKandangTable.tsx @@ -4,12 +4,10 @@ import { useEffect, useState } from 'react'; 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 { ProductionResult } from '@/types/api/report/production-result'; import { useTableFilter } from '@/services/hooks/useTableFilter'; @@ -52,8 +50,6 @@ const ProductionResultProjectFlockKandangTable = ({ } ); - const [open, setOpen] = useState(false); - const [sorting, setSorting] = useState([]); const productionResultColumns: ColumnDef[] = [ @@ -270,93 +266,60 @@ const ProductionResultProjectFlockKandangTable = ({ } }, [sorting]); - useEffect(() => { - if (!open) { - setOpen( + return ( + 0 : false - ); - } - }, [productionResults, isResponseSuccess]); - - return ( - - -
{kandangName}
- - - + + data={ + isResponseSuccess(productionResults) ? productionResults?.data : [] } - className='w-full!' - titleClassName='w-full p-0!' - > -
- {/*
-
- -
-
*/} - - - data={ - isResponseSuccess(productionResults) - ? productionResults?.data - : [] - } - columns={productionResultColumns} - pageSize={tableFilterState.pageSize} - onPageSizeChange={setPageSize} - rowOptions={[10, 20, 50, 100]} - page={ - isResponseSuccess(productionResults) - ? productionResults?.meta?.page - : 0 - } - totalItems={ - isResponseSuccess(productionResults) - ? productionResults?.meta?.total_results - : 0 - } - onPageChange={setPage} - isLoading={isLoadingProductionResults} - sorting={sorting} - setSorting={setSorting} - renderFooter={false} - className={{ - containerClassName: cn({ - 'w-full mb-20': - isResponseSuccess(productionResults) && - productionResults?.data?.length === 0, - }), - headerColumnClassName: - 'px-4 py-3 border-x border-base-content/10 text-base-content/50', - }} - /> -
-
+ columns={productionResultColumns} + pageSize={tableFilterState.pageSize} + onPageSizeChange={setPageSize} + rowOptions={[10, 20, 50, 100]} + page={ + isResponseSuccess(productionResults) + ? productionResults?.meta?.page + : 0 + } + totalItems={ + isResponseSuccess(productionResults) + ? productionResults?.meta?.total_results + : 0 + } + onPageChange={setPage} + isLoading={isLoadingProductionResults} + sorting={sorting} + setSorting={setSorting} + renderFooter={false} + 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/report/production-result/ProductionResultTabs.tsx b/src/components/pages/report/production-result/ProductionResultTabs.tsx new file mode 100644 index 00000000..2bd7765f --- /dev/null +++ b/src/components/pages/report/production-result/ProductionResultTabs.tsx @@ -0,0 +1,39 @@ +'use client'; + +import { useState } from 'react'; +import Tabs from '@/components/Tabs'; +import ProductionResultTab from '@/components/pages/report/production-result/tab/ProductionResultTab'; +import { useReportTabStore } from '@/stores/report/report-tab.store'; + +const ProductionResultTabs = () => { + const [activeTabId, setActiveTabId] = useState('1'); + const tabActions = useReportTabStore((state) => state.tabActions); + + const tabs = [ + { + id: '1', + label: 'Hasil Produksi', + content: , + }, + ]; + + return ( +
+ +
+ ); +}; + +export default ProductionResultTabs; diff --git a/src/components/pages/report/production-result/filter/ProductionResultFilter.ts b/src/components/pages/report/production-result/filter/ProductionResultFilter.ts index 1c1979eb..3281bb19 100644 --- a/src/components/pages/report/production-result/filter/ProductionResultFilter.ts +++ b/src/components/pages/report/production-result/filter/ProductionResultFilter.ts @@ -1,48 +1,17 @@ import * as yup from 'yup'; -export type ProductionResultFilterType = { +export type ProductionResultFilterProps = { area_id: string | null; location_id: string | null; project_flock_id: string | null; kandang_id: string | null; - date_start: string | null; - date_end: string | null; - sort_by: string | null; - show_unrecorded: boolean | null; }; export const ProductionResultFilterSchema = yup.object({ - area_id: yup.string().nullable(), - location_id: yup.string().nullable(), - project_flock_id: yup.string().nullable(), - kandang_id: yup.string().nullable(), - date_start: yup - .string() - .nullable() - .test( - 'is-valid-date', - 'Tanggal mulai tidak valid', - (value) => !value || !isNaN(Date.parse(value)) - ), - date_end: yup - .string() - .nullable() - .test( - 'is-valid-date', - 'Tanggal akhir tidak valid', - (value) => !value || !isNaN(Date.parse(value)) - ) - .test( - 'is-after-start', - 'Tanggal akhir tidak boleh lebih awal dari tanggal mulai', - function (value) { - const { date_start } = this.parent; - if (!date_start || !value) return true; - return new Date(value) >= new Date(date_start); - } - ), - sort_by: yup.string().nullable(), - show_unrecorded: yup.boolean().nullable(), + area_id: yup.string().required('Area wajib dipilih'), + location_id: yup.string().required('Lokasi wajib dipilih'), + project_flock_id: yup.string().required('Project Flock wajib dipilih'), + kandang_id: yup.string().required('Kandang wajib dipilih'), }); export type ProductionResultFilterValues = yup.InferType< diff --git a/src/components/pages/report/production-result/skeleton/ProductionResultSkeleton.tsx b/src/components/pages/report/production-result/skeleton/ProductionResultSkeleton.tsx index c8cd63d8..07d33233 100644 --- a/src/components/pages/report/production-result/skeleton/ProductionResultSkeleton.tsx +++ b/src/components/pages/report/production-result/skeleton/ProductionResultSkeleton.tsx @@ -4,13 +4,26 @@ import Table from '@/components/Table'; import { ProductionResult } from '@/types/api/report/production-result'; import { ColumnDef } from '@tanstack/react-table'; +type ProductionResultColumn = + | ColumnDef + | { + header: string; + columns: Array<{ + header: string; + accessorKey?: string; + cell?: (props: { + row: { original: ProductionResult }; + }) => React.ReactNode; + }>; + }; + const ProductionResultSkeleton = ({ columns, icon, title, subtitle, }: { - columns: ColumnDef[]; + columns: ProductionResultColumn[]; icon: React.ReactNode; title: string; subtitle: string; diff --git a/src/components/pages/report/production-result/tab/ProductionResultTab.tsx b/src/components/pages/report/production-result/tab/ProductionResultTab.tsx index ac5e76fd..2d7915e8 100644 --- a/src/components/pages/report/production-result/tab/ProductionResultTab.tsx +++ b/src/components/pages/report/production-result/tab/ProductionResultTab.tsx @@ -1,6 +1,7 @@ 'use client'; -import React, { useState } from 'react'; +import React, { useState, useCallback, useEffect, useMemo } from 'react'; +import useSWR from 'swr'; import { generateProductionResultExcel } from '../export/ProductionResultExportXLSX'; import toast from 'react-hot-toast'; @@ -13,8 +14,9 @@ import SelectInput, { } from '@/components/input/SelectInput'; import Menu from '@/components/menu/Menu'; import MenuItem from '@/components/menu/MenuItem'; -import Card from '@/components/Card'; import ProductionResultProjectFlockKandangTable from '@/components/pages/report/production-result/ProductionResultProjectFlockKandangTable'; +import { useFormik } from 'formik'; +import { ProductionResultFilterSchema } from '@/components/pages/report/production-result/filter/ProductionResultFilter'; import { BaseKandang } from '@/types/api/master-data/kandang'; import { AreaApi, LocationApi } from '@/services/api/master-data'; @@ -26,87 +28,248 @@ import { BaseProjectFlockKandang, ProjectFlockKandang, } from '@/types/api/production/project-flock-kandang'; -import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; -import Pagination from '@/components/Pagination'; +import { isResponseSuccess } from '@/lib/api-helper'; import { ProductionResultReportApi } from '@/services/api/report/production-result'; import { BaseApiResponse } from '@/types/api/api-general'; import { httpClient } from '@/services/http/client'; +import { ColumnDef } from '@tanstack/react-table'; import { ProductionResult } from '@/types/api/report/production-result'; import ProductionResultReportPDF from '../export/ProductionResultExportPDF'; import { pdf } from '@react-pdf/renderer'; +import { useReportTabStore } from '@/stores/report/report-tab.store'; +import Modal, { useModal } from '@/components/Modal'; +import { cn, formatNumber } from '@/lib/helper'; +import Pagination from '@/components/Pagination'; +import ProductionResultSkeleton from '@/components/pages/report/production-result/skeleton/ProductionResultSkeleton'; -const ProductionResultContent = () => { - const [projectFlockKandangs, setProjectFlockKandangs] = useState< - ProjectFlockKandang[] | null - >(null); - const [projectFlockKandangMetadata, setProjectFlockKandangMetadata] = - useState< - | { - page: number; - limit: number; - total_pages: number; - total_results: number; - } - | undefined - >(undefined); +interface ProductionResultTabProps { + tabId: string; +} +interface FilterParams { + area_id?: string; + location_id?: string; + project_flock_id?: string; + project_flock_kandang_id?: string; +} + +type ProductionResultFilterFormValues = { + area_id: OptionType | null; + location_id: OptionType | null; + project_flock_id: OptionType | null; + kandang_id: OptionType | null; +}; + +const ProductionResultContent = ({ tabId }: ProductionResultTabProps) => { + // ===== STATE MANAGEMENT ===== + const [isPdfExportLoading, setIsPdfExportLoading] = useState(false); + const [isExcelExportLoading, setIsExcelExportLoading] = useState(false); + const isAnyExportLoading = isPdfExportLoading || isExcelExportLoading; + + // ===== SUBMISSION STATE ===== + const [isSubmitted, setIsSubmitted] = useState(false); + const [filterParams, setFilterParams] = useState({}); + + // ===== PAGINATION STATE ===== const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); - const [isLoadingSearch, setIsLoadingSearch] = useState(false); + const filterModal = useModal(); - const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] = - useState(false); + // ===== TABLE COLUMNS ===== + const productionResultColumns: ColumnDef[] = [ + { + header: 'No', + cell: (props) => props.row.index + 1, + }, + { + accessorKey: 'woa', + header: 'WOA', + }, + { + accessorKey: 'bw', + header: 'BW', + cell: (props) => formatNumber(props.row.original.bw), + }, + { + accessorKey: 'std_bw', + header: 'STD BW', + cell: (props) => formatNumber(props.row.original.std_bw), + }, + { + accessorKey: 'uniformity', + header: 'Uniformity', + cell: (props) => formatNumber(props.row.original.uniformity), + }, + { + accessorKey: 'std_uniformity', + header: 'STD Uniformity', + }, + { + accessorKey: 'dep_kum', + header: 'Dep Kum', + cell: (props) => formatNumber(props.row.original.dep_kum), + }, + { + accessorKey: 'dep_std', + header: 'Dep STD', + cell: (props) => formatNumber(props.row.original.dep_std), + }, + { + header: 'Butiran', + columns: [ + { + accessorKey: 'butiran_utuh', + header: 'Utuh', + cell: (props) => formatNumber(props.row.original.butiran_utuh), + }, + { + accessorKey: 'butiran_putih', + header: 'Putih', + cell: (props) => formatNumber(props.row.original.butiran_putih), + }, + { + accessorKey: 'butiran_retak', + header: 'Retak', + cell: (props) => formatNumber(props.row.original.butiran_retak), + }, + { + accessorKey: 'butiran_pecah', + header: 'Pecah', + cell: (props) => formatNumber(props.row.original.butiran_pecah), + }, + { + accessorKey: 'butiran_jumlah', + header: 'Jumlah (Butir)', + cell: (props) => formatNumber(props.row.original.butiran_jumlah), + }, + { + accessorKey: 'total_butir', + header: 'Total Butir', + cell: (props) => formatNumber(props.row.original.total_butir), + }, + ], + }, + { + header: 'Kg', + columns: [ + { + accessorKey: 'kg_utuh', + header: 'Utuh (Kg)', + cell: (props) => formatNumber(props.row.original.kg_utuh), + }, + { + accessorKey: 'kg_putih', + header: 'Putih (Kg)', + cell: (props) => formatNumber(props.row.original.kg_putih), + }, + { + accessorKey: 'kg_retak', + header: 'Retak (Kg)', + cell: (props) => formatNumber(props.row.original.kg_retak), + }, + { + accessorKey: 'kg_pecah', + header: 'Pecah (Kg)', + cell: (props) => formatNumber(props.row.original.kg_pecah), + }, + { + accessorKey: 'kg_jumlah', + header: 'Jumlah (Kg)', + cell: (props) => formatNumber(props.row.original.kg_jumlah), + }, + { + accessorKey: 'total_kg', + header: 'Total Kg', + cell: (props) => formatNumber(props.row.original.total_kg), + }, + ], + }, + { + header: '%', + columns: [ + { + accessorKey: 'persen_utuh', + header: 'Utuh', + cell: (props) => formatNumber(props.row.original.persen_utuh), + }, + { + accessorKey: 'persen_putih', + header: 'Putih', + cell: (props) => formatNumber(props.row.original.persen_putih), + }, + { + accessorKey: 'persen_retak', + header: 'Retak', + cell: (props) => formatNumber(props.row.original.persen_retak), + }, + { + accessorKey: 'persen_pecah', + header: 'Pecah', + cell: (props) => formatNumber(props.row.original.persen_pecah), + }, + ], + }, + ]; - const [isLoadingExportingToPdf, setIsLoadingExportingToPdf] = useState(false); - - const [selectedArea, setSelectedArea] = useState(null); - const [selectedLocation, setSelectedLocation] = useState( - null - ); - const [selectedProjectFlock, setSelectedProjectFlock] = - useState(null); - const [selectedProjectFlockKandang, setSelectedProjectFlockKandang] = - useState(null); + // ===== FORMIK SETUP ===== + const formik = useFormik({ + initialValues: { + area_id: null, + location_id: null, + project_flock_id: null, + kandang_id: null, + }, + validationSchema: ProductionResultFilterSchema, + onSubmit: (values) => { + setFilterParams({ + area_id: values.area_id?.value + ? String(values.area_id.value) + : undefined, + location_id: values.location_id?.value + ? String(values.location_id.value) + : undefined, + project_flock_id: values.project_flock_id?.value + ? String(values.project_flock_id.value) + : undefined, + project_flock_kandang_id: values.kandang_id?.value + ? String(values.kandang_id.value) + : undefined, + }); + filterModal.closeModal(); + setIsSubmitted(true); + setPage(1); + }, + onReset: () => { + setFilterParams({}); + setIsSubmitted(false); + setPage(1); + }, + }); + // ===== OPTIONS ===== const { setInputValue: setAreaInputValue, options: areaOptions, - isLoadingOptions: isLoadingAreaOptions, + isLoadingOptions: isLoadingAreas, loadMore: loadMoreAreas, - } = useSelect(AreaApi.basePath, 'id', 'name'); - - const areaChangeHandler = (val: OptionType | OptionType[] | null) => { - setSelectedArea(val as OptionType); - - setSelectedLocation(null); - - setSelectedProjectFlock(null); - - setSelectedProjectFlockKandang(null); - }; + } = useSelect(AreaApi.basePath, 'id', 'name', 'search'); const { setInputValue: setLocationInputValue, options: locationOptions, - isLoadingOptions: isLoadingLocationOptions, + isLoadingOptions: isLoadingLocations, loadMore: loadMoreLocations, } = useSelect(LocationApi.basePath, 'id', 'name', 'search', { - area_id: selectedArea ? ((selectedArea as OptionType).value as string) : '', + area_id: formik.values.area_id?.value + ? String(formik.values.area_id.value) + : '', }); - const locationChangeHandler = (val: OptionType | OptionType[] | null) => { - setSelectedLocation(val as OptionType); - - setSelectedProjectFlock(null); - - setSelectedProjectFlockKandang(null); - }; - const { setInputValue: setProjectFlockInputValue, options: projectFlockOptions, - isLoadingOptions: isLoadingProjectFlockOptions, + isLoadingOptions: isLoadingProjectFlocks, loadMore: loadMoreProjectFlocks, } = useSelect( ProjectFlockApi.basePath, @@ -114,26 +277,20 @@ const ProductionResultContent = () => { 'flock_name', 'search', { - area_id: selectedArea - ? ((selectedArea as OptionType).value as string) + area_id: formik.values.area_id?.value + ? String(formik.values.area_id.value) : '', - location_id: selectedLocation - ? ((selectedLocation as OptionType).value as string) + location_id: formik.values.location_id?.value + ? String(formik.values.location_id.value) : '', category: 'LAYING', } ); - const projectFlockChangeHandler = (val: OptionType | OptionType[] | null) => { - setSelectedProjectFlock(val as OptionType); - - setSelectedProjectFlockKandang(null); - }; - const { setInputValue: setProjectFlockKandangInputValue, options: projectFlockKandangOptions, - isLoadingOptions: isLoadingProjectFlockKandangOptions, + isLoadingOptions: isLoadingProjectFlockKandangs, loadMore: loadMoreProjectFlockKandangs, } = useSelect( ProjectFlockKandangApi.basePath, @@ -141,37 +298,103 @@ const ProductionResultContent = () => { 'kandang.name', 'search', { - area_id: selectedArea - ? ((selectedArea as OptionType).value as string) + area_id: formik.values.area_id?.value + ? String(formik.values.area_id.value) : '', - location_id: selectedLocation - ? ((selectedLocation as OptionType).value as string) + location_id: formik.values.location_id?.value + ? String(formik.values.location_id.value) : '', - project_flock_id: selectedProjectFlock - ? ((selectedProjectFlock as OptionType).value as string) + project_flock_id: formik.values.project_flock_id?.value + ? String(formik.values.project_flock_id.value) : '', } ); - const projectFlockKandangChangeHandler = ( - val: OptionType | OptionType[] | null - ) => { - setSelectedProjectFlockKandang(val as OptionType); - }; + // ===== FILTER HELPERS ===== + const areaValue = useMemo( + () => formik.values.area_id, + [formik.values.area_id] + ); - const exportToExcelHandler = async () => { - setIsLoadingExportingToExcel(true); + const locationValue = useMemo( + () => formik.values.location_id, + [formik.values.location_id] + ); + + const projectFlockValue = useMemo( + () => formik.values.project_flock_id, + [formik.values.project_flock_id] + ); + + const projectFlockKandangValue = useMemo( + () => formik.values.kandang_id, + [formik.values.kandang_id] + ); + + // ===== ACTIVE FILTERS COUNT ===== + const activeFiltersCount = useMemo(() => { + let count = 0; + + if (filterParams.area_id) count += 1; + if (filterParams.location_id) count += 1; + if (filterParams.project_flock_id) count += 1; + if (filterParams.project_flock_kandang_id) count += 1; + + return count; + }, [filterParams]); + + const hasFilters = activeFiltersCount > 0; + + // ===== DATA FETCHING ===== + const { data: projectFlockKandangsData, isLoading } = useSWR< + BaseApiResponse + >( + isSubmitted + ? () => { + const params = new URLSearchParams(); + if (filterParams.area_id) + params.append('area_id', filterParams.area_id); + if (filterParams.project_flock_id) + params.append('project_flock_id', filterParams.project_flock_id); + params.append('page', String(page)); + params.append('limit', String(pageSize)); + + return [`/production/project-flock-kandangs?${params.toString()}`]; + } + : null, + ([url]: string[]) => httpClient>(url) + ); + + const projectFlockKandangs = useMemo( + () => + isResponseSuccess(projectFlockKandangsData) + ? projectFlockKandangsData.data + : null, + [projectFlockKandangsData] + ); + + const projectFlockKandangMetadata = useMemo( + () => + isResponseSuccess(projectFlockKandangsData) + ? projectFlockKandangsData.meta + : undefined, + [projectFlockKandangsData] + ); + + // ===== EXPORT HANDLERS ===== + const exportToExcelHandler = useCallback(async () => { + setIsExcelExportLoading(true); try { - let projectFlockKandangsData: BaseProjectFlockKandang[] = []; + let projectFlockKandangsFetch: BaseProjectFlockKandang[] = []; - if (selectedProjectFlockKandang) { + if (filterParams.project_flock_kandang_id) { const projectFlockKandangResponse = await ProjectFlockKandangApi.getSingle( - selectedProjectFlockKandang?.value as number + Number(filterParams.project_flock_kandang_id) ); - projectFlockKandangsData = isResponseSuccess( + projectFlockKandangsFetch = isResponseSuccess( projectFlockKandangResponse ) ? [projectFlockKandangResponse.data] @@ -179,11 +402,11 @@ const ProductionResultContent = () => { } else { const projectFlockKandangsResponse = await ProjectFlockKandangApi.getAll({ - area_id: selectedArea?.value, - project_flock_id: selectedProjectFlock?.value, + area_id: filterParams.area_id, + project_flock_id: filterParams.project_flock_id, }); - projectFlockKandangsData = isResponseSuccess( + projectFlockKandangsFetch = isResponseSuccess( projectFlockKandangsResponse ) ? projectFlockKandangsResponse.data @@ -192,7 +415,7 @@ const ProductionResultContent = () => { const productionResultData: ProductionResult[] = []; - for (const kandang of projectFlockKandangsData) { + for (const kandang of projectFlockKandangsFetch) { const getProductionResultPath = `${ProductionResultReportApi.basePath}/${kandang.id}?page=1&limit=100`; const getProductionResultRes = await httpClient< BaseApiResponse @@ -205,7 +428,7 @@ const ProductionResultContent = () => { project_flock: { ...result.project_flock, name: - selectedProjectFlock?.label || + projectFlockValue?.label || kandang.project_flock?.name || `Project Flock #${kandang.project_flock_id}`, category: kandang.project_flock?.category || '', @@ -222,7 +445,7 @@ const ProductionResultContent = () => { if (productionResultData.length === 0) { toast.error('Tidak ada data untuk diexport.'); - setIsLoadingExportingToExcel(false); + setIsExcelExportLoading(false); return; } @@ -233,23 +456,23 @@ const ProductionResultContent = () => { } catch { toast.error('Gagal melakukan export laporan hasil produksi! Coba lagi.'); } finally { - setIsLoadingExportingToExcel(false); + setIsExcelExportLoading(false); } - }; + }, [filterParams, projectFlockValue]); - const exportToPdfHandler = async () => { - setIsLoadingExportingToPdf(true); + const exportToPdfHandler = useCallback(async () => { + setIsPdfExportLoading(true); try { - let projectFlockKandangsData: BaseProjectFlockKandang[] = []; + let projectFlockKandangsFetch: BaseProjectFlockKandang[] = []; - if (selectedProjectFlockKandang) { + if (filterParams.project_flock_kandang_id) { const projectFlockKandangResponse = await ProjectFlockKandangApi.getSingle( - selectedProjectFlockKandang?.value as number + Number(filterParams.project_flock_kandang_id) ); - projectFlockKandangsData = isResponseSuccess( + projectFlockKandangsFetch = isResponseSuccess( projectFlockKandangResponse ) ? [projectFlockKandangResponse.data] @@ -257,11 +480,11 @@ const ProductionResultContent = () => { } else { const projectFlockKandangsResponse = await ProjectFlockKandangApi.getAll({ - area_id: selectedArea?.value, - project_flock_id: selectedProjectFlock?.value, + area_id: filterParams.area_id, + project_flock_id: filterParams.project_flock_id, }); - projectFlockKandangsData = isResponseSuccess( + projectFlockKandangsFetch = isResponseSuccess( projectFlockKandangsResponse ) ? projectFlockKandangsResponse.data @@ -272,7 +495,7 @@ const ProductionResultContent = () => { projectFlockKandang: BaseProjectFlockKandang; productionResult: ProductionResult[] | null; }[] = await Promise.all( - projectFlockKandangsData.map(async (projectFlockKandang) => { + projectFlockKandangsFetch.map(async (projectFlockKandang) => { const getProductionResultPath = `${ProductionResultReportApi.basePath}/${projectFlockKandang.id}?page=1&limit=100`; const getProductionResultRes = await httpClient< BaseApiResponse @@ -289,7 +512,7 @@ const ProductionResultContent = () => { if (mappedProductionResults.length === 0) { toast.error('Tidak ada data untuk diexport.'); - setIsLoadingExportingToPdf(false); + setIsPdfExportLoading(false); return; } @@ -312,258 +535,141 @@ const ProductionResultContent = () => { toast.error('Gagal melakukan export laporan hasil produksi! Coba lagi.'); } - setIsLoadingExportingToPdf(false); - }; + setIsPdfExportLoading(false); + }, [filterParams]); - const searchHandler = async () => { - setProjectFlockKandangs(null); - setIsLoadingSearch(true); + // ===== REGISTER TAB ACTIONS TO STORE ===== + const setTabActions = useReportTabStore((state) => state.setTabActions); + const clearTabActions = useReportTabStore((state) => state.clearTabActions); - try { - if (selectedProjectFlockKandang) { - const projectFlockKandangResponse = - await ProjectFlockKandangApi.getSingle( - selectedProjectFlockKandang?.value as number - ); + useEffect(() => { + setTabActions( + tabId, +
+ - if ( - !projectFlockKandangResponse || - isResponseError(projectFlockKandangResponse) - ) { - throw new Error(); - } + + + Export +
+ +
+ + } + align='end' + className={{ + content: + 'mt-1 p-0 w-full shadow-button-soft border border-base-content/10 rounded-lg', + }} + > + + + + +
+
+ ); + }, [ + tabId, + hasFilters, + activeFiltersCount, + isAnyExportLoading, + exportToExcelHandler, + exportToPdfHandler, + setTabActions, + ]); - setProjectFlockKandangs([projectFlockKandangResponse.data]); - setProjectFlockKandangMetadata({ - page: 1, - limit: 10, - total_pages: 1, - total_results: 1, - }); - setIsLoadingSearch(false); - return; - } - - const projectFlockKandangsResponse = await ProjectFlockKandangApi.getAll({ - area_id: selectedArea?.value, - project_flock_id: selectedProjectFlock?.value, - }); - - if ( - !projectFlockKandangsResponse || - isResponseError(projectFlockKandangsResponse) - ) { - throw new Error(); - } - - setProjectFlockKandangs(projectFlockKandangsResponse.data); - setProjectFlockKandangMetadata(projectFlockKandangsResponse.meta); - setIsLoadingSearch(false); - } catch (error) { - toast.error('Gagal mencari data! Coba lagi.'); - setProjectFlockKandangs(null); - setProjectFlockKandangMetadata(undefined); - setIsLoadingSearch(false); - } - }; - - const resetHandler = () => { - setProjectFlockKandangs(null); - setSelectedArea(null); - setSelectedLocation(null); - setSelectedProjectFlock(null); - setSelectedProjectFlockKandang(null); - // resetFilter(); - }; + useEffect(() => { + return () => { + clearTabActions(tabId); + }; + }, [tabId, clearTabActions]); return ( -
- -
-

- Laporan Hasil Produksi -

-
- - {/* Filters */} -
-
- - - - - - - -
- -
-
- - - - - Export{' '} - - - } - > - - - - - -
-
-
-
- -
- {isLoadingSearch && ( - - )} - - {!isLoadingSearch && !projectFlockKandangs && ( -

- Silakan pilih filter dan klik tombol Cari untuk menampilkan data. -

- )} - - {!isLoadingSearch && projectFlockKandangs?.length === 0 && ( -

- Tidak ada data kandang project flock yang dapat ditampilkan. -

- )} - - {!isLoadingSearch && projectFlockKandangs && ( - - {projectFlockKandangs.map((projectFlockKandang) => ( - +
+ {!isSubmitted ? ( + - ))} + } + title='No Filters Selected' + subtitle='Please choose filters to narrow down your results and make your search easier.' + /> + ) : isLoading ? ( +
+ +
+ ) : !projectFlockKandangs || projectFlockKandangs.length === 0 ? ( + + } + title='Data Not Yet Available' + subtitle='Please change your filters to get the data.' + /> + ) : ( + <> + {projectFlockKandangs.map( + (projectFlockKandang: ProjectFlockKandang) => ( + + ) + )} -
+
{ onRowChange={setPageSize} />
- + )}
-
+ + {/* Filter Modal */} + + {/* Modal Header */} +
+
+ +

Filter Data

+
+ +
+ +
+ {/* Modal Body */} +
+ { + formik.setFieldValue('area_id', val); + formik.setFieldValue('location_id', null); + formik.setFieldValue('project_flock_id', null); + formik.setFieldValue('kandang_id', null); + }} + onInputChange={setAreaInputValue} + onMenuScrollToBottom={loadMoreAreas} + isClearable + className={{ wrapper: 'w-full' }} + /> + + { + formik.setFieldValue('location_id', val); + formik.setFieldValue('project_flock_id', null); + formik.setFieldValue('kandang_id', null); + }} + onInputChange={setLocationInputValue} + onMenuScrollToBottom={loadMoreLocations} + isClearable + isDisabled={!formik.values.area_id} + className={{ wrapper: 'w-full' }} + /> + + { + formik.setFieldValue('project_flock_id', val); + formik.setFieldValue('kandang_id', null); + }} + onInputChange={setProjectFlockInputValue} + onMenuScrollToBottom={loadMoreProjectFlocks} + isClearable + isDisabled={!formik.values.location_id} + className={{ wrapper: 'w-full' }} + /> + + { + formik.setFieldValue('kandang_id', val); + }} + onInputChange={setProjectFlockKandangInputValue} + onMenuScrollToBottom={loadMoreProjectFlockKandangs} + isClearable + isDisabled={!formik.values.project_flock_id} + className={{ wrapper: 'w-full' }} + /> +
+ + {/* Modal Footer */} +
+ + +
+
+
+ ); };