2025-09-11 18:20:40 +07:00
|
|
|
'use client'
|
|
|
|
|
|
|
|
|
|
import DateRangePicker from '@/components/RangeDatePicker'
|
|
|
|
|
import { ReportItem, ReportItemFooter, ReportItemHeader, ReportItemSubheader } from '@/components/report/ReportItem'
|
2025-09-25 15:45:49 +07:00
|
|
|
import { ExcelExportProfitLossService } from '@/services/export/excel/ExcelExportProfitLossService'
|
2025-09-25 19:35:22 +07:00
|
|
|
import { PDFExportProfitLossService } from '@/services/export/pdf/PDFExportProfitLossService'
|
2025-09-25 14:59:08 +07:00
|
|
|
import { ProfitLossReport } from '@/types/services/analytic'
|
2025-09-25 19:35:22 +07:00
|
|
|
import { Button, Card, CardContent, Box, Menu, MenuItem, ListItemIcon, ListItemText } from '@mui/material'
|
|
|
|
|
|
|
|
|
|
import { useState } from 'react'
|
2025-09-11 18:20:40 +07:00
|
|
|
|
2025-09-25 14:59:08 +07:00
|
|
|
interface ReportProfitLossContentProps {
|
|
|
|
|
profitData: ProfitLossReport | undefined
|
|
|
|
|
startDate: Date | null
|
|
|
|
|
endDate: Date | null
|
|
|
|
|
onStartDateChange: (date: Date | null) => void
|
|
|
|
|
onEndDateChange: (date: Date | null) => void
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Utility function to format date for display
|
|
|
|
|
const formatDisplayDate = (dateString: string) => {
|
|
|
|
|
const date = new Date(dateString)
|
|
|
|
|
return date.toLocaleDateString('id-ID', {
|
|
|
|
|
day: '2-digit',
|
|
|
|
|
month: '2-digit',
|
|
|
|
|
year: 'numeric'
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ReportProfitLossContent = ({
|
|
|
|
|
profitData,
|
|
|
|
|
startDate,
|
|
|
|
|
endDate,
|
|
|
|
|
onStartDateChange,
|
|
|
|
|
onEndDateChange
|
|
|
|
|
}: ReportProfitLossContentProps) => {
|
2025-09-25 19:35:22 +07:00
|
|
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
|
|
|
|
|
const open = Boolean(anchorEl)
|
|
|
|
|
|
|
|
|
|
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
|
|
|
setAnchorEl(event.currentTarget)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleClose = () => {
|
|
|
|
|
setAnchorEl(null)
|
|
|
|
|
}
|
|
|
|
|
const handleExportExcel = async () => {
|
2025-09-25 15:45:49 +07:00
|
|
|
if (!profitData) return
|
2025-09-25 19:35:22 +07:00
|
|
|
handleClose()
|
2025-09-25 15:45:49 +07:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const result = await ExcelExportProfitLossService.exportProfitLossToExcel(profitData)
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
2025-09-25 19:35:22 +07:00
|
|
|
console.log('Excel export successful:', result.filename)
|
|
|
|
|
} else {
|
|
|
|
|
console.error('Excel export failed:', result.error)
|
|
|
|
|
alert('Export Excel gagal. Silakan coba lagi.')
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Excel export error:', error)
|
|
|
|
|
alert('Terjadi kesalahan saat export Excel.')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleExportPDF = async () => {
|
|
|
|
|
if (!profitData) return
|
|
|
|
|
handleClose()
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const result = await PDFExportProfitLossService.exportProfitLossToPDF(profitData)
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
console.log('PDF export successful:', result.filename)
|
2025-09-25 15:45:49 +07:00
|
|
|
// Optional: Show success notification
|
|
|
|
|
} else {
|
2025-09-25 19:35:22 +07:00
|
|
|
console.error('PDF export failed:', result.error)
|
|
|
|
|
alert('Export PDF gagal. Silakan coba lagi.')
|
2025-09-25 15:45:49 +07:00
|
|
|
}
|
|
|
|
|
} catch (error) {
|
2025-09-25 19:35:22 +07:00
|
|
|
console.error('PDF export error:', error)
|
|
|
|
|
alert('Terjadi kesalahan saat export PDF.')
|
2025-09-25 15:45:49 +07:00
|
|
|
}
|
2025-09-25 14:59:08 +07:00
|
|
|
}
|
2025-09-11 18:20:40 +07:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Card>
|
|
|
|
|
<div className='p-6 border-be'>
|
|
|
|
|
<div className='flex items-center justify-end gap-2'>
|
|
|
|
|
<Button
|
|
|
|
|
color='secondary'
|
|
|
|
|
variant='tonal'
|
2025-09-25 19:35:22 +07:00
|
|
|
startIcon={<i className='tabler-download' />}
|
|
|
|
|
endIcon={<i className='tabler-chevron-down' />}
|
2025-09-11 18:20:40 +07:00
|
|
|
className='max-sm:is-full'
|
2025-09-25 19:35:22 +07:00
|
|
|
onClick={handleClick}
|
2025-09-25 14:59:08 +07:00
|
|
|
disabled={!profitData}
|
2025-09-25 19:35:22 +07:00
|
|
|
aria-controls={open ? 'export-menu' : undefined}
|
|
|
|
|
aria-haspopup='true'
|
|
|
|
|
aria-expanded={open ? 'true' : undefined}
|
2025-09-11 18:20:40 +07:00
|
|
|
>
|
2025-09-25 19:35:22 +07:00
|
|
|
Export
|
2025-09-11 18:20:40 +07:00
|
|
|
</Button>
|
2025-09-25 19:35:22 +07:00
|
|
|
|
|
|
|
|
<Menu
|
|
|
|
|
id='export-menu'
|
|
|
|
|
anchorEl={anchorEl}
|
|
|
|
|
open={open}
|
|
|
|
|
onClose={handleClose}
|
|
|
|
|
MenuListProps={{
|
|
|
|
|
'aria-labelledby': 'export-button'
|
|
|
|
|
}}
|
|
|
|
|
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
|
|
|
|
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
|
|
|
|
>
|
|
|
|
|
<MenuItem onClick={handleExportExcel}>
|
|
|
|
|
<ListItemIcon>
|
|
|
|
|
<i className='tabler-file-type-xls text-green-600' />
|
|
|
|
|
</ListItemIcon>
|
|
|
|
|
<ListItemText>Export to Excel</ListItemText>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
|
|
|
|
|
<MenuItem onClick={handleExportPDF}>
|
|
|
|
|
<ListItemIcon>
|
|
|
|
|
<i className='tabler-file-type-pdf text-red-600' />
|
|
|
|
|
</ListItemIcon>
|
|
|
|
|
<ListItemText>Export to PDF</ListItemText>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
</Menu>
|
2025-09-11 18:20:40 +07:00
|
|
|
<DateRangePicker
|
|
|
|
|
startDate={startDate}
|
|
|
|
|
endDate={endDate}
|
2025-09-25 14:59:08 +07:00
|
|
|
onStartDateChange={onStartDateChange}
|
|
|
|
|
onEndDateChange={onEndDateChange}
|
2025-09-11 18:20:40 +07:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<CardContent>
|
2025-09-25 14:59:08 +07:00
|
|
|
{profitData ? (
|
|
|
|
|
<>
|
|
|
|
|
{/* Summary Section */}
|
|
|
|
|
<ReportItemHeader
|
|
|
|
|
title='Pendapatan'
|
|
|
|
|
date={`${profitData.date_from.split('T')[0]} - ${profitData.date_to.split('T')[0]}`}
|
|
|
|
|
/>
|
|
|
|
|
<ReportItemSubheader title='Penjualan' />
|
|
|
|
|
<ReportItem
|
|
|
|
|
accountCode=''
|
|
|
|
|
accountName='Revenue'
|
|
|
|
|
amount={profitData.summary.total_revenue}
|
|
|
|
|
onClick={() => {}}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<ReportItemFooter title='Total Pendapatan' amount={profitData.summary.total_revenue} />
|
|
|
|
|
<ReportItemSubheader title='' />
|
|
|
|
|
|
|
|
|
|
<ReportItemHeader
|
|
|
|
|
title='Beban Pokok Penjualan'
|
|
|
|
|
date={`${profitData.date_from.split('T')[0]} - ${profitData.date_to.split('T')[0]}`}
|
|
|
|
|
/>
|
|
|
|
|
<ReportItem
|
|
|
|
|
accountCode=''
|
|
|
|
|
accountName='Cost of Goods Sold'
|
|
|
|
|
amount={profitData.summary.total_cost}
|
|
|
|
|
onClick={() => {}}
|
|
|
|
|
/>
|
|
|
|
|
<ReportItemFooter title='Total Beban Pokok Penjualan' amount={profitData.summary.total_cost} />
|
|
|
|
|
<ReportItemSubheader title='' />
|
|
|
|
|
|
|
|
|
|
<ReportItemHeader title='Laba Kotor' amount={profitData.summary.gross_profit} />
|
|
|
|
|
<ReportItemSubheader title='' />
|
|
|
|
|
|
|
|
|
|
{/* Daily Data Breakdown Section */}
|
|
|
|
|
{profitData.data && profitData.data.length > 0 && (
|
|
|
|
|
<>
|
|
|
|
|
<ReportItemHeader
|
|
|
|
|
title='Rincian Data Harian'
|
|
|
|
|
date={`${profitData.date_from.split('T')[0]} - ${profitData.date_to.split('T')[0]}`}
|
|
|
|
|
/>
|
|
|
|
|
<ReportItemSubheader title='Breakdown per Hari' />
|
|
|
|
|
|
|
|
|
|
{profitData.data.map((dailyData, index) => (
|
|
|
|
|
<div key={index} className='mb-4'>
|
|
|
|
|
<ReportItemSubheader title={`Data ${formatDisplayDate(dailyData.date)}`} />
|
|
|
|
|
|
|
|
|
|
<ReportItem
|
|
|
|
|
accountCode=''
|
|
|
|
|
accountName='Revenue Harian'
|
|
|
|
|
amount={dailyData.revenue}
|
|
|
|
|
onClick={() => {}}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<ReportItem accountCode='' accountName='Cost Harian' amount={dailyData.cost} onClick={() => {}} />
|
|
|
|
|
|
|
|
|
|
<ReportItem
|
|
|
|
|
accountCode=''
|
|
|
|
|
accountName='Gross Profit Harian'
|
|
|
|
|
amount={dailyData.gross_profit}
|
|
|
|
|
onClick={() => {}}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<ReportItem accountCode='' accountName='Tax Harian' amount={dailyData.tax} onClick={() => {}} />
|
|
|
|
|
|
|
|
|
|
<ReportItem
|
|
|
|
|
accountCode=''
|
|
|
|
|
accountName='Discount Harian'
|
|
|
|
|
amount={dailyData.discount}
|
|
|
|
|
onClick={() => {}}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<ReportItem
|
|
|
|
|
accountCode=''
|
|
|
|
|
accountName='Orders Harian'
|
|
|
|
|
amount={dailyData.orders}
|
|
|
|
|
onClick={() => {}}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<ReportItemFooter
|
|
|
|
|
title={`Net Profit ${formatDisplayDate(dailyData.date)}`}
|
|
|
|
|
amount={dailyData.net_profit}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
|
|
<ReportItemSubheader title='' />
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Operational Costs Section */}
|
|
|
|
|
<ReportItemHeader
|
|
|
|
|
title='Biaya Operasional'
|
|
|
|
|
date={`${profitData.date_from.split('T')[0]} - ${profitData.date_to.split('T')[0]}`}
|
|
|
|
|
/>
|
|
|
|
|
<ReportItemSubheader title='Biaya Operasional' />
|
|
|
|
|
|
|
|
|
|
<ReportItem accountCode='' accountName='Tax' amount={profitData.summary.total_tax} onClick={() => {}} />
|
|
|
|
|
<ReportItem
|
|
|
|
|
accountCode=''
|
|
|
|
|
accountName='Discount'
|
|
|
|
|
amount={profitData.summary.total_discount}
|
|
|
|
|
onClick={() => {}}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<ReportItemFooter
|
|
|
|
|
title='Total Biaya Operasional'
|
|
|
|
|
amount={profitData.summary.total_tax + profitData.summary.total_discount}
|
|
|
|
|
/>
|
|
|
|
|
<ReportItemSubheader title='' />
|
|
|
|
|
|
|
|
|
|
<ReportItemHeader title='Laba Bersih' amount={profitData.summary.net_profit} />
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<Box display='flex' justifyContent='center' alignItems='center' minHeight={200}>
|
|
|
|
|
<span>No data available</span>
|
|
|
|
|
</Box>
|
|
|
|
|
)}
|
2025-09-11 18:20:40 +07:00
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default ReportProfitLossContent
|