diff --git a/src/components/pages/inventory/product/detail/InventoryProductDetail.tsx b/src/components/pages/inventory/product/detail/InventoryProductDetail.tsx index 39609b06..715a7a43 100644 --- a/src/components/pages/inventory/product/detail/InventoryProductDetail.tsx +++ b/src/components/pages/inventory/product/detail/InventoryProductDetail.tsx @@ -1,5 +1,6 @@ import Card from '@/components/Card'; import { FormHeader } from '@/components/helper/form/FormHeader'; +import RequirePermission from '@/components/helper/RequirePermission'; import StockLogTable from '@/components/pages/inventory/product/detail/StockLogTable'; import StockProductWarehouseTable from '@/components/pages/inventory/product/detail/StockProductWarehouseTable'; import { formatCurrency, formatNumber } from '@/lib/helper'; @@ -11,18 +12,6 @@ const InventoryProductDetail = ({ }: { inventoryProduct?: InventoryProduct; }) => { - const stockLogs = useMemo(() => { - return ( - inventoryProduct?.product_warehouses?.flatMap((warehouse) => - warehouse.stock_logs.map((log) => ({ - ...log, - warehouse_name: warehouse.warehouse_name, - warehouse_id: warehouse.warehouse_id, - })) - ) || [] - ); - }, [inventoryProduct]); - return (
- + + {inventoryProduct?.product_warehouses?.map((productWarehouse) => ( + + ))} +
); }; diff --git a/src/components/pages/inventory/product/detail/StockLogTable.tsx b/src/components/pages/inventory/product/detail/StockLogTable.tsx index a8240952..b92a4512 100644 --- a/src/components/pages/inventory/product/detail/StockLogTable.tsx +++ b/src/components/pages/inventory/product/detail/StockLogTable.tsx @@ -1,11 +1,20 @@ +import Button from '@/components/Button'; import Card from '@/components/Card'; import Table from '@/components/Table'; +import { isResponseSuccess } from '@/lib/api-helper'; import { formatDate, formatNumber, formatTitleCase } from '@/lib/helper'; +import { StockLogApi } from '@/services/api/inventory'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { StockLog } from '@/types/api/inventory/product'; +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 useSWR from 'swr'; -const stockLogTableColumns: ColumnDef[] = [ +const stockLogTableColumns: (warehouseName: string) => ColumnDef[] = ( + warehouseName +) => [ { header: 'ID', accessorKey: 'id', @@ -20,6 +29,7 @@ const stockLogTableColumns: ColumnDef[] = [ { header: 'Gudang', accessorKey: 'warehouse_name', + cell: warehouseName, }, { header: 'Stock Akhir', @@ -65,31 +75,77 @@ const stockLogTableColumns: ColumnDef[] = [ ]; const StockLogTable = ({ - stockLogs, + productWarehouse, }: { - stockLogs: (StockLog & { warehouse_name: string; warehouse_id: number })[]; + productWarehouse: ProductWarehouseStock; }) => { - const { state: tableFilterState, setPage, setPageSize } = useTableFilter(); + const [isExportLoading, setIsExportLoading] = useState(false); + + const { + state: tableFilterState, + setPage, + setPageSize, + toQueryString: getTableFilterQueryString, + } = useTableFilter({ + initial: { + product_warehouse_id: productWarehouse.id, + }, + }); + + const handleExportExcel = async () => { + setIsExportLoading(true); + try { + await StockLogApi.exportToExcel( + productWarehouse.warehouse_name, + getTableFilterQueryString() + ); + toast.success('Excel berhasil dibuat dan diunduh.'); + } catch { + toast.error('Gagal membuat Excel. Silakan coba lagi.'); + } finally { + setIsExportLoading(false); + } + }; + + const { data: stockLogsResponse, isLoading: isLoadingStockLogs } = useSWR( + `${StockLogApi.basePath}${getTableFilterQueryString()}`, + StockLogApi.getAllFetcher + ); + + const stockLogs = isResponseSuccess(stockLogsResponse) + ? stockLogsResponse.data + : []; return ( +
+ +
data={stockLogs} - columns={stockLogTableColumns} + columns={stockLogTableColumns(productWarehouse.warehouse_name)} page={tableFilterState.page ?? 0} pageSize={tableFilterState.pageSize} onPageChange={setPage} onPageSizeChange={setPageSize} - totalItems={stockLogs?.length ?? 0} + isLoading={isLoadingStockLogs} + totalItems={ + isResponseSuccess(stockLogsResponse) + ? stockLogsResponse.meta?.total_results + : 0 + } className={{ - containerClassName: 'mt-6', + 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', diff --git a/src/services/api/inventory.ts b/src/services/api/inventory.ts index 70a7c8f9..df72959f 100644 --- a/src/services/api/inventory.ts +++ b/src/services/api/inventory.ts @@ -13,7 +13,9 @@ import { CreateInventoryAdjustmentPayload, InventoryAdjustment, } from '@/types/api/inventory/adjustment'; -import { InventoryProduct } from '@/types/api/inventory/product'; +import { InventoryProduct, StockLog } from '@/types/api/inventory/product'; +import { httpClient } from '../http/client'; +import { formatDate } from '@/lib/helper'; export const ProductWarehouseApi = new BaseApiService< ProductWarehouse, @@ -65,3 +67,41 @@ export const InventoryProductApi = new BaseApiService< unknown, unknown >('/inventory/product-stocks'); + +export class StockLogService extends BaseApiService< + StockLog, + unknown, + unknown +> { + constructor(basePath: string = '/inventory/stock-logs') { + super(basePath); + } + + async exportToExcel(warehouseName: string, initialQueryString: string) { + const params = new URLSearchParams(initialQueryString); + + params.set('export', 'excel'); + params.set('page', '1'); + params.set('limit', '99999999999'); + + const queryString = `?${params.toString()}`; + + const res = await httpClient(`${this.basePath}${queryString}`, { + method: 'GET', + responseType: 'blob', + }); + + const url = window.URL.createObjectURL(new Blob([res])); + const link = document.createElement('a'); + link.href = url; + + const fileName = `informasi-stok-produk-${warehouseName.toLowerCase().replaceAll(' ', '-')}-${formatDate(Date.now(), 'DD-MM-YYYY')}.xlsx`; + link.setAttribute('download', fileName); + + document.body.appendChild(link); + link.click(); + link.remove(); + } +} + +export const StockLogApi = new StockLogService('/inventory/stock-logs');