'use client'; import React, { useMemo } from 'react'; 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 { BaseSales, ClosingSalesSummary } from '@/types/api/closing'; 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'; import SalesClosingSkeleton from '@/components/pages/closing/skeleton/SalesClosingSkeleton'; interface SalesClosingTableProps { projectFlockId: number; } 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(() => { if (isResponseSuccess(sales)) { return sales.data.sales || []; } return []; }, [sales]); const summary: ClosingSalesSummary | undefined = useMemo(() => { if (isResponseSuccess(sales)) { return sales.data.summary; } return undefined; }, [sales]); const totals = useMemo(() => { if (salesData.length === 0) { return { totalQuantity: 0, totalWeight: 0, avgWeight: 0, avgSalesPrice: 0, totalSalesPrice: 0, avgActualPrice: 0, totalActualPrice: 0, }; } const totalQuantity = salesData.reduce( (sum, item) => sum + (item.qty || 0), 0 ); const totalWeight = salesData.reduce( (sum, item) => sum + (item.weight || 0), 0 ); const avgWeight = totalQuantity > 0 ? totalWeight / totalQuantity : 0; const totalSalesPrice = salesData.reduce( (sum, item) => sum + (item.total_sales_price || 0), 0 ); const validSalesPriceItems = salesData.filter( (item) => item.sales_price != null && item.sales_price > 0 ); const avgSalesPrice = validSalesPriceItems.length > 0 ? validSalesPriceItems.reduce( (sum, item) => sum + item.sales_price, 0 ) / validSalesPriceItems.length : 0; const totalActualPrice = salesData.reduce( (sum, item) => sum + (item.total_actual_price || 0), 0 ); const validActualPriceItems = salesData.filter( (item) => item.actual_price != null && item.actual_price > 0 ); const avgActualPrice = validActualPriceItems.length > 0 ? validActualPriceItems.reduce( (sum, item) => sum + item.actual_price, 0 ) / validActualPriceItems.length : 0; return { totalQuantity, totalWeight, avgWeight, avgSalesPrice, totalSalesPrice, avgActualPrice, totalActualPrice, }; }, [salesData]); const salesColumns: ColumnDef[] = useMemo( () => [ { id: 'realization_date', accessorKey: 'realization_date', header: 'Tanggal Realisasi', cell: (props) => { const date = props.row.original.realization_date; return date ? formatDate(date, 'DD MMM YYYY') : '-'; }, footer: () => (
Total Penjualan
), }, { id: 'age', accessorKey: 'age', header: 'Umur', cell: (props) => { const age = props.row.original.age; const week = props.row.original.week; return age && week ? `${age} hari (${week} minggu)` : '-'; }, }, { id: 'do_number', accessorKey: 'do_number', header: 'No. DO', cell: (props) => props.getValue() || '-', }, { id: 'product', accessorKey: 'product', header: 'Produk', cell: (props) => { const product = props.getValue() as Product; return product?.name || '-'; }, }, { id: 'customer', accessorKey: 'customer', header: 'Customer', cell: (props) => { const customer = props.getValue() as Customer; return customer?.name || '-'; }, }, { id: 'jumlah', header: 'Jumlah', columns: [ { id: 'qty', accessorKey: 'qty', header: 'Kuantitas', cell: (props) => { const value = props.getValue() as number; return
{formatNumber(value)}
; }, footer: () => (
{formatNumber(totals.totalQuantity)}
), }, { id: 'weight', accessorKey: 'weight', header: 'Kg', cell: (props) => { const value = props.getValue() as number; return
{formatNumber(value)}
; }, footer: () => (
{formatNumber(totals.totalWeight)}
), }, ], }, { id: 'avg_weight', accessorKey: 'avg_weight', header: 'AVG (Kg)', cell: (props) => { const value = props.getValue() as number; return
{formatNumber(value)}
; }, footer: () => (
{formatNumber(totals.avgWeight)}
), }, { id: 'sales_price', accessorKey: 'sales_price', header: 'Harga Sales (Rp)', cell: (props) => { const value = props.getValue() as number; return
{formatCurrency(value)}
; }, footer: () => (
{summary ? formatCurrency(summary.avg_sales_price) : formatCurrency(totals.avgSalesPrice)}
), }, { id: 'total_sales_price', accessorKey: 'total_sales_price', header: 'Total Sales (Rp)', cell: (props) => { const value = props.getValue() as number; return
{formatCurrency(value)}
; }, footer: () => (
{summary ? formatCurrency(summary.total_sales_price) : formatCurrency(totals.totalSalesPrice)}
), }, { id: 'actual_price', accessorKey: 'actual_price', header: 'Harga Act (Rp)', cell: (props) => { const value = props.getValue() as number; return
{formatCurrency(value)}
; }, footer: () => (
{summary ? formatCurrency(summary.avg_actual_price) : formatCurrency(totals.avgActualPrice)}
), }, { id: 'total_actual_price', accessorKey: 'total_actual_price', header: 'Total Act (Rp)', cell: (props) => { const value = props.getValue() as number; return
{formatCurrency(value)}
; }, footer: () => (
{summary ? formatCurrency(summary.total_actual_price) : formatCurrency(totals.totalActualPrice)}
), }, { id: 'kandang', accessorKey: 'kandang', header: 'Kandang', cell: (props) => { const kandang = props.getValue() as Kandang; return kandang?.name || '-'; }, }, // { // id: 'payment_status', // accessorKey: 'payment_status', // header: 'Status Pembayaran', // cell: (props) => { // const status = props.getValue() as string; // const getStatusColor = (status: string) => { // if (!status) return 'neutral'; // switch (status.toLowerCase()) { // case 'paid': // return 'success'; // case 'tempo': // return 'warning'; // default: // return 'neutral'; // } // }; // return ( // // {status || '-'} // // ); // }, // }, ], [ summary, totals.avgActualPrice, totals.avgSalesPrice, totals.avgWeight, totals.totalActualPrice, totals.totalQuantity, totals.totalSalesPrice, totals.totalWeight, ] ); return (
{isLoading ? ( ) : salesData.length === 0 ? ( ) : ( 0} 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', }} /> )} ); }; export default SalesClosingTable;