krishnasales_mobile/src/screens/AnalyticsPage.tsx
2026-07-20 13:34:47 +05:30

150 lines
5.2 KiB
TypeScript

import { useEffect, useState } from 'react';
import { orderBookingClient } from '../api/clients';
import { ORDER_BOOKING } from '../api/config';
import { Spinner } from '../components/reusable/Spinner';
import { EmptyState } from '../components/reusable/EmptyState';
import { StatsTiles } from '../components/reusable/StatsTiles';
import { TrendingUp } from 'lucide-react';
import {
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip as RechartsTooltip, ResponsiveContainer,
PieChart, Pie, Cell, Legend
} from 'recharts';
interface Tile {
tile_uid: string;
key: string;
value: string | number;
}
interface ChartRow {
dimension: string | null;
value: number;
}
interface ChartData {
chart_uid: string;
key: string;
rows: ChartRow[];
}
const COLORS = ['#6366f1', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#06b6d4', '#f97316'];
export function AnalyticsPage() {
const [tiles, setTiles] = useState<Tile[]>([]);
const [charts, setCharts] = useState<ChartData[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let live = true;
async function run() {
try {
setLoading(true);
const res = await orderBookingClient.recordView(ORDER_BOOKING.recordViews.ANALYTICS, {
limit: 100,
sortBy: 'instance_id',
sortDir: 'desc'
});
if (live) {
setTiles((res.tile_values || []) as Tile[]);
setCharts((res.chart_data || []) as ChartData[]);
}
} catch (e: any) {
if (live) setError(e.message || 'Failed to load analytics');
} finally {
if (live) setLoading(false);
}
}
run();
return () => { live = false; };
}, []);
if (loading) return <div className="h-full flex items-center justify-center"><Spinner label="Loading Analytics..." /></div>;
if (error) return <EmptyState title="Analytics Error" hint={error} />;
const renderChart = (chart: ChartData, index: number) => {
const data = chart.rows.map(r => ({
name: r.dimension || 'Unknown',
value: r.value
}));
const title = chart.key.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
// Decide chart type based on key
if (chart.key === 'status_overview') {
return (
<div key={chart.chart_uid} className="bg-white rounded-2xl shadow-[0_2px_12px_-4px_rgba(0,0,0,0.08)] border border-slate-100 p-5 flex flex-col gap-4">
<div>
<h2 className="text-[15px] font-bold text-slate-800 tracking-tight">{title}</h2>
</div>
<div className="h-64 w-full">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={data}
cx="50%"
cy="50%"
innerRadius={60}
outerRadius={80}
paddingAngle={5}
dataKey="value"
>
{data.map((_, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
))}
</Pie>
<RechartsTooltip
contentStyle={{ borderRadius: '8px', border: 'none', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)' }}
/>
<Legend />
</PieChart>
</ResponsiveContainer>
</div>
</div>
);
}
return (
<div key={chart.chart_uid} className="bg-white rounded-2xl shadow-[0_2px_12px_-4px_rgba(0,0,0,0.08)] border border-slate-100 p-5 flex flex-col gap-4">
<div>
<h2 className="text-[15px] font-bold text-slate-800 tracking-tight">{title}</h2>
</div>
<div className="h-64 w-full">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={data} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#f1f5f9" />
<XAxis dataKey="name" tick={{ fontSize: 12, fill: '#64748b' }} axisLine={false} tickLine={false} />
<YAxis tick={{ fontSize: 12, fill: '#64748b' }} axisLine={false} tickLine={false} />
<RechartsTooltip
cursor={{ fill: '#f8fafc' }}
contentStyle={{ borderRadius: '8px', border: 'none', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)' }}
/>
<Bar dataKey="value" fill={COLORS[index % COLORS.length]} radius={[4, 4, 0, 0]} barSize={40} />
</BarChart>
</ResponsiveContainer>
</div>
</div>
);
};
return (
<div className="flex flex-col gap-6 animate-in fade-in slide-in-from-bottom-2 duration-300">
<div className="flex items-center gap-2 px-1">
<TrendingUp className="text-sunrise-500" size={24} />
<h1 className="text-xl font-extrabold text-slate-800 tracking-tight">Analytics Overview</h1>
</div>
{/* Tiles Grid */}
<StatsTiles tiles={tiles as any} />
{/* Charts Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 pb-4">
{charts.map((chart, index) => renderChart(chart, index))}
</div>
</div>
);
}