mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 12:15:23 +02:00
feat: show both current and previous values in Ads comparison view
- AdsPerformance.tsx: Update SummaryCard and TableCell to display 'Prev: [Value]' explicitly - AdsPerformance.tsx: Standardized formatting using de-DE locale across all comparison metrics - AdsPerformance.tsx: Improved growth calculation reliability in comparison mode
This commit is contained in:
@@ -183,19 +183,19 @@ const AdsPerformance: React.FC<AdsPerformanceProps> = ({ data, filters }) => {
|
||||
|
||||
{/* Summary Grid */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-7 xl:grid-cols-13 gap-4 bg-[#141820] p-4 rounded-xl border border-white/5 shadow-xl">
|
||||
<SummaryCard label="TOTAL SALES" value={formatCurrency(totals.curr.salesTotal)} prevValue={totals.prev?.salesTotal} color="text-emerald-400" />
|
||||
<SummaryCard label="ADS SALES" value={formatCurrency(totals.curr.salesAds)} prevValue={totals.prev?.salesAds} color="text-orange-400" />
|
||||
<SummaryCard label="ORGANIC SALES" value={formatCurrency(totals.curr.salesOrganic)} prevValue={totals.prev?.salesOrganic} color="text-blue-400" />
|
||||
<SummaryCard label="AD SPEND" value={formatCurrency(totals.curr.cost)} prevValue={totals.prev?.cost} color="text-indigo-400" />
|
||||
<SummaryCard label="IMPR." value={formatNumber(totals.curr.impressions)} prevValue={totals.prev?.impressions} />
|
||||
<SummaryCard label="CLICKS" value={formatNumber(totals.curr.clicks)} prevValue={totals.prev?.clicks} />
|
||||
<SummaryCard label="ADS ORDERS" value={formatNumber(totals.curr.conversions)} prevValue={totals.prev?.conversions} />
|
||||
<SummaryCard label="CPC" value={`$${currMetrics.cpc.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`} prevValue={prevMetrics?.cpc} inverse />
|
||||
<SummaryCard label="CTR" value={formatPercent(currMetrics.ctr)} prevValue={prevMetrics?.ctr} />
|
||||
<SummaryCard label="CVR" value={formatPercent(currMetrics.cvr)} prevValue={prevMetrics?.cvr} />
|
||||
<SummaryCard label="ACOS" value={formatPercent(currMetrics.acos)} prevValue={prevMetrics?.acos} inverse />
|
||||
<SummaryCard label="ROAS" value={currMetrics.roas.toFixed(2)} prevValue={prevMetrics?.roas} color="text-blue-300" />
|
||||
<SummaryCard label="TACOS" value={formatPercent(currMetrics.tacos)} prevValue={prevMetrics?.tacos} inverse />
|
||||
<SummaryCard label="TOTAL SALES" value={totals.curr.salesTotal} prevValue={totals.prev?.salesTotal} format="currency" color="text-emerald-400" />
|
||||
<SummaryCard label="ADS SALES" value={totals.curr.salesAds} prevValue={totals.prev?.salesAds} format="currency" color="text-orange-400" />
|
||||
<SummaryCard label="ORGANIC SALES" value={totals.curr.salesOrganic} prevValue={totals.prev?.salesOrganic} format="currency" color="text-blue-400" />
|
||||
<SummaryCard label="AD SPEND" value={totals.curr.cost} prevValue={totals.prev?.cost} format="currency" color="text-indigo-400" />
|
||||
<SummaryCard label="IMPR." value={totals.curr.impressions} prevValue={totals.prev?.impressions} format="number" />
|
||||
<SummaryCard label="CLICKS" value={totals.curr.clicks} prevValue={totals.prev?.clicks} format="number" />
|
||||
<SummaryCard label="ADS ORDERS" value={totals.curr.conversions} prevValue={totals.prev?.conversions} format="number" />
|
||||
<SummaryCard label="CPC" value={currMetrics.cpc} prevValue={prevMetrics?.cpc} format="number" prefix="$" decimals={2} inverse />
|
||||
<SummaryCard label="CTR" value={currMetrics.ctr} prevValue={prevMetrics?.ctr} format="percent" />
|
||||
<SummaryCard label="CVR" value={currMetrics.cvr} prevValue={prevMetrics?.cvr} format="percent" />
|
||||
<SummaryCard label="ACOS" value={currMetrics.acos} prevValue={prevMetrics?.acos} format="percent" inverse />
|
||||
<SummaryCard label="ROAS" value={currMetrics.roas} prevValue={prevMetrics?.roas} format="number" decimals={2} color="text-blue-300" />
|
||||
<SummaryCard label="TACOS" value={currMetrics.tacos} prevValue={prevMetrics?.tacos} format="percent" inverse />
|
||||
</div>
|
||||
|
||||
{/* Table Section */}
|
||||
@@ -271,22 +271,49 @@ const AdsPerformance: React.FC<AdsPerformanceProps> = ({ data, filters }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const SummaryCard = ({ label, value, prevValue, color = "text-white", inverse = false }: { label: string; value: string; prevValue?: number; color?: string; inverse?: boolean }) => {
|
||||
// Correctly parse value regardless of locale formatting (handles dots as thousands and commas as decimals)
|
||||
const numericValue = parseFloat(value.replace(/[^\d,]/g, '').replace(',', '.'));
|
||||
const SummaryCard = ({
|
||||
label,
|
||||
value,
|
||||
prevValue,
|
||||
format,
|
||||
prefix = "",
|
||||
decimals = 0,
|
||||
color = "text-white",
|
||||
inverse = false
|
||||
}: {
|
||||
label: string;
|
||||
value: number;
|
||||
prevValue?: number;
|
||||
format: 'currency' | 'number' | 'percent';
|
||||
prefix?: string;
|
||||
decimals?: number;
|
||||
color?: string;
|
||||
inverse?: boolean
|
||||
}) => {
|
||||
const growth = prevValue !== undefined && prevValue > 0
|
||||
? ((numericValue - prevValue) / prevValue) * 100
|
||||
? ((value - prevValue) / prevValue) * 100
|
||||
: null;
|
||||
|
||||
const formatVal = (v: number) => {
|
||||
if (format === 'currency') return `$${Math.round(v).toLocaleString('de-DE')}`;
|
||||
if (format === 'percent') return v.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + "%";
|
||||
return prefix + v.toLocaleString('de-DE', { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center p-2 border-r last:border-r-0 border-white/5 w-full min-w-[80px]">
|
||||
<span className={`${color} text-[10px] font-black tracking-widest mb-1 text-center leading-tight uppercase`}>{label}</span>
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-sm font-bold text-white transition-all">{value}</span>
|
||||
{growth !== null && (
|
||||
<span className={`text-[9px] font-bold mt-0.5 ${(growth > 0 ? !inverse : inverse) ? 'text-emerald-400' : 'text-rose-500'}`}>
|
||||
{growth > 0 ? '↑' : '↓'} {Math.abs(growth).toFixed(1)}%
|
||||
</span>
|
||||
<span className="text-sm font-bold text-white transition-all">{formatVal(value)}</span>
|
||||
{prevValue !== undefined && (
|
||||
<div className="flex flex-col items-center mt-0.5">
|
||||
<span className="text-[9px] text-slate-500 font-medium">Prev: {formatVal(prevValue)}</span>
|
||||
{growth !== null && (
|
||||
<span className={`text-[9px] font-black ${(growth > 0 ? !inverse : inverse) ? 'text-emerald-400' : 'text-rose-500'}`}>
|
||||
{growth > 0 ? '↑' : '↓'} {Math.abs(growth).toFixed(1)}%
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -296,25 +323,31 @@ const SummaryCard = ({ label, value, prevValue, color = "text-white", inverse =
|
||||
const TableCell = ({ value, prevValue, format, prefix = "", inverse = false, highlight = false }: { value: number; prevValue?: number; format: 'currency' | 'number' | 'percent'; prefix?: string; inverse?: boolean; highlight?: boolean }) => {
|
||||
const growth = prevValue !== undefined && prevValue > 0 ? ((value - prevValue) / prevValue) * 100 : null;
|
||||
|
||||
let displayValue = "";
|
||||
if (format === 'currency') displayValue = Math.round(value).toLocaleString('de-DE');
|
||||
else if (format === 'number') {
|
||||
if (prefix === "$") {
|
||||
displayValue = "$" + value.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
} else {
|
||||
displayValue = prefix + Math.round(value).toLocaleString('de-DE');
|
||||
const formatVal = (v: number) => {
|
||||
if (format === 'currency') return Math.round(v).toLocaleString('de-DE');
|
||||
if (format === 'number') {
|
||||
if (prefix === "$") {
|
||||
return "$" + v.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
} else {
|
||||
return prefix + Math.round(v).toLocaleString('de-DE');
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (format === 'percent') displayValue = value.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + "%";
|
||||
return v.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + "%";
|
||||
};
|
||||
|
||||
return (
|
||||
<td className="p-4 text-right">
|
||||
<div className="flex flex-col items-end">
|
||||
<span className={`font-bold ${highlight ? 'text-blue-300' : 'text-white'}`}>{displayValue}</span>
|
||||
{growth !== null && (
|
||||
<span className={`text-[9px] font-medium ${(growth > 0 ? !inverse : inverse) ? 'text-emerald-400' : 'text-rose-400'} opacity-80`}>
|
||||
{growth > 0 ? '+' : ''}{growth.toFixed(1)}%
|
||||
</span>
|
||||
<span className={`font-bold ${highlight ? 'text-blue-300' : 'text-white'}`}>{formatVal(value)}</span>
|
||||
{prevValue !== undefined && (
|
||||
<div className="flex flex-col items-end scale-[0.85] origin-right opacity-80 mt-0.5">
|
||||
<span className="text-[10px] text-slate-500 font-medium">Prev: {formatVal(prevValue)}</span>
|
||||
{growth !== null && (
|
||||
<span className={`text-[10px] font-black ${(growth > 0 ? !inverse : inverse) ? 'text-emerald-400' : 'text-rose-400'}`}>
|
||||
{growth > 0 ? '+' : ''}{growth.toFixed(1)}%
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user