91 lines
3.3 KiB
TypeScript
91 lines
3.3 KiB
TypeScript
import { DashboardStats } from "@/types"
|
|
import { leads } from "./leads"
|
|
import { filterLeadsByPeriod, groupLeadsByInterval } from "@/lib/date-utils"
|
|
|
|
const periodLabels: Record<string, string> = {
|
|
"7days": "Last 7 days",
|
|
"30days": "Last 30 days",
|
|
"6months": "Last 6 months",
|
|
"12months": "Last 12 months",
|
|
}
|
|
|
|
export function getDashboardStats(period: string): DashboardStats {
|
|
const filtered = filterLeadsByPeriod(leads, period)
|
|
const label = periodLabels[period] ?? "Selected period"
|
|
|
|
const totalLeads = filtered.length
|
|
const openLeads = filtered.filter((l) => l.status === "open").length
|
|
const contactedLeads = filtered.filter((l) => l.status === "contacted").length
|
|
const pendingLeads = filtered.filter((l) => l.status === "pending").length
|
|
const closedLeads = filtered.filter((l) => l.status === "closed").length
|
|
const ignoredLeads = filtered.filter((l) => l.status === "ignored").length
|
|
const conversionRate = totalLeads > 0
|
|
? Math.round((closedLeads / totalLeads) * 100)
|
|
: 0
|
|
|
|
function getStatusDistribution() {
|
|
const statusCounts: Record<string, number> = { open: 0, contacted: 0, pending: 0, closed: 0, ignored: 0 }
|
|
filtered.forEach((l) => {
|
|
statusCounts[l.status]++
|
|
})
|
|
|
|
return [
|
|
{ name: "Open", value: statusCounts.open, color: "#3b82f6" },
|
|
{ name: "Contacted", value: statusCounts.contacted, color: "#f59e0b" },
|
|
{ name: "Pending", value: statusCounts.pending, color: "#8b5cf6" },
|
|
{ name: "Closed", value: statusCounts.closed, color: "#10b981" },
|
|
{ name: "Ignored", value: statusCounts.ignored, color: "#6B7280" },
|
|
]
|
|
}
|
|
|
|
const monthlyBreakdown = groupLeadsByInterval(filtered, period).map(m => ({
|
|
label: m.label,
|
|
total: m.leads,
|
|
open: 0,
|
|
contacted: 0,
|
|
pending: 0,
|
|
closed: m.closed,
|
|
ignored: 0,
|
|
}))
|
|
|
|
const trendVal = (current: number, prev: number) => ({
|
|
pct: prev > 0 ? Math.round(((current - prev) / prev) * 100) : current > 0 ? 100 : 0,
|
|
up: current >= prev,
|
|
})
|
|
|
|
const prevFiltered = filterLeadsByPeriod(leads, period === "7days" ? "30days" : "7days")
|
|
const prevTotal = prevFiltered.length
|
|
const prevOpen = prevFiltered.filter((l) => l.status === "open").length
|
|
const prevContacted = prevFiltered.filter((l) => l.status === "contacted").length
|
|
const prevPending = prevFiltered.filter((l) => l.status === "pending").length
|
|
const prevClosed = prevFiltered.filter((l) => l.status === "closed").length
|
|
const prevIgnored = prevFiltered.filter((l) => l.status === "ignored").length
|
|
const prevConversion = prevTotal > 0 ? Math.round((prevClosed / prevTotal) * 100) : 0
|
|
|
|
const trends = {
|
|
totalLeads: trendVal(totalLeads, prevTotal),
|
|
openLeads: trendVal(openLeads, prevOpen),
|
|
contactedLeads: trendVal(contactedLeads, prevContacted),
|
|
pendingLeads: trendVal(pendingLeads, prevPending),
|
|
closedLeads: trendVal(closedLeads, prevClosed),
|
|
ignoredLeads: trendVal(ignoredLeads, prevIgnored),
|
|
conversionRate: trendVal(conversionRate, prevConversion),
|
|
}
|
|
|
|
return {
|
|
totalLeads,
|
|
openLeads,
|
|
contactedLeads,
|
|
pendingLeads,
|
|
closedLeads,
|
|
ignoredLeads,
|
|
conversionRate,
|
|
monthlyBreakdown,
|
|
leadsPerMonth: groupLeadsByInterval(filtered, period),
|
|
recentLeads: filtered.slice(0, 10),
|
|
statusDistribution: getStatusDistribution(),
|
|
periodLabel: label,
|
|
trends,
|
|
}
|
|
}
|