"use client";
import { useMemo } from "react";
import type { Deal, PipelineStage } from "@/types";
import {
DollarSign,
TrendingUp,
Target,
BarChart3,
Trophy,
XCircle,
Info,
} from "lucide-react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
interface PipelineAnalyticsProps {
stages: PipelineStage[];
deals: Deal[];
}
function formatCurrency(value: number) {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(value);
}
/**
* Weighted pipeline value: value × per-stage probability.
* First stage ≈ 10%, stages interpolate up to 90% before the final stage,
* final stage (Won) = 100%. Lost deals excluded.
*/
function computeStageProbability(
stage: PipelineStage,
sortedStages: PipelineStage[],
): number {
const n = sortedStages.length;
if (n <= 1) return 1;
const index = sortedStages.findIndex((s) => s.id === stage.id);
if (index < 0) return 0;
if (index === n - 1) return 1;
const slots = n - 1;
if (slots <= 1) return 0.1;
const t = index / (slots - 1);
return 0.1 + t * (0.9 - 0.1);
}
export function PipelineAnalytics({ stages, deals }: PipelineAnalyticsProps) {
const sortedStages = useMemo(
() => [...stages].sort((a, b) => a.position - b.position),
[stages],
);
const stats = useMemo(() => {
const active = deals.filter((d) => d.status !== "lost");
const openDeals = active.filter((d) => d.status !== "won");
const totalCount = active.length;
const totalValue = active.reduce((sum, d) => sum + Number(d.value || 0), 0);
const avgValue = totalCount > 0 ? totalValue / totalCount : 0;
const stageById = new Map(sortedStages.map((s) => [s.id, s]));
const weightedValue = openDeals.reduce((sum, d) => {
const stage = stageById.get(d.stage_id);
if (!stage) return sum;
const prob = computeStageProbability(stage, sortedStages);
return sum + Number(d.value || 0) * prob;
}, 0);
const now = new Date();
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
const thisMonth = (d: Deal) => {
const ts = d.updated_at ?? d.created_at;
return ts ? new Date(ts) >= monthStart : false;
};
const wonThisMonth = deals.filter(
(d) => d.status === "won" && thisMonth(d),
).length;
const lostThisMonth = deals.filter(
(d) => d.status === "lost" && thisMonth(d),
).length;
return {
totalCount,
totalValue,
avgValue,
weightedValue,
wonThisMonth,
lostThisMonth,
};
}, [deals, sortedStages]);
return (
{value}