From b18cc63d19e1dc8b522b927e9e9261aa05175c49 Mon Sep 17 00:00:00 2001 From: Yves Gugger Date: Tue, 16 Dec 2025 17:15:34 +0100 Subject: [PATCH] Earnings Dashboard: Duplikate entfernt, cleaner Layout --- frontend/src/components/admin/EarningsTab.tsx | 557 ++++++------------ 1 file changed, 169 insertions(+), 388 deletions(-) diff --git a/frontend/src/components/admin/EarningsTab.tsx b/frontend/src/components/admin/EarningsTab.tsx index 78297b7..4fcb13b 100644 --- a/frontend/src/components/admin/EarningsTab.tsx +++ b/frontend/src/components/admin/EarningsTab.tsx @@ -17,7 +17,6 @@ import { Target, Activity, BarChart3, - PieChart as PieChartIcon, } from 'lucide-react' import clsx from 'clsx' import { @@ -25,11 +24,7 @@ import { Area, BarChart, Bar, - LineChart, Line, - PieChart, - Pie, - Cell, XAxis, YAxis, CartesianGrid, @@ -76,24 +71,22 @@ interface HistoryData { timestamp: string } -// Custom colors matching the design system +// Custom colors const COLORS = { - primary: '#ef4444', // red-500 - accent: '#22c55e', // green-500 - amber: '#f59e0b', // amber-500 - blue: '#3b82f6', // blue-500 - purple: '#8b5cf6', // purple-500 - cyan: '#06b6d4', // cyan-500 - rose: '#f43f5e', // rose-500 + primary: '#ef4444', + accent: '#22c55e', + amber: '#f59e0b', + blue: '#3b82f6', + rose: '#f43f5e', } const TIER_COLORS = { - scout: '#6b7280', // gray-500 - trader: '#22c55e', // green-500 - tycoon: '#f59e0b', // amber-500 + scout: '#6b7280', + trader: '#22c55e', + tycoon: '#f59e0b', } -// Custom tooltip component +// Custom tooltip const CustomTooltip = ({ active, payload, label }: any) => { if (!active || !payload || !payload.length) return null @@ -102,15 +95,10 @@ const CustomTooltip = ({ active, payload, label }: any) => {

{label}

{payload.map((entry: any, index: number) => (
-
+
{entry.name}: - {entry.name.includes('$') || entry.dataKey === 'mrr' || entry.dataKey === 'arr' - ? `$${entry.value.toLocaleString()}` - : entry.value} + {entry.dataKey === 'mrr' ? `$${entry.value.toLocaleString()}` : entry.value}
))} @@ -123,7 +111,7 @@ export function EarningsTab() { const [history, setHistory] = useState(null) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) - const [activeChart, setActiveChart] = useState<'mrr' | 'customers' | 'tiers'>('mrr') + const [activeChart, setActiveChart] = useState<'revenue' | 'growth'>('revenue') const loadData = async () => { try { @@ -167,18 +155,8 @@ export function EarningsTab() { ) } - // Prepare pie chart data - const pieData = [ - { name: 'Tycoon', value: data.tier_breakdown.tycoon.count, color: TIER_COLORS.tycoon }, - { name: 'Trader', value: data.tier_breakdown.trader.count, color: TIER_COLORS.trader }, - { name: 'Scout', value: data.tier_breakdown.scout.count, color: TIER_COLORS.scout }, - ].filter(d => d.value > 0) - - // Revenue pie data - const revenuePieData = [ - { name: 'Tycoon', value: data.tier_breakdown.tycoon.revenue, color: TIER_COLORS.tycoon }, - { name: 'Trader', value: data.tier_breakdown.trader.revenue, color: TIER_COLORS.trader }, - ].filter(d => d.value > 0) + // Calculate net growth + const netGrowth = data.new_subscriptions.month - data.churn.month return (
@@ -190,7 +168,7 @@ export function EarningsTab() { Revenue Dashboard

- Last updated: {new Date(data.timestamp).toLocaleString()} + Updated: {new Date(data.timestamp).toLocaleString()}

- {/* Main KPI Cards */} -
+ {/* Main KPIs - 6 Cards */} +
{/* MRR */} -
-
-
-
- - MRR -
-
- ${data.mrr.toLocaleString()} -
- {history.metrics.mrr_growth_percent !== 0 && ( -
0 ? "text-green-400" : "text-rose-400" - )}> - {history.metrics.mrr_growth_percent > 0 ? ( - - ) : ( - - )} - {Math.abs(history.metrics.mrr_growth_percent)}% vs last month -
- )} +
+
+ + MRR
+
+ ${data.mrr.toLocaleString()} +
+ {history.metrics.mrr_growth_percent !== 0 && ( +
0 ? "text-green-400" : "text-rose-400" + )}> + {history.metrics.mrr_growth_percent > 0 ? : } + {Math.abs(history.metrics.mrr_growth_percent)}% +
+ )}
{/* ARR */} -
-
-
-
- - ARR -
-
- ${data.arr.toLocaleString()} -
-
- Projected annual -
+
+
+ + ARR
+
+ ${data.arr.toLocaleString()} +
+
projected
- {/* Customers */} -
-
+ {/* Paying Customers */} +
+
- Paying + Paying
-
+
{data.paying_customers}
-
- - +{data.new_subscriptions.week} this week -
+
customers
{/* ARPU */} -
-
+
+
- ARPU + ARPU
-
- ${history.metrics.arpu.toFixed(2)} +
+ ${history.metrics.arpu.toFixed(0)}
-
- LTV: ${history.metrics.ltv.toFixed(0)} +
LTV: ${history.metrics.ltv.toFixed(0)}
+
+ + {/* Net Growth */} +
= 0 ? "border-green-500/20 bg-green-500/[0.03]" : "border-rose-500/20 bg-rose-500/[0.03]" + )}> +
+ {netGrowth >= 0 ? : } + Net Growth
+
= 0 ? "text-green-400" : "text-rose-400")}> + {netGrowth >= 0 ? '+' : ''}{netGrowth} +
+
this month
+
+ + {/* Yield Revenue */} +
+
+ + Yield +
+
+ ${data.yield_revenue_month.toFixed(0)} +
+
30% cut
- {/* MRR Chart */} + {/* Chart Section */}
-

