107 lines
3.8 KiB
JavaScript
107 lines
3.8 KiB
JavaScript
import React, { useMemo } from 'react';
|
|
import {
|
|
ResponsiveContainer,
|
|
LineChart,
|
|
Line,
|
|
XAxis,
|
|
YAxis,
|
|
CartesianGrid,
|
|
Tooltip,
|
|
BarChart,
|
|
Bar,
|
|
PieChart,
|
|
Pie,
|
|
Cell,
|
|
Legend,
|
|
} from 'recharts';
|
|
|
|
const PIE_COLORS = ['#0A39DF', '#059669', '#333F48', '#D97706', '#DC2626', '#0891B2', '#DB2777'];
|
|
|
|
function last7Days() {
|
|
const days = [];
|
|
for (let i = 6; i >= 0; i--) {
|
|
const d = new Date();
|
|
d.setDate(d.getDate() - i);
|
|
days.push(d.toISOString().split('T')[0]);
|
|
}
|
|
return days;
|
|
}
|
|
|
|
export default function ActivityCharts({ activities }) {
|
|
const timeSeries = useMemo(() => {
|
|
const days = last7Days();
|
|
return days.map((day) => {
|
|
const dayActivities = activities.filter((a) => (a.created_date || '').slice(0, 10) === day);
|
|
return {
|
|
date: day.slice(5),
|
|
signups: dayActivities.filter((a) => a.event_type === 'signup').length,
|
|
logins: dayActivities.filter((a) => a.event_type === 'login').length,
|
|
actions: dayActivities.length,
|
|
};
|
|
});
|
|
}, [activities]);
|
|
|
|
const byType = useMemo(() => {
|
|
const counts = {};
|
|
activities.forEach((a) => {
|
|
counts[a.event_type] = (counts[a.event_type] || 0) + 1;
|
|
});
|
|
return Object.entries(counts).map(([name, value]) => ({ name, value }));
|
|
}, [activities]);
|
|
|
|
const byRole = useMemo(() => {
|
|
const counts = {};
|
|
activities.forEach((a) => {
|
|
counts[a.account_type] = (counts[a.account_type] || 0) + 1;
|
|
});
|
|
return Object.entries(counts).map(([name, value]) => ({ name, value }));
|
|
}, [activities]);
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
<div className="bg-white rounded-xl border border-[#E5E7EB] p-5">
|
|
<h3 className="text-[14px] font-semibold text-[#111827] mb-4">Activity (Last 7 Days)</h3>
|
|
<ResponsiveContainer width="100%" height={240}>
|
|
<LineChart data={timeSeries}>
|
|
<CartesianGrid strokeDasharray="3 3" stroke="#F3F4F6" />
|
|
<XAxis dataKey="date" tick={{ fontSize: 11 }} stroke="#9CA3AF" />
|
|
<YAxis tick={{ fontSize: 11 }} stroke="#9CA3AF" allowDecimals={false} />
|
|
<Tooltip />
|
|
<Legend wrapperStyle={{ fontSize: 12 }} />
|
|
<Line type="monotone" dataKey="signups" stroke="#0A39DF" strokeWidth={2} />
|
|
<Line type="monotone" dataKey="logins" stroke="#059669" strokeWidth={2} />
|
|
<Line type="monotone" dataKey="actions" stroke="#D97706" strokeWidth={2} />
|
|
</LineChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
|
|
<div className="bg-white rounded-xl border border-[#E5E7EB] p-5">
|
|
<h3 className="text-[14px] font-semibold text-[#111827] mb-4">Actions by Type</h3>
|
|
<ResponsiveContainer width="100%" height={240}>
|
|
<BarChart data={byType}>
|
|
<CartesianGrid strokeDasharray="3 3" stroke="#F3F4F6" />
|
|
<XAxis dataKey="name" tick={{ fontSize: 10 }} stroke="#9CA3AF" interval={0} angle={-20} textAnchor="end" height={50} />
|
|
<YAxis tick={{ fontSize: 11 }} stroke="#9CA3AF" allowDecimals={false} />
|
|
<Tooltip />
|
|
<Bar dataKey="value" fill="#0A39DF" radius={[4, 4, 0, 0]} />
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
|
|
<div className="bg-white rounded-xl border border-[#E5E7EB] p-5">
|
|
<h3 className="text-[14px] font-semibold text-[#111827] mb-4">Users by Role</h3>
|
|
<ResponsiveContainer width="100%" height={240}>
|
|
<PieChart>
|
|
<Pie data={byRole} dataKey="value" nameKey="name" cx="50%" cy="50%" outerRadius={80} label>
|
|
{byRole.map((_, i) => (
|
|
<Cell key={i} fill={PIE_COLORS[i % PIE_COLORS.length]} />
|
|
))}
|
|
</Pie>
|
|
<Tooltip />
|
|
<Legend wrapperStyle={{ fontSize: 12 }} />
|
|
</PieChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |