From 0ffd02e22e299bef8aedc405ce9b75e3269c9a86 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 15 Jan 2026 16:26:28 -0500 Subject: [PATCH] Add baseline comparison for discount simulator --- .../discount-simulator/SummaryCard.tsx | 123 +++++++++++++++--- inventory/src/pages/DiscountSimulator.tsx | 35 +++-- 2 files changed, 133 insertions(+), 25 deletions(-) diff --git a/inventory/src/components/discount-simulator/SummaryCard.tsx b/inventory/src/components/discount-simulator/SummaryCard.tsx index 0189faa..dba4b26 100644 --- a/inventory/src/components/discount-simulator/SummaryCard.tsx +++ b/inventory/src/components/discount-simulator/SummaryCard.tsx @@ -1,9 +1,12 @@ +import { differenceInDays } from "date-fns"; import { Card, CardContent } from "@/components/ui/card"; import { formatCurrency } from "@/utils/productUtils"; import { DiscountSimulationResponse } from "@/types/discount-simulator"; import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import { Separator } from "@/components/ui/separator"; +import { Bookmark, X } from "lucide-react"; // Utility function to interpolate between two colors const interpolateColor = (color1: [number, number, number], color2: [number, number, number], ratio: number): [number, number, number] => { @@ -49,6 +52,37 @@ const getProfitPercentageColor = (percentage: number): string => { interface SummaryCardProps { result?: DiscountSimulationResponse; isLoading: boolean; + baselineResult?: DiscountSimulationResponse; + onSaveAsBaseline?: () => void; + onClearBaseline?: () => void; +} + +function calculateAnnualizedProfitDiff( + current: DiscountSimulationResponse, + baseline: DiscountSimulationResponse +): number | null { + const currentTotals = current.totals; + const baselineTotals = baseline.totals; + + if (!currentTotals || !baselineTotals) return null; + + // Calculate days in the current simulation period + const startDate = new Date(current.dateRange.start); + const endDate = new Date(current.dateRange.end); + const daysInPeriod = differenceInDays(endDate, startDate) + 1; // +1 to include both start and end days + + if (daysInPeriod <= 0) return null; + + // Profit difference per order + const profitDiffPerOrder = currentTotals.weightedProfitAmount - baselineTotals.weightedProfitAmount; + + // Total profit difference for the period + const totalProfitDiff = profitDiffPerOrder * currentTotals.orders; + + // Annualize: scale to 365 days + const annualizedDiff = (totalProfitDiff / daysInPeriod) * 365; + + return annualizedDiff; } const formatPercent = (value: number) => { @@ -56,16 +90,23 @@ const formatPercent = (value: number) => { return `${(value * 100).toFixed(2)}%`; }; -export function SummaryCard({ result, isLoading }: SummaryCardProps) { +export function SummaryCard({ + result, + isLoading, + baselineResult, + onSaveAsBaseline, + onClearBaseline +}: SummaryCardProps) { if (isLoading && !result) { return ( - - -
+ + +
- +
+
@@ -89,32 +130,84 @@ export function SummaryCard({ result, isLoading }: SummaryCardProps) { : "destructive" : "secondary"; + // Calculate annualized profit difference if baseline exists + const annualizedDiff = result && baselineResult + ? calculateAnnualizedProfitDiff(result, baselineResult) + : null; + const hasBaseline = !!baselineResult; + const isPositiveDiff = annualizedDiff !== null && annualizedDiff >= 0; + return ( - - -
+ + +
+ {/* Weighted Average Profit */}
-

Weighted Average Profit

+

Weighted Average Profit

{totals ? ( - {weightedProfitPercent} ) : ( - + {weightedProfitPercent} )}
- + + + + {/* Weighted Profit Per Order */}
-

Weighted Profit Per Order

- +

Weighted Profit Per Order

+ {weightedProfitAmount}
+ + {/* Baseline comparison section */} + {hasBaseline && annualizedDiff !== null && ( + <> + +
+

Annual Change vs Baseline

+ + {isPositiveDiff ? '+' : ''}{formatCurrency(annualizedDiff)} + +
+ + + )} + + {/* Save as baseline button */} + {result && !hasBaseline && ( + <> + + + + )}
diff --git a/inventory/src/pages/DiscountSimulator.tsx b/inventory/src/pages/DiscountSimulator.tsx index 43b1c44..c7f2f7a 100644 --- a/inventory/src/pages/DiscountSimulator.tsx +++ b/inventory/src/pages/DiscountSimulator.tsx @@ -62,6 +62,7 @@ export function DiscountSimulator() { const [pointDollarValue, setPointDollarValue] = useState(DEFAULT_POINT_VALUE); const [pointDollarTouched, setPointDollarTouched] = useState(false); const [simulationResult, setSimulationResult] = useState(undefined); + const [baselineResult, setBaselineResult] = useState(undefined); const [isSimulating, setIsSimulating] = useState(false); const [hasLoadedConfig, setHasLoadedConfig] = useState(false); const [loadedFromStorage, setLoadedFromStorage] = useState(false); @@ -402,6 +403,16 @@ export function DiscountSimulator() { } }; + const handleSaveAsBaseline = useCallback(() => { + if (simulationResult) { + setBaselineResult(simulationResult); + } + }, [simulationResult]); + + const handleClearBaseline = useCallback(() => { + setBaselineResult(undefined); + }, []); + const resetConfig = useCallback(() => { skipAutoRunRef.current = true; const defaultRange = getDefaultDateRange(); @@ -478,17 +489,21 @@ export function DiscountSimulator() { {/* Right Side - Results */}
- {/* Top Right - Summary and Chart */} -
-
- -
-
- -
+ {/* Top - Summary (horizontal) */} + + + {/* Chart */} +
+
- - {/* Bottom Right - Table */} + + {/* Table */}