diff --git a/src/components/pages/inventory/product/detail/InventoryProductDetail.tsx b/src/components/pages/inventory/product/detail/InventoryProductDetail.tsx index 715a7a43..428a0953 100644 --- a/src/components/pages/inventory/product/detail/InventoryProductDetail.tsx +++ b/src/components/pages/inventory/product/detail/InventoryProductDetail.tsx @@ -1,9 +1,16 @@ +'use client'; + import Card from '@/components/Card'; +import { OptionType } from '@/components/input/SelectInput'; import { FormHeader } from '@/components/helper/form/FormHeader'; +import ButtonFilter from '@/components/helper/ButtonFilter'; import RequirePermission from '@/components/helper/RequirePermission'; +import { useModal } from '@/components/Modal'; +import StockLogFilterModal from '@/components/pages/inventory/product/detail/StockLogFilterModal'; import StockLogTable from '@/components/pages/inventory/product/detail/StockLogTable'; import StockProductWarehouseTable from '@/components/pages/inventory/product/detail/StockProductWarehouseTable'; import { formatCurrency, formatNumber } from '@/lib/helper'; +import { useTableFilter } from '@/services/hooks/useTableFilter'; import { InventoryProduct } from '@/types/api/inventory/product'; import { useMemo } from 'react'; @@ -12,6 +19,35 @@ const InventoryProductDetail = ({ }: { inventoryProduct?: InventoryProduct; }) => { + const filterModal = useModal(); + + const { state: filterState, updateFilter } = useTableFilter<{ + warehouse_ids: OptionType[]; + }>({ + initial: { + warehouse_ids: [], + }, + persist: true, + storeName: 'inventory-product-stock-log-filter', + }); + + const filteredProductWarehouses = useMemo(() => { + const warehouses = inventoryProduct?.product_warehouses ?? []; + if (!filterState.warehouse_ids?.length) return warehouses; + const selectedIds = new Set(filterState.warehouse_ids.map((w) => w.value)); + return warehouses.filter((pw) => selectedIds.has(pw.warehouse_id)); + }, [inventoryProduct?.product_warehouses, filterState.warehouse_ids]); + + const filterSubmitHandler = (values: { + warehouse_ids: OptionType[]; + }) => { + updateFilter('warehouse_ids', values.warehouse_ids, true); + }; + + const filterResetHandler = () => { + updateFilter('warehouse_ids', [], true); + }; + return (
- {inventoryProduct?.product_warehouses?.map((productWarehouse) => ( +
+ +
+ {filteredProductWarehouses.map((productWarehouse) => ( ))}
+ +
); }; diff --git a/src/components/pages/inventory/product/detail/StockLogFilterModal.tsx b/src/components/pages/inventory/product/detail/StockLogFilterModal.tsx new file mode 100644 index 00000000..db93236f --- /dev/null +++ b/src/components/pages/inventory/product/detail/StockLogFilterModal.tsx @@ -0,0 +1,115 @@ +'use client'; + +import Button from '@/components/Button'; +import SelectInputCheckbox from '@/components/input/SelectInputCheckbox'; +import { OptionType } from '@/components/input/SelectInput'; +import Modal from '@/components/Modal'; +import { ProductWarehouseStock } from '@/types/api/inventory/product'; +import { Icon } from '@iconify/react'; +import { useFormik } from 'formik'; +import { RefObject, useCallback } from 'react'; + +interface StockLogFilterModalProps { + ref: RefObject; + productWarehouses: ProductWarehouseStock[]; + initialValues: { + warehouse_ids: OptionType[]; + }; + onSubmit: (values: { warehouse_ids: OptionType[] }) => void; + onReset: () => void; +} + +const StockLogFilterModal = ({ + ref, + productWarehouses, + initialValues, + onSubmit, + onReset, +}: StockLogFilterModalProps) => { + const closeModalHandler = () => { + ref.current?.close(); + }; + + const warehouseOptions: OptionType[] = productWarehouses.map( + (pw) => ({ + label: pw.warehouse_name, + value: pw.warehouse_id, + }) + ); + + const formik = useFormik({ + initialValues, + enableReinitialize: true, + onSubmit: (values) => { + onSubmit(values); + closeModalHandler(); + }, + }); + + const { resetForm } = formik; + + const formikResetHandler = useCallback(() => { + resetForm({ values: { warehouse_ids: [] } }); + onReset(); + closeModalHandler(); + }, [resetForm, onReset]); + + return ( + +
+
+
+ +

Filter Stock Log

+
+ +
+ +
+ + formik.setFieldValue('warehouse_ids', val as OptionType[]) + } + isMulti + /> +
+ +
+ + +
+
+
+ ); +}; + +export default StockLogFilterModal; diff --git a/src/components/pages/inventory/product/detail/StockLogTable.tsx b/src/components/pages/inventory/product/detail/StockLogTable.tsx index b92a4512..ff81c513 100644 --- a/src/components/pages/inventory/product/detail/StockLogTable.tsx +++ b/src/components/pages/inventory/product/detail/StockLogTable.tsx @@ -9,7 +9,7 @@ import { ProductWarehouseStock, StockLog } from '@/types/api/inventory/product'; import { ColumnDef } from '@tanstack/react-table'; import { FileDown } from 'lucide-react'; import toast from 'react-hot-toast'; -import { useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import useSWR from 'swr'; const stockLogTableColumns: (warehouseName: string) => ColumnDef[] = ( @@ -80,6 +80,23 @@ const StockLogTable = ({ productWarehouse: ProductWarehouseStock; }) => { const [isExportLoading, setIsExportLoading] = useState(false); + const [hasBeenVisible, setHasBeenVisible] = useState(false); + const containerRef = useRef(null); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setHasBeenVisible(true); + observer.disconnect(); + } + }, + { threshold: 0.1 } + ); + + if (containerRef.current) observer.observe(containerRef.current); + return () => observer.disconnect(); + }, []); const { state: tableFilterState, @@ -108,7 +125,9 @@ const StockLogTable = ({ }; const { data: stockLogsResponse, isLoading: isLoadingStockLogs } = useSWR( - `${StockLogApi.basePath}${getTableFilterQueryString()}`, + hasBeenVisible + ? `${StockLogApi.basePath}${getTableFilterQueryString()}` + : null, StockLogApi.getAllFetcher ); @@ -117,46 +136,48 @@ const StockLogTable = ({ : []; return ( - -
- -
- - data={stockLogs} - columns={stockLogTableColumns(productWarehouse.warehouse_name)} - page={tableFilterState.page ?? 0} - pageSize={tableFilterState.pageSize} - onPageChange={setPage} - onPageSizeChange={setPageSize} - isLoading={isLoadingStockLogs} - totalItems={ - isResponseSuccess(stockLogsResponse) - ? stockLogsResponse.meta?.total_results - : 0 - } +
+ - + > +
+ +
+ + data={stockLogs} + columns={stockLogTableColumns(productWarehouse.warehouse_name)} + page={tableFilterState.page ?? 0} + pageSize={tableFilterState.pageSize} + onPageChange={setPage} + onPageSizeChange={setPageSize} + isLoading={isLoadingStockLogs} + totalItems={ + isResponseSuccess(stockLogsResponse) + ? stockLogsResponse.meta?.total_results + : 0 + } + className={{ + containerClassName: 'mt-4 mb-0', + tableWrapperClassName: 'overflow-x-auto min-h-full!', + tableClassName: 'font-inter w-full table-auto min-h-full!', + headerRowClassName: 'border-b border-b-gray-200', + headerColumnClassName: + 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', + bodyRowClassName: 'border-b border-b-gray-200', + bodyColumnClassName: + 'px-6 py-3 last:flex last:flex-row last:justify-end', + }} + /> + +
); }; diff --git a/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx b/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx index 8c3e30e7..63d89661 100644 --- a/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx +++ b/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx @@ -113,6 +113,7 @@ const CATEGORY_LABELS: { [key: string]: string } = { pullet_close: 'Pullet Close', produksi_open: 'Produksi Open', produksi_close: 'Produksi Close', + empty_kandang: 'Kandang Kosong', }; const TIME_TYPE_ORDER = ['Umum', 'Pagi', 'Siang', 'Sore', 'Malam'];