update report add standard hpp, realisasi hpp, dan status
This commit is contained in:
@@ -15,6 +15,23 @@ import ReportGeneratorComponent from '@/views/dashboards/daily-report/report-gen
|
||||
import ReportHeader from '@/views/dashboards/daily-report/report-header'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
// Dummy HPP values — ganti dengan data real nantinya
|
||||
const DUMMY_STD_HPP = 30 // 30%
|
||||
const DUMMY_REAL_HPP = 28 // 28%
|
||||
|
||||
const getHppStatus = (stdHpp: number, realHpp: number): 'Sehat' | 'Tidak Sehat' =>
|
||||
realHpp <= stdHpp ? 'Sehat' : 'Tidak Sehat'
|
||||
|
||||
const StatusBadge = ({ status }: { status: 'Sehat' | 'Tidak Sehat' }) => (
|
||||
<span
|
||||
className={`inline-block px-3 py-1 rounded-full text-base font-bold ${
|
||||
status === 'Sehat' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
||||
}`}
|
||||
>
|
||||
{status}
|
||||
</span>
|
||||
)
|
||||
|
||||
const DailyPOSReport = () => {
|
||||
const reportRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
@@ -108,11 +125,7 @@ const DailyPOSReport = () => {
|
||||
}
|
||||
|
||||
const getReportTitle = () => {
|
||||
if (filterType === 'single') {
|
||||
return 'Laporan Transaksi'
|
||||
} else {
|
||||
return `Laporan Transaksi`
|
||||
}
|
||||
return 'Laporan Transaksi'
|
||||
}
|
||||
|
||||
const handleGeneratePDF = async () => {
|
||||
@@ -342,7 +355,7 @@ const DailyPOSReport = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Category Summary */}
|
||||
{/* Category Summary — +3 kolom baru */}
|
||||
<div className='px-8 pb-8'>
|
||||
<h3 className='text-3xl font-bold mb-8' style={{ color: '#36175e' }}>
|
||||
Ringkasan Kategori
|
||||
@@ -355,18 +368,31 @@ const DailyPOSReport = () => {
|
||||
<th className='text-left text-xl p-4 font-semibold'>Nama</th>
|
||||
<th className='text-center text-xl p-4 font-semibold'>Qty</th>
|
||||
<th className='text-right text-xl p-4 font-semibold'>Pendapatan</th>
|
||||
<th className='text-center text-xl p-4 font-semibold'>% Std HPP</th>
|
||||
<th className='text-center text-xl p-4 font-semibold'>% Real HPP</th>
|
||||
<th className='text-center text-xl p-4 font-semibold'>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{category?.data?.map((c, index) => (
|
||||
<tr key={index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
|
||||
<td className='p-4 text-xl font-medium text-gray-800'>{c.category_name}</td>
|
||||
<td className='p-4 text-xl text-center text-gray-700'>{c.total_quantity}</td>
|
||||
<td className='p-4 text-xl text-right font-semibold' style={{ color: '#36175e' }}>
|
||||
{formatCurrency(c.total_revenue)}
|
||||
</td>
|
||||
</tr>
|
||||
)) || []}
|
||||
{category?.data?.map((c, index) => {
|
||||
const stdHpp = DUMMY_STD_HPP
|
||||
const realHpp = DUMMY_REAL_HPP
|
||||
const status = getHppStatus(stdHpp, realHpp)
|
||||
return (
|
||||
<tr key={index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
|
||||
<td className='p-4 text-xl font-medium text-gray-800'>{c.category_name}</td>
|
||||
<td className='p-4 text-xl text-center text-gray-700'>{c.total_quantity}</td>
|
||||
<td className='p-4 text-xl text-right font-semibold' style={{ color: '#36175e' }}>
|
||||
{formatCurrency(c.total_revenue)}
|
||||
</td>
|
||||
<td className='p-4 text-xl text-center text-gray-700'>{stdHpp}%</td>
|
||||
<td className='p-4 text-xl text-center text-gray-700'>{realHpp}%</td>
|
||||
<td className='p-4 text-center'>
|
||||
<StatusBadge status={status} />
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}) || []}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr className='text-gray-800 border-t-2 border-gray-300'>
|
||||
@@ -375,13 +401,16 @@ const DailyPOSReport = () => {
|
||||
<td className='p-4 text-xl text-right font-bold'>
|
||||
{formatCurrency(categorySummary?.totalRevenue ?? 0)}
|
||||
</td>
|
||||
<td className='p-4'></td>
|
||||
<td className='p-4'></td>
|
||||
<td className='p-4'></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Product Summary - Dipisah per kategori dengan tabel terpisah */}
|
||||
{/* Product Summary — +3 kolom baru */}
|
||||
<div className='px-8 pb-8'>
|
||||
<h3 className='text-3xl font-bold mb-8' style={{ color: '#36175e' }}>
|
||||
Ringkasan Item Per Kategori
|
||||
@@ -398,7 +427,6 @@ const DailyPOSReport = () => {
|
||||
})
|
||||
.map((categoryName, catIndex) => {
|
||||
const categoryProducts = groupedProducts[categoryName].sort((a, b) => {
|
||||
// Sort by product_sku ASC
|
||||
const skuA = a.product_sku || ''
|
||||
const skuB = b.product_sku || ''
|
||||
return skuA.localeCompare(skuB)
|
||||
@@ -421,31 +449,57 @@ const DailyPOSReport = () => {
|
||||
style={{ borderCollapse: 'collapse', tableLayout: 'fixed', width: '100%' }}
|
||||
>
|
||||
<colgroup>
|
||||
<col style={{ width: '50%' }} />
|
||||
<col style={{ width: '20%' }} />
|
||||
<col style={{ width: '30%' }} />
|
||||
<col style={{ width: '28%' }} />
|
||||
<col style={{ width: '10%' }} />
|
||||
<col style={{ width: '22%' }} />
|
||||
<col style={{ width: '13%' }} />
|
||||
<col style={{ width: '13%' }} />
|
||||
<col style={{ width: '14%' }} />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr className='text-gray-800 border-b-2 border-gray-300 bg-gray-100'>
|
||||
<th className='text-left text-xl p-5 font-semibold border-r border-gray-300'>Produk</th>
|
||||
<th className='text-center text-xl p-5 font-semibold border-r border-gray-300'>Qty</th>
|
||||
<th className='text-right text-xl p-5 font-semibold'>Pendapatan</th>
|
||||
<th className='text-right text-xl p-5 font-semibold border-r border-gray-300'>
|
||||
Pendapatan
|
||||
</th>
|
||||
<th className='text-center text-xl p-5 font-semibold border-r border-gray-300'>
|
||||
% Std HPP
|
||||
</th>
|
||||
<th className='text-center text-xl p-5 font-semibold border-r border-gray-300'>
|
||||
% Real HPP
|
||||
</th>
|
||||
<th className='text-center text-xl p-5 font-semibold'>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{categoryProducts.map((item, index) => (
|
||||
<tr key={index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
|
||||
<td className='p-5 text-xl font-medium text-gray-800 border-r border-gray-200'>
|
||||
{item.product_name}
|
||||
</td>
|
||||
<td className='p-5 text-xl text-center text-gray-700 border-r border-gray-200'>
|
||||
{item.quantity_sold}
|
||||
</td>
|
||||
<td className='p-5 text-xl text-right font-semibold text-gray-800'>
|
||||
{formatCurrency(item.revenue)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{categoryProducts.map((item, index) => {
|
||||
const stdHpp = DUMMY_STD_HPP
|
||||
const realHpp = DUMMY_REAL_HPP
|
||||
const status = getHppStatus(stdHpp, realHpp)
|
||||
return (
|
||||
<tr key={index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
|
||||
<td className='p-5 text-xl font-medium text-gray-800 border-r border-gray-200'>
|
||||
{item.product_name}
|
||||
</td>
|
||||
<td className='p-5 text-xl text-center text-gray-700 border-r border-gray-200'>
|
||||
{item.quantity_sold}
|
||||
</td>
|
||||
<td className='p-5 text-xl text-right font-semibold text-gray-800 border-r border-gray-200'>
|
||||
{formatCurrency(item.revenue)}
|
||||
</td>
|
||||
<td className='p-5 text-xl text-center text-gray-700 border-r border-gray-200'>
|
||||
{stdHpp}%
|
||||
</td>
|
||||
<td className='p-5 text-xl text-center text-gray-700 border-r border-gray-200'>
|
||||
{realHpp}%
|
||||
</td>
|
||||
<td className='p-5 text-center'>
|
||||
<StatusBadge status={status} />
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr className='bg-gray-200 border-t-2 border-gray-400'>
|
||||
@@ -455,9 +509,12 @@ const DailyPOSReport = () => {
|
||||
<td className='p-5 text-xl text-center font-bold text-gray-800 border-r border-gray-400'>
|
||||
{categoryTotalQty}
|
||||
</td>
|
||||
<td className='p-5 text-xl text-right font-bold text-gray-800'>
|
||||
<td className='p-5 text-xl text-right font-bold text-gray-800 border-r border-gray-400'>
|
||||
{formatCurrency(categoryTotalRevenue)}
|
||||
</td>
|
||||
<td className='p-5 border-r border-gray-400'></td>
|
||||
<td className='p-5 border-r border-gray-400'></td>
|
||||
<td className='p-5'></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
@@ -468,24 +525,35 @@ const DailyPOSReport = () => {
|
||||
|
||||
{/* Grand Total */}
|
||||
<div className='bg-purple-50 rounded-lg border-2 border-purple-300 mt-6'>
|
||||
<table className='w-full' style={{ borderCollapse: 'collapse' }}>
|
||||
<table className='w-full' style={{ borderCollapse: 'collapse', tableLayout: 'fixed' }}>
|
||||
<colgroup>
|
||||
<col style={{ width: '28%' }} />
|
||||
<col style={{ width: '10%' }} />
|
||||
<col style={{ width: '22%' }} />
|
||||
<col style={{ width: '13%' }} />
|
||||
<col style={{ width: '13%' }} />
|
||||
<col style={{ width: '14%' }} />
|
||||
</colgroup>
|
||||
<tfoot>
|
||||
<tr className='text-gray-800'>
|
||||
<td
|
||||
className='p-5 text-2xl font-bold border-r-2 border-purple-300'
|
||||
style={{ width: '50%', color: '#36175e' }}
|
||||
>
|
||||
<td className='p-5 text-2xl font-bold border-r-2 border-purple-300' style={{ color: '#36175e' }}>
|
||||
TOTAL KESELURUHAN
|
||||
</td>
|
||||
<td
|
||||
className='p-5 text-2xl text-center font-bold border-r-2 border-purple-300'
|
||||
style={{ width: '20%', color: '#36175e' }}
|
||||
style={{ color: '#36175e' }}
|
||||
>
|
||||
{productSummary.totalQuantitySold ?? 0}
|
||||
</td>
|
||||
<td className='p-5 text-2xl text-right font-bold' style={{ width: '30%', color: '#36175e' }}>
|
||||
<td
|
||||
className='p-5 text-2xl text-right font-bold border-r-2 border-purple-300'
|
||||
style={{ color: '#36175e' }}
|
||||
>
|
||||
{formatCurrency(productSummary.totalRevenue ?? 0)}
|
||||
</td>
|
||||
<td className='p-5 border-r-2 border-purple-300'></td>
|
||||
<td className='p-5 border-r-2 border-purple-300'></td>
|
||||
<td className='p-5'></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user