From 6e7a4044b6bbfde8fc704ad2ec0143022f545f04 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sun, 23 Aug 2026 20:12:20 +0700 Subject: [PATCH] feat: add total products sold to revenue summary and update dashboard display for marketing roles --- app/Services/DashboardService.php | 5 ++ resources/js/pages/admin/analysis/index.tsx | 9 ++- .../manage/transaction/transaction-card.tsx | 27 ++++--- resources/js/pages/dashboard.tsx | 75 +++++++++++-------- 4 files changed, 69 insertions(+), 47 deletions(-) diff --git a/app/Services/DashboardService.php b/app/Services/DashboardService.php index 2e74ff2..4cc3fd4 100644 --- a/app/Services/DashboardService.php +++ b/app/Services/DashboardService.php @@ -13,6 +13,7 @@ use App\Models\Expense; use App\Models\LeaveRequest; use App\Models\Order; +use App\Models\OrderItem; use App\Models\Payroll; use App\Models\User; use Carbon\Carbon; @@ -154,6 +155,9 @@ public function getRevenueSummary(?User $user = null): array $gross = $totalRevenue - $totalCogs; $net = $gross - $payrollTotal - $expenseTotal; + $orderIds = (clone $baseQuery)->pluck('id'); + $totalProductsSold = (int) OrderItem::whereIn('order_id', $orderIds)->sum('quantity'); + return [ 'total_revenue' => $totalRevenue, 'total_discount' => (int) $stats->total_discount, @@ -164,6 +168,7 @@ public function getRevenueSummary(?User $user = null): array 'payroll_total' => $payrollTotal, 'expense_total' => $expenseTotal, 'total_orders' => (int) $stats->total_orders, + 'total_products_sold' => $totalProductsSold, ]; } diff --git a/resources/js/pages/admin/analysis/index.tsx b/resources/js/pages/admin/analysis/index.tsx index d4deddb..cd55169 100644 --- a/resources/js/pages/admin/analysis/index.tsx +++ b/resources/js/pages/admin/analysis/index.tsx @@ -483,6 +483,7 @@ export default function Analysis({ monthlyRetailRevenue, }: AnalysisProps) { const { can, hasAnyRole, hasRole } = useCan(); + const isMarketing = hasAnyRole(['marketing-offline', 'marketing-online']); const [startDate, setStartDate] = useState(initialFilters.start_date ?? ''); const [endDate, setEndDate] = useState(initialFilters.end_date ?? ''); const [selectedPreset, setSelectedPreset] = useState(''); @@ -591,8 +592,8 @@ export default function Analysis({ return { statCards: 1, revenue: 2, revenueByChannel: 3, orderPie: 4, totalOrder: 5, topProducts: 6, topCustomers: 7 }; } - if (hasRole('marketing')) { - return { statCards: 1, revenue: 2, revenueByChannel: 3, orderPie: 4, totalOrder: 5, revenueTrend: 6, topProducts: 7, topCustomers: 8 }; + if (isMarketing) { + return { statCards: 1, revenueByChannel: 3, orderPie: 4, totalOrder: 5, revenueTrend: 6, topProducts: 7, topCustomers: 8 }; } return {}; @@ -753,7 +754,7 @@ export default function Analysis({ )} - {can('analysis.revenue') && ( + {can('analysis.revenue') && !isMarketing && (
@@ -911,7 +912,7 @@ export default function Analysis({
)} - {can('analysis.revenue') && ( + {can('analysis.revenue') && !isMarketing && (
diff --git a/resources/js/pages/admin/manage/transaction/transaction-card.tsx b/resources/js/pages/admin/manage/transaction/transaction-card.tsx index 99b9ccd..cba9c36 100644 --- a/resources/js/pages/admin/manage/transaction/transaction-card.tsx +++ b/resources/js/pages/admin/manage/transaction/transaction-card.tsx @@ -47,7 +47,8 @@ export function TransactionCardRow({ onUpdateStatus, onPrint, }: TransactionCardRowParams) { - const { can } = useCan(); + const { can, hasAnyRole } = useCan(); + const isMarketing = hasAnyRole(['marketing-offline', 'marketing-online']); const [refundDialogOpen, setRefundDialogOpen] = useState(false); const [cancelDialogOpen, setCancelDialogOpen] = useState(false); const variantCount = transaction.items_count ?? 0; @@ -188,18 +189,22 @@ export function TransactionCardRow({ {formatCurrency(transaction.total_amount)} - = 0 ? 'text-green-600' : 'text-red-600'}> - - Laba:{' '} + {!isMarketing && ( + = 0 ? 'text-green-600' : 'text-red-600'}> + + Laba:{' '} + + {formatCurrency(transaction.profit)} - {formatCurrency(transaction.profit)} - - - - HPP:{' '} + )} + {!isMarketing && ( + + + HPP:{' '} + + {formatCurrency(transaction.cogs)} - {formatCurrency(transaction.cogs)} - + )}
{transaction.photo_url && ( diff --git a/resources/js/pages/dashboard.tsx b/resources/js/pages/dashboard.tsx index 1c01871..78da460 100644 --- a/resources/js/pages/dashboard.tsx +++ b/resources/js/pages/dashboard.tsx @@ -53,6 +53,7 @@ type DashboardProps = { payroll_total: number; expense_total: number; total_orders: number; + total_products_sold: number; }; expenseSummary: { total: number; @@ -97,7 +98,8 @@ export default function Dashboard({ isOnLeave, canCheckIn, }: DashboardProps) { - const { can, hasRole } = useCan(); + const { can, hasRole, hasAnyRole } = useCan(); + const isMarketing = hasAnyRole(['marketing-offline', 'marketing-online']); const { auth } = usePage().props as { auth: { user?: { username?: string } } }; const [currentTime, setCurrentTime] = useState(new Date()); const [showCamera, setShowCamera] = useState(false); @@ -336,37 +338,46 @@ export default function Dashboard({ mainLabel="Total" mainValue={`Rp${formatRupiah(revenueSummary.total_revenue)}`} subLabel={`${revenueSummary.total_orders} transaksi selesai`} - items={[ - { - label: 'Kotor', - value: `Rp${formatRupiah(revenueSummary.gross)}`, - }, - { - label: 'Bersih', - value: `Rp${formatRupiah(revenueSummary.net)}`, - }, - { - label: 'Gaji', - value: `Rp${formatRupiah(revenueSummary.payroll_total)}`, - }, - { - label: 'Pengeluaran', - value: `Rp${formatRupiah(revenueSummary.expense_total)}`, - }, - { - label: 'Potongan', - value: `Rp${formatRupiah(revenueSummary.total_deduction)}`, - }, - { - label: 'Diskon', - value: `Rp${formatRupiah(revenueSummary.total_discount)}`, - }, - { - label: 'HPP', - value: `Rp${formatRupiah(revenueSummary.total_cogs)}`, - }, - ]} - cols={visibleStatsCount === 1 ? 4 : visibleStatsCount === 2 ? 3 : 2} + items={ + isMarketing + ? [ + { + label: 'Produk Terjual', + value: revenueSummary.total_products_sold.toLocaleString('id-ID'), + }, + ] + : [ + { + label: 'Kotor', + value: `Rp${formatRupiah(revenueSummary.gross)}`, + }, + { + label: 'Bersih', + value: `Rp${formatRupiah(revenueSummary.net)}`, + }, + { + label: 'Gaji', + value: `Rp${formatRupiah(revenueSummary.payroll_total)}`, + }, + { + label: 'Pengeluaran', + value: `Rp${formatRupiah(revenueSummary.expense_total)}`, + }, + { + label: 'Potongan', + value: `Rp${formatRupiah(revenueSummary.total_deduction)}`, + }, + { + label: 'Diskon', + value: `Rp${formatRupiah(revenueSummary.total_discount)}`, + }, + { + label: 'HPP', + value: `Rp${formatRupiah(revenueSummary.total_cogs)}`, + }, + ] + } + cols={isMarketing ? 2 : visibleStatsCount === 1 ? 4 : visibleStatsCount === 2 ? 3 : 2} /> )}