Files
ember-market-frontend/components/analytics/MetricsCard.tsx
g fe01f31538
Some checks failed
Build Frontend / build (push) Failing after 7s
Refactor UI imports and update component paths
Replaces imports from 'components/ui' with 'components/common' across the app and dashboard pages, and updates model and API imports to use new paths under 'lib'. Removes redundant authentication checks from several dashboard pages. Adds new dashboard components and utility files, and reorganizes hooks and services into the 'lib' directory for improved structure.
2026-01-13 05:02:13 +00:00

116 lines
3.9 KiB
TypeScript

"use client"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card";
import { TrendingUp, TrendingDown, Minus } from "lucide-react";
import { LucideIcon } from "lucide-react";
import { motion } from "framer-motion";
interface MetricsCardProps {
title: string;
value: string;
description: string;
icon: LucideIcon;
trend: "up" | "down" | "neutral";
trendValue: string;
}
export default function MetricsCard({
title,
value,
description,
icon: Icon,
trend,
trendValue
}: MetricsCardProps) {
const getTrendIcon = () => {
switch (trend) {
case "up":
return <TrendingUp className="h-4 w-4 text-emerald-500" />;
case "down":
return <TrendingDown className="h-4 w-4 text-rose-500" />;
default:
return <Minus className="h-4 w-4 text-muted-foreground" />;
}
};
const getTrendColor = () => {
switch (trend) {
case "up":
return "text-emerald-400 bg-emerald-500/10 border-emerald-500/10";
case "down":
return "text-rose-400 bg-rose-500/10 border-rose-500/10";
default:
return "text-blue-400 bg-blue-500/10 border-blue-500/10";
}
};
const getCategoryColor = () => {
const t = title.toLowerCase();
if (t.includes("revenue") || t.includes("profit")) return "amber";
if (t.includes("order")) return "blue";
if (t.includes("customer")) return "indigo";
if (t.includes("product") || t.includes("inventory")) return "purple";
return "primary";
}
const categoryColor = getCategoryColor();
const getIconContainerColor = () => {
switch (categoryColor) {
case "amber": return "bg-amber-500/15 text-amber-500 border-amber-500/20";
case "blue": return "bg-blue-500/15 text-blue-500 border-blue-500/20";
case "indigo": return "bg-indigo-500/15 text-indigo-500 border-indigo-500/20";
case "purple": return "bg-purple-500/15 text-purple-500 border-purple-500/20";
default: return "bg-primary/15 text-primary border-primary/20";
}
}
const getBadgeColor = () => {
switch (categoryColor) {
case "amber": return "bg-amber-500/10 text-amber-400/80 border-amber-500/20";
case "blue": return "bg-blue-500/10 text-blue-400/80 border-blue-500/20";
case "indigo": return "bg-indigo-500/10 text-indigo-400/80 border-indigo-500/20";
case "purple": return "bg-purple-500/10 text-purple-400/80 border-purple-500/20";
default: return "bg-primary/10 text-primary/60 border-primary/20";
}
}
return (
<motion.div
whileHover={{ y: -4 }}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
>
<Card className="glass-morphism premium-card relative overflow-hidden group border-white/5">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-4 relative z-10">
<CardTitle className="text-[10px] font-bold tracking-wider text-white/40 uppercase">
{title}
</CardTitle>
<div className={`p-2.5 rounded-2xl border ${getIconContainerColor()} transition-all duration-300 group-hover:scale-105`}>
<Icon className="h-4 w-4" />
</div>
</CardHeader>
<CardContent className="relative z-10">
<div className="space-y-1.5">
<div className="text-3xl font-bold text-white">
{value}
</div>
<p className="text-[11px] text-white/40">
{description}
</p>
</div>
<div className="flex items-center gap-2 mt-6 pt-5">
<div className={`flex items-center gap-1.5 px-3 py-1 rounded-full border ${getTrendColor()} transition-all duration-300`}>
{getTrendIcon()}
<span className="text-[10px] font-bold uppercase tracking-wide">
{trend === "up" ? "+" : ""}{trendValue}
</span>
</div>
</div>
</CardContent>
</Card>
</motion.div>
);
}