|
|
|
@@ -3,6 +3,7 @@
|
|
|
|
|
import { useEffect, useState } from "react"
|
|
|
|
|
import { motion } from "framer-motion"
|
|
|
|
|
import { cn } from "@/lib/utils"
|
|
|
|
|
import { useWebsiteTheme } from "@/providers/website-theme-provider"
|
|
|
|
|
import { Card, CardContent } from "@/components/ui/card"
|
|
|
|
|
import { LucideIcon } from "lucide-react"
|
|
|
|
|
|
|
|
|
@@ -19,12 +20,12 @@ interface StatCardProps {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const cardColors: Record<string, { bg: string; text: string; glow: string; accent: string }> = {
|
|
|
|
|
"Total Leads": { bg: "bg-[#CC0000]/10", text: "text-[#CC0000] dark:text-[#FF1111]", glow: "via-[rgba(204,0,0,0.25)]", accent: "#CC0000" },
|
|
|
|
|
"Open Leads": { bg: "bg-[#0033CC]/10", text: "text-[#0033CC] dark:text-[#1144FF]", glow: "via-[rgba(0,51,204,0.25)]", accent: "#0033CC" },
|
|
|
|
|
"Contacted": { bg: "bg-[#CC0000]/10", text: "text-[#CC0000] dark:text-[#FF1111]", glow: "via-[rgba(204,0,0,0.25)]", accent: "#CC0000" },
|
|
|
|
|
"Pending": { bg: "bg-[#0033CC]/10", text: "text-[#0033CC] dark:text-[#1144FF]", glow: "via-[rgba(0,51,204,0.25)]", accent: "#0033CC" },
|
|
|
|
|
"Closed": { bg: "bg-[#CC0000]/10", text: "text-[#CC0000] dark:text-[#FF1111]", glow: "via-[rgba(204,0,0,0.25)]", accent: "#CC0000" },
|
|
|
|
|
"Conversion Rate": { bg: "bg-[#0033CC]/10", text: "text-[#0033CC] dark:text-[#1144FF]", glow: "via-[rgba(0,51,204,0.25)]", accent: "#0033CC" },
|
|
|
|
|
"Total Leads": { bg: "bg-[#C84B4B]/10", text: "text-[#C84B4B] dark:text-[#FF1111]", glow: "via-[rgba(200,75,75,0.25)]", accent: "#C84B4B" },
|
|
|
|
|
"Open Leads": { bg: "bg-[#5A8FC4]/10", text: "text-[#5A8FC4] dark:text-[#1144FF]", glow: "via-[rgba(90,143,196,0.25)]", accent: "#5A8FC4" },
|
|
|
|
|
"Contacted": { bg: "bg-[#C84B4B]/10", text: "text-[#C84B4B] dark:text-[#FF1111]", glow: "via-[rgba(200,75,75,0.25)]", accent: "#C84B4B" },
|
|
|
|
|
"Pending": { bg: "bg-[#5A8FC4]/10", text: "text-[#5A8FC4] dark:text-[#1144FF]", glow: "via-[rgba(90,143,196,0.25)]", accent: "#5A8FC4" },
|
|
|
|
|
"Closed": { bg: "bg-[#C84B4B]/10", text: "text-[#C84B4B] dark:text-[#FF1111]", glow: "via-[rgba(200,75,75,0.25)]", accent: "#C84B4B" },
|
|
|
|
|
"Conversion Rate": { bg: "bg-[#5A8FC4]/10", text: "text-[#5A8FC4] dark:text-[#1144FF]", glow: "via-[rgba(90,143,196,0.25)]", accent: "#5A8FC4" },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function computeGoal(max: number): number {
|
|
|
|
@@ -50,6 +51,7 @@ function smoothPath(points: { x: number; y: number }[]): string {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function StatCard({ title, value, icon: Icon, description, index = 0, trend, sparklineField, monthlyBreakdown, conversionRate }: StatCardProps) {
|
|
|
|
|
const { websiteTheme } = useWebsiteTheme()
|
|
|
|
|
const color = cardColors[title] ?? cardColors["Total Leads"]
|
|
|
|
|
|
|
|
|
|
const isNumeric = typeof value === "number"
|
|
|
|
@@ -108,8 +110,8 @@ export function StatCard({ title, value, icon: Icon, description, index = 0, tre
|
|
|
|
|
const sparkColor = color.accent
|
|
|
|
|
|
|
|
|
|
const isRed = index % 2 === 0
|
|
|
|
|
const stripColor = isRed ? "from-[#CC0000] via-[#FF1111] to-[#CC0000]" : "from-[#0033CC] via-[#1144FF] to-[#0033CC]"
|
|
|
|
|
const stripGlow = isRed ? "shadow-[#CC0000]/20" : "shadow-[#0033CC]/20"
|
|
|
|
|
const stripColor = isRed ? "from-[#C84B4B] via-[#C84B4B] to-[#C84B4B]" : "from-[#5A8FC4] via-[#5A8FC4] to-[#5A8FC4]"
|
|
|
|
|
const stripGlow = isRed ? "shadow-[#C84B4B]/20" : "shadow-[#5A8FC4]/20"
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<motion.div
|
|
|
|
@@ -119,28 +121,30 @@ export function StatCard({ title, value, icon: Icon, description, index = 0, tre
|
|
|
|
|
className="relative"
|
|
|
|
|
>
|
|
|
|
|
<Card className="group h-full hover:shadow-xl transition-all duration-200 relative z-10 overflow-hidden">
|
|
|
|
|
{/* Red/Blue gradient top strip */}
|
|
|
|
|
<div className={`h-1 w-full bg-gradient-to-r ${stripColor} ${stripGlow} shadow-sm`} />
|
|
|
|
|
{websiteTheme === "spidey" && (
|
|
|
|
|
<div className={`h-1 w-full bg-gradient-to-r ${stripColor} ${stripGlow} shadow-sm`} />
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Action lines on hover */}
|
|
|
|
|
<div
|
|
|
|
|
className="absolute inset-0 pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-0"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundImage: `url("data:image/svg+xml,%3Csvg width='200' height='200' viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cg stroke='%23CC0000' stroke-width='0.4' opacity='0.15'%3E%3Cline x1='100' y1='100' x2='0' y2='0'/%3E%3Cline x1='100' y1='100' x2='50' y2='0'/%3E%3Cline x1='100' y1='100' x2='100' y2='0'/%3E%3Cline x1='100' y1='100' x2='150' y2='0'/%3E%3Cline x1='100' y1='100' x2='200' y2='0'/%3E%3Cline x1='100' y1='100' x2='200' y2='50'/%3E%3Cline x1='100' y1='100' x2='200' y2='100'/%3E%3Cline x1='100' y1='100' x2='200' y2='150'/%3E%3Cline x1='100' y1='100' x2='200' y2='200'/%3E%3Cline x1='100' y1='100' x2='150' y2='200'/%3E%3Cline x1='100' y1='100' x2='100' y2='200'/%3E%3Cline x1='100' y1='100' x2='50' y2='200'/%3E%3Cline x1='100' y1='100' x2='0' y2='200'/%3E%3Cline x1='100' y1='100' x2='0' y2='150'/%3E%3Cline x1='100' y1='100' x2='0' y2='100'/%3E%3Cline x1='100' y1='100' x2='0' y2='50'/%3E%3C/g%3E%3C/svg%3E")`,
|
|
|
|
|
backgroundSize: "cover",
|
|
|
|
|
backgroundPosition: "center",
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
{websiteTheme === "spidey" && (
|
|
|
|
|
<div
|
|
|
|
|
className="absolute inset-0 pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-0"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundImage: `url("data:image/svg+xml,%3Csvg width='200' height='200' viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cg stroke='%23CC0000' stroke-width='0.4' opacity='0.15'%3E%3Cline x1='100' y1='100' x2='0' y2='0'/%3E%3Cline x1='100' y1='100' x2='50' y2='0'/%3E%3Cline x1='100' y1='100' x2='100' y2='0'/%3E%3Cline x1='100' y1='100' x2='150' y2='0'/%3E%3Cline x1='100' y1='100' x2='200' y2='0'/%3E%3Cline x1='100' y1='100' x2='200' y2='50'/%3E%3Cline x1='100' y1='100' x2='200' y2='100'/%3E%3Cline x1='100' y1='100' x2='200' y2='150'/%3E%3Cline x1='100' y1='100' x2='200' y2='200'/%3E%3Cline x1='100' y1='100' x2='150' y2='200'/%3E%3Cline x1='100' y1='100' x2='100' y2='200'/%3E%3Cline x1='100' y1='100' x2='50' y2='200'/%3E%3Cline x1='100' y1='100' x2='0' y2='200'/%3E%3Cline x1='100' y1='100' x2='0' y2='150'/%3E%3Cline x1='100' y1='100' x2='0' y2='100'/%3E%3Cline x1='100' y1='100' x2='0' y2='50'/%3E%3C/g%3E%3C/svg%3E")`,
|
|
|
|
|
backgroundSize: "cover",
|
|
|
|
|
backgroundPosition: "center",
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<CardContent className="p-6 flex flex-col relative z-[1]">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<p className="text-sm font-medium text-muted-foreground">{title}</p>
|
|
|
|
|
<p className="text-3xl font-bold tracking-tight text-[#111111] dark:text-white">
|
|
|
|
|
<p className="text-3xl font-bold tracking-tight text-[#2D3020] dark:text-white">
|
|
|
|
|
{isNumeric ? display : value}
|
|
|
|
|
</p>
|
|
|
|
|
{description && (
|
|
|
|
|
<p className="text-xs text-[#888888] dark:text-[#666666]">{description}</p>
|
|
|
|
|
<p className="text-xs text-[#8A9078] dark:text-[#666666]">{description}</p>
|
|
|
|
|
)}
|
|
|
|
|
{trend && (
|
|
|
|
|
<span className={cn(
|
|
|
|
@@ -196,9 +200,9 @@ export function StatCard({ title, value, icon: Icon, description, index = 0, tre
|
|
|
|
|
{title === "Conversion Rate" && conversionRate !== undefined && (
|
|
|
|
|
<div className="mt-3">
|
|
|
|
|
<div className="w-full h-1.5 bg-muted rounded-full overflow-hidden">
|
|
|
|
|
<div className="h-full rounded-full bg-gradient-to-r from-[#CC0000] to-[#0033CC]" style={{ width: `${Math.min(conversionRate, 100)}%` }} />
|
|
|
|
|
<div className="h-full rounded-full bg-gradient-to-r from-[#C84B4B] to-[#5A8FC4]" style={{ width: `${Math.min(conversionRate, 100)}%` }} />
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-xs text-[#888888] dark:text-[#666666] mt-1">{conversionRate}% conversion rate</p>
|
|
|
|
|
<p className="text-xs text-[#8A9078] dark:text-[#666666] mt-1">{conversionRate}% conversion rate</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</CardContent>
|
|
|
|
|