Files
CrazeAnalytix/components/mkt/KPICards.tsx
T

75 lines
2.7 KiB
TypeScript

import React from 'react';
import { formatCurrency, formatPercent, calculateProductMetrics } from './utils';
import { Product } from './types';
import { DollarSign, TrendingUp, AlertTriangle, Activity } from 'lucide-react';
interface KPICardsProps {
products: Product[];
includeCOGS: boolean;
}
export const KPICards: React.FC<KPICardsProps> = ({ products, includeCOGS }) => {
const totals = products.reduce(
(acc, product) => {
const metrics = calculateProductMetrics(product, includeCOGS);
acc.grossSales += product.grossSales;
acc.netMargin += metrics.netMargin;
acc.ppcSpend += product.ppcSpend;
acc.chargebacks += product.chargebacks;
return acc;
},
{ grossSales: 0, netMargin: 0, ppcSpend: 0, chargebacks: 0 }
);
const marginPercent = totals.grossSales > 0 ? totals.netMargin / totals.grossSales : 0;
const tacos = totals.grossSales > 0 ? totals.ppcSpend / totals.grossSales : 0;
const cards = [
{
title: 'Total Sales',
value: formatCurrency(totals.grossSales),
icon: <DollarSign className="w-6 h-6 text-emerald-400" />,
description: 'Gross revenue for the period',
},
{
title: 'Est. Net Margin',
value: formatPercent(marginPercent),
subValue: formatCurrency(totals.netMargin),
icon: <TrendingUp className="w-6 h-6 text-indigo-400" />,
description: includeCOGS ? 'After COGS and expenses' : 'Before COGS',
},
{
title: 'TACOS',
value: formatPercent(tacos),
icon: <Activity className="w-6 h-6 text-purple-400" />,
description: 'Total advertising cost / Sales',
},
{
title: 'Total Chargebacks',
value: formatCurrency(totals.chargebacks),
icon: <AlertTriangle className="w-6 h-6 text-rose-400" />,
description: 'Logistics issues / returns',
},
];
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{cards.map((card, idx) => (
<div key={idx} className="bg-[#13161F] rounded-xl border border-[#1F2433] p-6 flex flex-col">
<div className="flex justify-between items-start mb-4">
<h3 className="text-sm font-medium text-slate-400 uppercase tracking-wider">{card.title}</h3>
<div className="p-2 bg-[#0A0C10] rounded-lg border border-[#1F2433]">{card.icon}</div>
</div>
<div className="flex items-baseline gap-2">
<span className="text-2xl font-bold text-white">{card.value}</span>
{card.subValue && (
<span className="text-sm font-medium text-slate-400">({card.subValue})</span>
)}
</div>
<p className="text-xs text-slate-500 mt-2">{card.description}</p>
</div>
))}
</div>
);
};