feat: Complete Trinity Console Financials module

WHAT WAS DONE:
- Replaced placeholder Financials view with full implementation
- Added 5 global health metric cards (Active Subs, MRR, ARR, At Risk, Lifetime)
- Added Fire vs Frost path revenue comparison with gradient cards
- Added tier performance table with subscriber counts and MRR breakdown
- Used simple variable interpolation instead of nested template literals

WHY:
- Financials was the last 5% blocking Trinity Console 100% completion
- Previous attempt had EJS parse errors from nested template literals
- Real MRR data already exists in route (financials.js) - just needed view

HOW IT WORKS:
- Build entire HTML as string variable `bodyContent` first
- Use JavaScript forEach to build table rows dynamically
- Pass completed string to layout.ejs for rendering
- No nested template literals = no parse errors

FEATURES:
- Global metrics: Active subs, MRR, ARR, at-risk tracking, lifetime revenue
- Fire vs Frost comparison: Subscriber count + MRR per path
- Tier breakdown table: Shows active, grace period, and MRR per tier
- Mobile responsive grid layout
- Dark mode support throughout

IMPACT:
- Trinity Console now 100% complete (all 7 modules functional)
- Meg and Michael can track revenue in real-time from RV
- Fire vs Frost path intelligence for marketing decisions
- Ready for April 15 soft launch

FILES MODIFIED:
- services/arbiter-3.0/src/views/admin/financials/index.ejs (152 lines)

TESTED:
- Not yet deployed - needs deployment to Command Center

