"use client" import { useState, useEffect } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { TrendingUp, TrendingDown, DollarSign, PieChart, Calculator, Info, AlertTriangle } from "lucide-react"; import { useToast } from "@/hooks/use-toast"; import { formatGBP } from "@/utils/format"; import { getProfitOverview, type ProfitOverview, type DateRange } from "@/lib/services/profit-analytics-service"; import { Skeleton } from "@/components/ui/skeleton"; interface ProfitAnalyticsChartProps { timeRange?: string; dateRange?: DateRange; hideNumbers?: boolean; } export default function ProfitAnalyticsChart({ timeRange, dateRange, hideNumbers = false }: ProfitAnalyticsChartProps) { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const { toast } = useToast(); const maskValue = (value: string): string => { if (!hideNumbers) return value; if (value.includes('£')) return '£***'; if (value.match(/^\d/)) { const numLength = value.replace(/[,\.%]/g, '').length; return '*'.repeat(Math.min(numLength, 4)); } return value; }; useEffect(() => { const fetchData = async () => { try { setIsLoading(true); setError(null); // Use dateRange if provided, otherwise fall back to timeRange, otherwise default to '30' const periodOrRange = dateRange || (timeRange ? timeRange : '30'); const response = await getProfitOverview(periodOrRange); setData(response); } catch (error) { console.error('Error fetching profit data:', error); setError('Failed to load profit analytics'); toast({ title: "Error", description: "Failed to load profit analytics data.", variant: "destructive", }); } finally { setIsLoading(false); } }; fetchData(); }, [timeRange, dateRange, toast]); if (isLoading) { return ( Profit Analytics Track your actual profits based on sales and cost data
{/* Summary Cards Skeleton */}
{[...Array(4)].map((_, i) => ( ))}
{/* Coverage Card Skeleton */}
{/* Products List Skeleton */}
{[...Array(3)].map((_, i) => (
))}
); } if (error || !data) { return ( Profit Analytics

Failed to load profit data

); } if (!data.hasCostData) { return ( Profit Analytics Track your actual profits based on sales and cost data No cost data available
Add cost prices to your products to see profit analytics. Go to Products → Edit → Cost & Profit Tracking section.
); } const profitDirection = data.summary.totalProfit >= 0; // Fallback for backwards compatibility const revenueFromTracked = data.summary.revenueFromTrackedProducts || data.summary.totalRevenue || 0; const totalRevenue = data.summary.totalRevenue || 0; const totalCost = data.summary.totalCost || 0; const totalProfit = data.summary.totalProfit || 0; const productsWithCostData = data.summary.productsWithCostData || 0; const totalProductsSold = data.summary.totalProductsSold || 0; return (
{/* Summary Cards */}
Revenue (Tracked)
{maskValue(formatGBP(revenueFromTracked))}

From {productsWithCostData} tracked items

Total revenue: {maskValue(formatGBP(totalRevenue))}

Total Cost
{maskValue(formatGBP(totalCost))}

From {productsWithCostData} tracked items

Total Profit
{profitDirection ? ( ) : ( )} {maskValue(formatGBP(totalProfit))}

{maskValue(`${(data.summary.overallProfitMargin || 0).toFixed(1)}%`)} margin

Avg Profit/Unit
{maskValue(formatGBP(data.summary.averageProfitPerUnit || 0))}

Per unit sold

{/* Cost Data Coverage */} Cost Data Coverage Percentage of sold items that have cost data for profit calculation
{hideNumbers ? "**%" : `${(data.summary.costDataCoverage || 0).toFixed(1)}%`}
{hideNumbers ? "** / **" : `${productsWithCostData} / ${totalProductsSold}`}
{/* Top Profitable Products */} Most Profitable Products {dateRange ? `Products generating the highest total profit (${new Date(dateRange.from).toLocaleDateString()} - ${new Date(dateRange.to).toLocaleDateString()})` : `Products generating the highest total profit (last ${timeRange || '30'} days)` } {data.topProfitableProducts.length === 0 ? (

No profitable products data available

) : (
{data.topProfitableProducts.map((product, index) => { const profitPositive = product.totalProfit >= 0; return (
{index + 1}

{product.productName}

{product.totalQuantitySold} units sold

{maskValue(formatGBP(product.totalProfit))}
{maskValue(`${product.profitMargin.toFixed(1)}%`)} margin
); })}
)}
); }