"use client"; import { useMemo } from "react"; type Metrics = { orderCount: number; paidOrderCount: number; openOrderCount: number; totalRevenue: number; pendingOrderValue: number; averagePaidOrderValue: number; productCount: number; lowStockSkuCount: number; inventoryValueAtCost: number; inventoryValueAtList: number; activeDiscountCount: number; }; type Product = { id: string; name: string; category: string; inStock: number; availableQty?: number; reservedQty?: number; demandScore: number; unitPrice: number; costPrice: number; profitPerUnitAtSale: number; }; type OrderRow = { orderId: string; createdAt: string; grandTotal: number; paid: boolean; voided: boolean; refunded: boolean; paymentMode: string; }; type Props = { metrics: Metrics | null; products: Product[]; orders: OrderRow[]; money: (n: number) => string; }; function pct(part: number, total: number) { if (total <= 0) return 0; return Math.round((part / total) * 100); } export function OverviewSection({ metrics, products, orders, money }: Props) { const stockHealth = useMemo(() => { let out = 0; let low = 0; let ok = 0; for (const p of products) { const avail = p.availableQty ?? p.inStock; if (avail <= 0) out += 1; else if (avail < 5) low += 1; else ok += 1; } return { out, low, ok, total: products.length }; }, [products]); const topDemand = useMemo( () => [...products].sort((a, b) => b.demandScore - a.demandScore).slice(0, 5), [products] ); const categoryRows = useMemo(() => { const map = new Map(); for (const p of products) { const cur = map.get(p.category) ?? { count: 0, stock: 0, listValue: 0 }; cur.count += 1; cur.stock += p.inStock; cur.listValue += p.inStock * p.unitPrice; map.set(p.category, cur); } return [...map.entries()] .map(([category, v]) => ({ category, ...v })) .sort((a, b) => b.listValue - a.listValue); }, [products]); const orderInsights = useMemo(() => { const paid = orders.filter((o) => o.paid && !o.voided && !o.refunded); const counter = paid.filter((o) => o.paymentMode === "COUNTER").length; const online = paid.filter((o) => o.paymentMode === "ONLINE").length; const last24h = orders.filter((o) => Date.now() - new Date(o.createdAt).getTime() < 86_400_000).length; return { paid: paid.length, counter, online, last24h, sample: orders.length }; }, [orders]); const marginAtSale = useMemo( () => products.reduce((sum, p) => sum + Math.max(0, p.inStock) * p.profitPerUnitAtSale, 0), [products] ); if (!metrics) { return

Loading analytics…

; } const revenueTotal = metrics.totalRevenue + metrics.pendingOrderValue; const paidShare = pct(metrics.totalRevenue, revenueTotal); return ( <>

Paid orders

{metrics.paidOrderCount}

All time (filtered store)

Net revenue

{money(metrics.totalRevenue)}

Completed payments

Open orders

{metrics.openOrderCount}

{money(metrics.pendingOrderValue)} pending

Avg paid ticket

{money(metrics.averagePaidOrderValue)}

Per settled order

SKU count

{metrics.productCount}

{metrics.lowStockSkuCount} low stock (< 5 per variant)

Inventory @ cost

{money(metrics.inventoryValueAtCost)}

On-hand × unit cost

Inventory @ list

{money(metrics.inventoryValueAtList)}

On-hand × list price

Active promos

{metrics.activeDiscountCount}

Live discount rules

{metrics.lowStockSkuCount > 0 ? (
{metrics.lowStockSkuCount} variant{metrics.lowStockSkuCount === 1 ? "" : "s"} below 5 units — open Inventory and use low-stock filter to replenish.
) : null}

Revenue mix

Settled vs still-open order value in the current filter.

Paid {money(metrics.totalRevenue)} ({paidShare}%) Pending {money(metrics.pendingOrderValue)} ( {100 - paidShare}%)

Est. margin on current stock (at shelf promo prices): {money(marginAtSale)}

Stock health

{stockHealth.total} SKUs in catalogue.

  • Healthy (5+ units) {stockHealth.ok}
  • Low stock {stockHealth.low}
  • Out of stock {stockHealth.out}

Orders snapshot

From the latest {orderInsights.sample} orders loaded.

  • Paid (in sample) {orderInsights.paid}
  • Last 24 hours {orderInsights.last24h}
  • Counter / online paid {orderInsights.counter} / {orderInsights.online}

Top demand SKUs

Higher demand score surfaces first on the customer shop.

{topDemand.length === 0 ? (

No products yet.

) : (
{topDemand.map((p) => ( ))}
Product Category Demand Stock List
{p.name} {p.category} {p.demandScore} {p.inStock} {money(p.unitPrice)}
)}

Category breakdown

SKU count and list-value on hand by aisle.

{categoryRows.length === 0 ? (

No categories yet.

) : (
{categoryRows.map((row) => ( ))}
Category SKUs Units on hand List value
{row.category} {row.count} {row.stock} {money(row.listValue)}
)}
); }