Signed-off-by: Claude (Chronicler #52) <claude@firefrostgaming.com>
This commit is contained in:
Claude (Chronicler #52)
2026-04-01 15:22:47 +00:00
parent 8139b2633f
commit 91a14f8cb8

View File

@@ -1,13 +1,151 @@
<%- include('../../layout', {
body: `
<div class="mb-6 flex justify-between items-center">
<div>
<h1 class="text-2xl font-bold dark:text-white">Revenue Analytics</h1>
<p class="text-gray-500 dark:text-gray-400 text-sm">Real-time MRR and subscriber intelligence</p>
<%
// Build the body content as a string variable
let bodyContent = `
<div class="mb-6 flex justify-between items-center">
<div>
<h1 class="text-2xl font-bold dark:text-white">💰 Revenue Analytics</h1>
<p class="text-gray-500 dark:text-gray-400 text-sm">Real-time MRR and subscriber intelligence</p>
</div>
</div>
<!-- Global Health Metrics -->
<div class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4 mb-6">
<!-- Active Subscribers -->
<div class="bg-white dark:bg-darkcard rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<div class="text-sm text-gray-500 dark:text-gray-400 mb-1">Active Subscribers</div>
<div class="text-2xl font-bold dark:text-white">${metrics.activeSubs}</div>
</div>
<!-- Recognized MRR -->
<div class="bg-white dark:bg-darkcard rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<div class="text-sm text-gray-500 dark:text-gray-400 mb-1">Monthly Revenue</div>
<div class="text-2xl font-bold text-green-600 dark:text-green-400">$${metrics.recognizedMrr.toFixed(2)}</div>
</div>
<!-- ARR -->
<div class="bg-white dark:bg-darkcard rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<div class="text-sm text-gray-500 dark:text-gray-400 mb-1">Annual Run Rate</div>
<div class="text-2xl font-bold dark:text-white">$${metrics.arr}</div>
</div>
<!-- At Risk -->
<div class="bg-white dark:bg-darkcard rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<div class="text-sm text-gray-500 dark:text-gray-400 mb-1">At Risk</div>
<div class="text-2xl font-bold text-yellow-600 dark:text-yellow-400">${metrics.atRiskSubs}</div>
<div class="text-xs text-gray-500 dark:text-gray-400">$${metrics.atRiskMrr.toFixed(2)} MRR</div>
</div>
<!-- Lifetime Revenue -->
<div class="bg-white dark:bg-darkcard rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<div class="text-sm text-gray-500 dark:text-gray-400 mb-1">Lifetime Revenue</div>
<div class="text-2xl font-bold text-purple-600 dark:text-purple-400">$${metrics.lifetimeRevenue.toFixed(2)}</div>
<div class="text-xs text-gray-500 dark:text-gray-400">${metrics.lifetimeSubs} Sovereign</div>
</div>
</div>
`;
// Fire vs Frost Path Comparison
bodyContent += `
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<!-- Fire Path -->
<div class="bg-gradient-to-br from-orange-50 to-red-50 dark:from-gray-800 dark:to-gray-800 rounded-lg p-6 border-2 border-orange-500">
<div class="flex items-center mb-4">
<span class="text-3xl mr-3">🔥</span>
<div>
<h3 class="text-xl font-bold text-orange-600 dark:text-orange-400">Fire Path</h3>
<p class="text-sm text-gray-600 dark:text-gray-400">PvP • Competition • Glory</p>
</div>
</div>
<div class="space-y-2">
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">Subscribers:</span>
<span class="font-bold dark:text-white">${paths.fire.subs}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">Monthly Revenue:</span>
<span class="font-bold text-green-600 dark:text-green-400">$${paths.fire.mrr.toFixed(2)}</span>
</div>
</div>
</div>
<div class="bg-white dark:bg-darkcard rounded-lg p-6 border border-gray-200 dark:border-gray-700">
<p class="text-gray-500">Financials module placeholder - data integration pending</p>
<!-- Frost Path -->
<div class="bg-gradient-to-br from-blue-50 to-cyan-50 dark:from-gray-800 dark:to-gray-800 rounded-lg p-6 border-2 border-cyan-500">
<div class="flex items-center mb-4">
<span class="text-3xl mr-3">❄️</span>
<div>
<h3 class="text-xl font-bold text-cyan-600 dark:text-cyan-400">Frost Path</h3>
<p class="text-sm text-gray-600 dark:text-gray-400">Building • Creativity • Chill</p>
</div>
</div>
<div class="space-y-2">
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">Subscribers:</span>
<span class="font-bold dark:text-white">${paths.frost.subs}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">Monthly Revenue:</span>
<span class="font-bold text-green-600 dark:text-green-400">$${paths.frost.mrr.toFixed(2)}</span>
</div>
</div>
</div>
`
}) %>
</div>
`;
// Tier Breakdown Table
bodyContent += `
<div class="bg-white dark:bg-darkcard rounded-lg border border-gray-200 dark:border-gray-700">
<div class="p-6 border-b border-gray-200 dark:border-gray-700">
<h2 class="text-xl font-bold dark:text-white">Tier Performance</h2>
<p class="text-sm text-gray-500 dark:text-gray-400">Subscriber distribution and revenue by tier</p>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Tier</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Path</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Active</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">At Risk</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">MRR</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
`;
// Loop through tiers and add rows
Object.keys(tierBreakdown).sort((a, b) => parseInt(b) - parseInt(a)).forEach(tierLevel => {
const tier = tierBreakdown[tierLevel];
const pathColor = tier.path === 'fire' ? 'text-orange-600 dark:text-orange-400' :
tier.path === 'frost' ? 'text-cyan-600 dark:text-cyan-400' :
'text-purple-600 dark:text-purple-400';
bodyContent += `
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800">
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium dark:text-white">${tier.name}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm font-medium ${pathColor}">${tier.path.charAt(0).toUpperCase() + tier.path.slice(1)}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
<span class="text-sm dark:text-white">${tier.activeCount}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
<span class="text-sm text-yellow-600 dark:text-yellow-400">${tier.graceCount}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
<span class="text-sm font-medium text-green-600 dark:text-green-400">$${tier.totalMrr.toFixed(2)}</span>
</td>
</tr>
`;
});
bodyContent += `
</tbody>
</table>
</div>
</div>
`;
%>
<%- include('../../layout', { body: bodyContent }) %>