Revenue Trend

+

Trend

- {(['mrr', 'customers', 'tiers'] as const).map((chart) => ( + {(['revenue', 'growth'] as const).map((chart) => ( ))}
-
+
- {activeChart === 'mrr' ? ( + {activeChart === 'revenue' ? ( - - - - - - `$${value}`} - /> + + `$${v}`} /> } /> - + - ) : activeChart === 'customers' ? ( + ) : ( - - + + } /> + {v}} /> - + - ) : ( - - - - - } /> - {value}} - /> - - - - )}
- {/* Bottom Section: Tier Breakdown + Pie Charts */} -
- {/* Tier Breakdown */} -
-
-

Subscription Breakdown

+ {/* Subscription Breakdown - Clean Table */} +
+
+

Subscriptions

+
+
+ {/* Tycoon */} +
+
+ +
+
+

Tycoon

+

$29/mo

+
+
+

{data.tier_breakdown.tycoon.count}

+
+
+

${data.tier_breakdown.tycoon.revenue}

+

/month

+
+
+
0 ? (data.tier_breakdown.tycoon.revenue / data.mrr) * 100 : 0}%` }} + /> +
-
- {/* Tycoon */} -
-
-
- -
-
-

Tycoon

-

$29/month · Premium

-
-
-
-

{data.tier_breakdown.tycoon.count}

-

${data.tier_breakdown.tycoon.revenue}/mo

-
-
-
-
-
- {/* Trader */} -
-
-
- -
-
-

Trader

-

$9/month · Standard

-
-
-
-

{data.tier_breakdown.trader.count}

-

${data.tier_breakdown.trader.revenue}/mo

-
-
-
-
+ {/* Trader */} +
+
+
+
+

Trader

+

$9/mo

+
+
+

{data.tier_breakdown.trader.count}

+
+
+

${data.tier_breakdown.trader.revenue}

+

/month

+
+
+
0 ? (data.tier_breakdown.trader.revenue / data.mrr) * 100 : 0}%` }} + /> +
+
- {/* Scout */} -
-
-
- -
-
-

Scout

-

Free tier

-
-
-
-

{data.tier_breakdown.scout.count}

-

$0/mo

-
-
-
-
+ {/* Scout */} +
+
+ +
+
+

Scout

+

Free

+
+
+

{data.tier_breakdown.scout.count}

+
+
+

$0

+

/month

+
+
+
- {/* Pie Charts */} -
- {/* Customer Distribution */} -
-
- - Customers -
-
- - - - {pieData.map((entry, index) => ( - - ))} - - { - if (!active || !payload || !payload.length) return null - const data = payload[0].payload - return ( -
-

- {data.name}: {data.value} -

-
- ) - }} - /> -
-
-
-
- {pieData.map((entry, index) => ( -
-
- {entry.name} -
- ))} -
+ {/* Total Row */} +
+
+ Total Monthly
- - {/* Revenue Distribution */} -
-
- - Revenue Split -
-
- - - - {revenuePieData.map((entry, index) => ( - - ))} - - { - if (!active || !payload || !payload.length) return null - const data = payload[0].payload - return ( -
-

- {data.name}: ${data.value} -

-
- ) - }} - /> -
-
-
-
- {revenuePieData.map((entry, index) => ( -
-
- {entry.name} -
- ))} -
-
-
-
- - {/* Growth Metrics */} -
- {/* New This Week */} -
-
- - New This Week -
-

{data.new_subscriptions.week}

-

paid subscriptions

-
- - {/* New This Month */} -
-
- - New This Month -
-

{data.new_subscriptions.month}

-

paid subscriptions

-
- - {/* Churn */} -
-
- - Churned -
-

{data.churn.month}

-

this month

-
- - {/* Yield Revenue */} -
-
- - Yield Revenue -
-

${data.yield_revenue_month.toFixed(0)}

-

platform cut (30%)

-
-
- - {/* Total Revenue Summary */} -
-
-
-

Total Monthly Revenue

-

Subscriptions + Yield Platform Cut

-
-
-

${data.total_revenue_month.toLocaleString()}

-

- ${(data.total_revenue_month * 12).toLocaleString()} projected ARR -

+
+ ${data.total_revenue_month.toLocaleString()}