324 lines
19 KiB
JavaScript

import React, { useState } from 'react';
import { Head } from '@inertiajs/react';
import AdminLayout from '@/Layouts/AdminLayout';
import {
BarChart3,
TrendingUp,
Users,
CreditCard,
Percent,
Award,
Sparkles,
Calendar,
Download
} from 'lucide-react';
import { Badge } from '@/components/ui/badge';
export default function AnalyticsHub({ user_growth, revenue_breakdown, retention_rates, hiring_funnel }) {
const [selectedTab, setSelectedTab] = useState('Growth');
// Helper coordinates calculation for Cohort growth Area Chart (450w, 200h)
const getCoordinatesForArea = (key, maxVal) => {
const points = [];
const paddingLeft = 40;
const paddingRight = 15;
const paddingTop = 20;
const paddingBottom = 25;
const graphHeight = 200 - paddingTop - paddingBottom;
const graphWidth = 450 - paddingLeft - paddingRight;
user_growth.forEach((d, idx) => {
const val = d[key];
const x = paddingLeft + (idx / (user_growth.length - 1)) * graphWidth;
const y = paddingTop + graphHeight - (val / maxVal) * graphHeight;
points.push(`${x},${y}`);
});
// Add bottom-right and bottom-left to close the area shape
const startX = paddingLeft;
const endX = paddingLeft + graphWidth;
const bottomY = paddingTop + graphHeight;
return `${startX},${bottomY} ${points.join(' ')} ${endX},${bottomY}`;
};
const getLineCoordinates = (key, maxVal) => {
const points = [];
const paddingLeft = 40;
const paddingRight = 15;
const paddingTop = 20;
const paddingBottom = 25;
const graphHeight = 200 - paddingTop - paddingBottom;
const graphWidth = 450 - paddingLeft - paddingRight;
user_growth.forEach((d, idx) => {
const val = d[key];
const x = paddingLeft + (idx / (user_growth.length - 1)) * graphWidth;
const y = paddingTop + graphHeight - (val / maxVal) * graphHeight;
points.push(`${x},${y}`);
});
return points.join(' ');
};
return (
<AdminLayout title="Reports & Analytics Hub">
<Head title="System Analytics" />
<div className="font-sans max-w-7xl mx-auto space-y-8">
{/* Title and date picker */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h1 className="text-xl font-bold text-gray-900 tracking-tight">Interactive Platform Reports & Analytics Hub</h1>
<p className="text-xs text-gray-500 mt-0.5">Explore subscription cohort retention, job conversions ratios, availability trends, and monthly billing splits.</p>
</div>
<div className="flex items-center gap-3">
<div className="flex bg-slate-200/60 p-1 rounded-xl w-fit border border-slate-200">
{['Growth', 'Revenue', 'Funnel'].map((tab) => (
<button
key={tab}
type="button"
onClick={() => setSelectedTab(tab)}
className={`px-4 py-1.5 rounded-lg text-[10px] font-black uppercase tracking-wider transition-all ${
selectedTab === tab
? 'bg-white text-[#0F6E56] shadow-sm'
: 'text-slate-600 hover:text-slate-900'
}`}
>
{tab} Report
</button>
))}
</div>
<button className="inline-flex items-center px-4 py-2.5 bg-slate-950 text-white rounded-xl text-xs font-bold hover:bg-slate-900 transition-colors shadow-sm space-x-2 uppercase tracking-wider">
<Download className="w-4 h-4" />
<span>Export PDF</span>
</button>
</div>
</div>
{/* Analytical Charts grid depending on tabs */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Growth cohort Report */}
{selectedTab === 'Growth' && (
<div className="bg-white border border-slate-200 rounded-3xl p-6 shadow-sm space-y-6">
<div>
<h3 className="text-sm font-black text-slate-800 uppercase tracking-widest flex items-center gap-1.5">
<TrendingUp className="w-4.5 h-4.5 text-[#0F6E56]" />
<span>User Cohort growth curves</span>
</h3>
<p className="text-[10px] text-slate-400 mt-0.5 uppercase tracking-wider font-bold">Employer and Worker registry trends (6 Months)</p>
</div>
<div className="relative">
<svg className="w-full h-56" viewBox="0 0 450 200">
{/* Grids */}
<line x1="40" y1="20" x2="435" y2="20" stroke="#f8fafc" strokeWidth="1.5" />
<line x1="40" y1="75" x2="435" y2="75" stroke="#f8fafc" strokeWidth="1.5" />
<line x1="40" y1="130" x2="435" y2="130" stroke="#f8fafc" strokeWidth="1.5" />
<line x1="40" y1="180" x2="435" y2="180" stroke="#cbd5e1" strokeWidth="1.5" />
{/* Worker Area fill */}
<polygon
fill="url(#workerGrad)"
points={getCoordinatesForArea('workers', 1500)}
className="opacity-45"
/>
{/* Worker Line */}
<polyline
fill="none"
stroke="#10b981"
strokeWidth="4"
points={getLineCoordinates('workers', 1500)}
/>
{/* Employer Line */}
<polyline
fill="none"
stroke="#3b82f6"
strokeWidth="3.5"
strokeDasharray="4 4"
points={getLineCoordinates('employers', 1500)}
/>
{/* Gradients */}
<defs>
<linearGradient id="workerGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#10b981" />
<stop offset="100%" stopColor="#ffffff" />
</linearGradient>
</defs>
{/* X-Axis Month labels */}
{user_growth.map((d, i) => {
const x = 40 + (i / 5) * 380;
return (
<text key={i} x={x} y="195" textAnchor="middle" className="text-[9px] font-bold fill-slate-400 font-mono">
{d.month}
</text>
);
})}
{/* Y Labels */}
<text x="30" y="24" textAnchor="end" className="text-[9px] font-black fill-slate-300">1.5K</text>
<text x="30" y="80" textAnchor="end" className="text-[9px] font-black fill-slate-300">750</text>
<text x="30" y="135" textAnchor="end" className="text-[9px] font-black fill-slate-300">300</text>
<text x="30" y="185" textAnchor="end" className="text-[9px] font-black fill-slate-300">0</text>
</svg>
<div className="flex items-center justify-center space-x-6 mt-4">
<div className="flex items-center space-x-2 text-xs font-bold text-slate-600">
<div className="w-3.5 h-3.5 bg-emerald-500 rounded" />
<span>Workers ({user_growth[5].workers})</span>
</div>
<div className="flex items-center space-x-2 text-xs font-bold text-slate-600">
<div className="w-3.5 h-1 border-t-2 border-dashed border-blue-500" />
<span>Employers ({user_growth[5].employers})</span>
</div>
</div>
</div>
</div>
)}
{/* Revenue reports */}
{selectedTab === 'Revenue' && (
<div className="bg-white border border-slate-200 rounded-3xl p-6 shadow-sm space-y-6">
<div>
<h3 className="text-sm font-black text-slate-800 uppercase tracking-widest flex items-center gap-1.5">
<CreditCard className="w-4.5 h-4.5 text-[#0F6E56]" />
<span>Stacked Subscription revenue splits</span>
</h3>
<p className="text-[10px] text-slate-400 mt-0.5 uppercase tracking-wider font-bold">Monthly billing splits (AED)</p>
</div>
<div className="relative">
<svg className="w-full h-56" viewBox="0 0 450 200">
<line x1="40" y1="180" x2="435" y2="180" stroke="#cbd5e1" strokeWidth="1.5" />
{/* Stacked bars for each month */}
{revenue_breakdown.map((d, i) => {
const x = 50 + i * 62;
const total = d.basic + d.premium + d.vip;
const maxVal = 80000;
const scale = 150; // Graph height scale factor
const basicH = (d.basic / maxVal) * scale;
const premiumH = (d.premium / maxVal) * scale;
const vipH = (d.vip / maxVal) * scale;
const basicY = 180 - basicH;
const premiumY = basicY - premiumH;
const vipY = premiumY - vipH;
return (
<g key={i} className="cursor-pointer group">
{/* Basic */}
<rect x={x} y={basicY} width="22" height={basicH} fill="#94a3b8" rx="2" />
{/* Premium */}
<rect x={x} y={premiumY} width="22" height={premiumH} fill="#3b82f6" />
{/* VIP */}
<rect x={x} y={vipY} width="22" height={vipH} fill="#10b981" rx="2" />
<text x={x + 11} y="195" textAnchor="middle" className="text-[9px] font-bold fill-slate-400 font-mono">
{d.month.split(' ')[0]}
</text>
</g>
);
})}
<text x="30" y="30" textAnchor="end" className="text-[9px] font-black fill-slate-300">80K</text>
<text x="30" y="105" textAnchor="end" className="text-[9px] font-black fill-slate-300">40K</text>
<text x="30" y="180" textAnchor="end" className="text-[9px] font-black fill-slate-300">0</text>
</svg>
<div className="flex items-center justify-center space-x-6 mt-4">
<div className="flex items-center space-x-2 text-xs font-bold text-slate-600">
<div className="w-3 h-3 bg-slate-400 rounded-sm" />
<span>Basic</span>
</div>
<div className="flex items-center space-x-2 text-xs font-bold text-slate-600">
<div className="w-3 h-3 bg-blue-500 rounded-sm" />
<span>Premium</span>
</div>
<div className="flex items-center space-x-2 text-xs font-bold text-slate-600">
<div className="w-3 h-3 bg-emerald-500 rounded-sm" />
<span>VIP</span>
</div>
</div>
</div>
</div>
)}
{/* Funnel Conversions */}
{selectedTab === 'Funnel' && (
<div className="bg-white border border-slate-200 rounded-3xl p-6 shadow-sm space-y-6">
<div>
<h3 className="text-sm font-black text-slate-800 uppercase tracking-widest flex items-center gap-1.5">
<Percent className="w-4.5 h-4.5 text-[#0F6E56]" />
<span>Hiring Placement Funnel ratios</span>
</h3>
<p className="text-[10px] text-slate-400 mt-0.5 uppercase tracking-wider font-bold">Hiring Conversion stage details (This month)</p>
</div>
<div className="space-y-4">
{hiring_funnel.map((item, idx) => {
const maxVal = hiring_funnel[0].count;
const pct = ((item.count / maxVal) * 100).toFixed(1);
return (
<div key={idx} className="space-y-2 text-xs font-bold text-slate-700">
<div className="flex items-center justify-between">
<span>{item.stage}</span>
<span>{item.count.toLocaleString()} ({pct}%)</span>
</div>
<div className="w-full bg-slate-100 rounded-full h-3.5 overflow-hidden">
<div
className="h-full rounded-full bg-gradient-to-r from-teal-600 to-teal-500 transition-all duration-500"
style={{ width: `${pct}%` }}
/>
</div>
</div>
);
})}
</div>
</div>
)}
{/* Right side cohort Retention Analysis: Month-over-Month curves */}
<div className="bg-white border border-slate-200 rounded-3xl p-6 shadow-sm flex flex-col justify-between">
<div>
<h3 className="text-sm font-black text-slate-800 uppercase tracking-widest flex items-center gap-1.5">
<Award className="w-4.5 h-4.5 text-[#0F6E56]" />
<span>Subscription Cohort Retention rates</span>
</h3>
<p className="text-[10px] text-slate-400 mt-0.5 uppercase tracking-wider font-bold">Month 1 to Month 6 user retention metrics</p>
</div>
<div className="space-y-4 my-6">
{retention_rates.map((cohort, i) => (
<div key={i} className="flex items-center justify-between text-xs font-bold text-slate-700">
<span className="w-20">{cohort.cohort}</span>
<div className="flex-1 mx-4 bg-slate-100 rounded-full h-3 overflow-hidden">
<div
className="h-full bg-gradient-to-r from-blue-600 to-blue-500 rounded-full"
style={{ width: `${cohort.rate}%` }}
/>
</div>
<span className="text-blue-600 w-10 text-right">{cohort.rate}%</span>
</div>
))}
</div>
<div className="bg-emerald-50/50 p-4 border border-emerald-100 rounded-2xl text-[11px] font-bold text-teal-800 leading-relaxed shadow-sm">
<Sparkles className="w-4 h-4 text-emerald-600 mb-1" />
<span>System Insights: Employer VIP and Premium tier subscribers yield a 79% retention rate beyond Month 3, validating platform organic vetting value.</span>
</div>
</div>
</div>
</div>
</AdminLayout>
);
}