'use client'; import Tabs from '@/components/Tabs'; import React, { useState, useMemo } from 'react'; import { ColumnDef } from '@tanstack/react-table'; import Table, { CustomHeaderRow } from '@/components/Table'; import Card from '@/components/Card'; import Badge from '@/components/Badge'; import { formatCurrency, formatNumber, formatDate } from '@/lib/helper'; import { BaseClosingSales, BaseSales } from '@/types/api/closing/closing'; interface SalesReportTableProps { type?: 'detail'; initialValues?: BaseClosingSales; } interface HeaderCell { id: string; content: React.ReactNode; colSpan?: number; rowSpan?: number; className: string; field?: string; } const generateCustomHeaders = (template: { groups: Array<{ label: string; field?: string; rowSpan?: number; colSpan?: number; subLabels?: string[]; }>; }): CustomHeaderRow[] => { const mainRow: Array<{ id: string; content: React.ReactNode; colSpan?: number; rowSpan?: number; className: string; }> = []; const subRow: Array<{ id: string; content: React.ReactNode; colSpan?: number; rowSpan?: number; className: string; }> = []; let subColumnIndex = 0; template.groups.forEach((group) => { if (group.subLabels) { const mainCell: HeaderCell = { id: `${group.field || 'group'}-${subColumnIndex}`, content: group.label, colSpan: group.colSpan, className: 'px-4 py-3 text-xs font-semibold text-gray-700 text-center whitespace-nowrap border border-gray-200', }; mainRow.push(mainCell); group.subLabels.forEach((subLabel) => { const subCell: HeaderCell = { id: `sub-${subColumnIndex}`, content: subLabel, className: 'px-4 py-3 text-xs font-semibold text-gray-700 text-left whitespace-nowrap border border-gray-200', }; if (group.label === 'Jumlah') { subCell.field = subLabel === 'Kuantitas' ? 'quantity' : 'weight'; } subRow.push(subCell); subColumnIndex++; }); } else { const mainCell: HeaderCell = { id: `${group.field}-header`, content: group.label, rowSpan: group.rowSpan, className: 'px-4 py-3 text-xs font-semibold text-gray-700 text-left whitespace-nowrap border border-gray-200', }; mainCell.field = group.field; mainRow.push(mainCell); } }); const rows: CustomHeaderRow[] = [ { id: 'main-header', cells: mainRow, className: 'bg-gray-50', }, ]; if (subRow.length > 0) { rows.push({ id: 'sub-header', cells: subRow, className: 'bg-gray-50', }); } return rows; }; // TODO: TEMPORARY - Remove this when backend API returns English field names const mapIndonesianDataToEnglish = (data: BaseClosingSales): BaseSales[] => { if (!data || !data.penjualan || !Array.isArray(data.penjualan)) { return []; } // eslint-disable-next-line @typescript-eslint/no-explicit-any return data.penjualan.map((item: any) => ({ id: item.id, realization_date: item.tanggal_realisasi, week_age: item.umur_minggu, age_label: item.umur_label, delivery_order_number: item.no_do, product: item.produk, product_category: item.jenis_produk, customer: item.customer, quantity: item.qty, weight: item.kg, average: item.avg, price: item.harga, total: item.total, kandang: item.kandang, kandang_id: item.kandang_id, payment_status: item.status_pembayaran, })); }; // END TODO const SalesReportTable = ({ type = 'detail', initialValues, }: SalesReportTableProps) => { const [activeTabId, setActiveTabId] = useState('penjualan'); const salesBroilerData: BaseSales[] = useMemo(() => { if (activeTabId === 'penjualan' && initialValues) { // TODO: TEMPORARY - Remove this when backend API returns English field names if (initialValues.penjualan && Array.isArray(initialValues.penjualan)) { return mapIndonesianDataToEnglish(initialValues); } // END TODO return []; } return []; }, [initialValues, activeTabId]); const totals = useMemo(() => { if (salesBroilerData.length === 0) { return { totalQuantity: 0, totalWeight: 0, avgWeight: 0, avgPricePartner: 0, totalPartner: 0, avgPriceAct: 0, totalAct: 0, }; } const totalQuantity = salesBroilerData.reduce( (sum, item) => sum + (item.quantity || 0), 0 ); const totalWeight = salesBroilerData.reduce( (sum, item) => sum + (item.weight || 0), 0 ); const avgWeight = totalQuantity > 0 ? totalWeight / totalQuantity : 0; const validPriceItems = salesBroilerData.filter( (item) => item.price != null && item.price > 0 ); const avgPricePartner = validPriceItems.length > 0 ? validPriceItems.reduce((sum, item) => sum + item.price, 0) / validPriceItems.length : 0; const totalPartner = salesBroilerData.reduce( (sum, item) => sum + (item.total || 0), 0 ); const avgPriceAct = avgPricePartner; const totalAct = totalPartner; return { totalQuantity, totalWeight, avgWeight, avgPricePartner, totalPartner, avgPriceAct, totalAct, }; }, [salesBroilerData]); 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') : '-'; }, }, { id: 'age_label', accessorKey: 'age_label', header: 'Umur', cell: (props) => props.getValue() || '-', }, { id: 'delivery_order_number', accessorKey: 'delivery_order_number', header: 'No. DO', cell: (props) => props.getValue() || '-', }, { id: 'product', accessorKey: 'product', header: 'Produk', cell: (props) => props.getValue() || '-', }, { id: 'customer', accessorKey: 'customer', header: 'Customer', cell: (props) => props.getValue() || '-', }, { id: 'quantity', accessorKey: 'quantity', header: 'Kuantitas', cell: (props) => { const value = props.getValue() as number; const isSummary = props.row.id === 'summary'; return (
{formatNumber(value)}
); }, }, { id: 'weight', accessorKey: 'weight', header: 'Kg', cell: (props) => { const value = props.getValue() as number; const isSummary = props.row.id === 'summary'; return (
{formatNumber(value)}
); }, }, { id: 'average', accessorKey: 'average', header: 'AVG (Kg)', cell: (props) => { const value = props.getValue() as number; const isSummary = props.row.id === 'summary'; return (
{formatNumber(value)}
); }, }, { id: 'price_partner', accessorKey: 'price', header: 'Harga Mitra (Rp)', cell: (props) => { const value = props.getValue() as number; const isSummary = props.row.id === 'summary'; return (
{formatCurrency(value)}
); }, }, { id: 'total_mitra', accessorKey: 'total', header: 'Total Mitra (Rp)', cell: (props) => { const value = props.getValue() as number; const isSummary = props.row.id === 'summary'; return (
{formatCurrency(value)}
); }, }, { id: 'price_act', accessorKey: 'price', header: 'Harga Act (Rp)', cell: (props) => { const value = props.getValue() as number; const isSummary = props.row.id === 'summary'; return (
{formatCurrency(value)}
); }, }, { id: 'total_act', accessorKey: 'total', header: 'Total Act (Rp)', cell: (props) => { const value = props.getValue() as number; const isSummary = props.row.id === 'summary'; return (
{formatCurrency(value)}
); }, }, { id: 'kandang', accessorKey: 'kandang', header: 'Kandang', cell: (props) => props.getValue() || '-', }, { 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 'lunas': return 'success'; case 'pending': return 'warning'; case 'belum lunas': return 'error'; default: return 'neutral'; } }; return ( {status || '-'} ); }, }, ], [] ); const headerTemplate = { groups: [ { label: 'Tanggal Realisasi', field: 'realization_date', rowSpan: 2 }, { label: 'Umur', field: 'age_label', rowSpan: 2 }, { label: 'No. DO', field: 'delivery_order_number', rowSpan: 2 }, { label: 'Produk', field: 'product', rowSpan: 2 }, { label: 'Customer', field: 'customer', rowSpan: 2 }, { label: 'Jumlah', colSpan: 2, subLabels: ['Kuantitas', 'Kg'], }, { label: 'AVG (Kg)', field: 'average', rowSpan: 2 }, { label: 'Harga Mitra (Rp)', field: 'price_partner', rowSpan: 2 }, { label: 'Total Mitra (Rp)', field: 'total_mitra', rowSpan: 2 }, { label: 'Harga Act (Rp)', field: 'price_act', rowSpan: 2 }, { label: 'Total Act (Rp)', field: 'total_act', rowSpan: 2 }, { label: 'Kandang', field: 'kandang', rowSpan: 2 }, { label: 'Status Pembayaran', field: 'payment_status', rowSpan: 2 }, ], }; const salesCustomHeaderRows = useMemo( () => generateCustomHeaders(headerTemplate), [] ); return ( <>

Penjualan

0} footerContent={ } className={{ tableWrapperClassName: 'overflow-x-auto', tableClassName: 'w-full table-auto text-sm', headerRowClassName: 'hidden', 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', }} /> ), }, ]} variant='lifted' />
            {JSON.stringify(
              {
                code: 200,
                status: 'success',
                message: 'Retrieved sales report successfully',
                data: {
                  project_type: 'GROWING',
                  flock_id: '1',
                  period: 10,
                  sales: [
                    {
                      id: 1,
                      realization_date: '2025-12-05T02:22:17.443165Z',
                      age: 20,
                      do_number: 'SO-DO-10001',
                      product: {
                        id: 1,
                        name: 'Laptop Gaming X500',
                        product_price: 15000000,
                        selling_price: 16500000.5,
                        uom: {
                          id: 1,
                          name: 'KG',
                        },
                        flags: ['Best Seller', 'New Arrival'],
                        product_category: {
                          id: 5,
                          name: 'Elektronik',
                          code: 'DOC',
                        },
                      },
                      customer: {
                        id: 12345,
                        name: 'PT. Solusi Teknologi Nusantara',
                        type: 'Perusahaan',
                        account_number: 'ACC1234567890',
                        balance: 5000000.75,
                        pic: {
                          id: 101,
                          name: 'Budi Santoso',
                          email: 'budi.santoso@example.com',
                          role: 'Manajer Akun',
                        },
                      },
                      qty: 6348,
                      weight: 19142,
                      avg_weight: 3.02,
                      price: 26419,
                      total_price: 505712498,
                      kandang: {
                        id: 1,
                        name: 'cibeber 1',
                      },
                      payment_status: 'Paid',
                    },
                  ],
                },
              },
              null,
              2
            )}
          
); }; export default SalesReportTable;
Total Penjualan {formatNumber(totals.totalQuantity)} {formatNumber(totals.totalWeight)} {formatNumber(totals.avgWeight)} {formatCurrency(totals.avgPricePartner)} {formatCurrency(totals.totalPartner)} {formatCurrency(totals.avgPriceAct)} {formatCurrency(totals.totalAct)}