mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 12:35:24 +02:00
feat: add MKT tab and integrate profitability dashboard
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user