modul-e / src /pages /Analytics.tsx
sitcod3's picture
Upload src/pages/Analytics.tsx with huggingface_hub
95bd9da verified
Raw History Blame Contribute Delete
11.5 kB
import { useQuery } from "@tanstack/react-query";
import { apiClient } from "../lib/apiClient";
import {
BarChart2,
TrendingUp,
Download,
Calendar,
Mail,
Inbox,
Flame,
ShieldCheck,
Percent,
ArrowUpRight
} from "lucide-react";
import {
ResponsiveContainer,
LineChart,
Line,
BarChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend
} from "recharts";
import { useState } from "react";
import StatCard from "../components/StatCard";
import CsvExportButton from "../components/CsvExportButton";
const timelineData = [
{ date: "Oct 1", sent: 820, opens: 530, replies: 48, bounces: 4 },
{ date: "Oct 5", sent: 1140, opens: 760, replies: 72, bounces: 8 },
{ date: "Oct 10", sent: 1650, opens: 1120, replies: 98, bounces: 11 },
{ date: "Oct 15", sent: 2100, opens: 1450, replies: 134, bounces: 15 },
{ date: "Oct 20", sent: 2800, opens: 1980, replies: 182, bounces: 21 },
{ date: "Oct 25", sent: 3400, opens: 2450, replies: 226, bounces: 28 },
{ date: "Oct 30", sent: 4120, opens: 2980, replies: 280, bounces: 34 },
];
const campaignComparisonData = [
{ name: "SaaS Enterprise VPs", sent: 4200, opens: 2600, replies: 240 },
{ name: "Series A Tech Founders", sent: 3100, opens: 2100, replies: 195 },
{ name: "Agency Owners Q4", sent: 2400, opens: 1400, replies: 120 },
{ name: "E-commerce Directors", sent: 1800, opens: 980, replies: 84 },
{ name: "RevOps Decision Makers", sent: 1200, opens: 720, replies: 65 },
];
export default function Analytics() {
const [dateRange, setDateRange] = useState("30d");
const { data: analytics } = useQuery({
queryKey: ["analytics-campaigns"],
queryFn: async () => {
const res = await apiClient.get("/analytics/campaigns");
return res.data;
},
});
const { data: emailAccounts = [] } = useQuery({
queryKey: ["email-accounts"],
queryFn: async () => {
const res = await apiClient.get("/email-accounts");
return res.data;
},
});
return (
<div className="p-6 max-w-7xl mx-auto space-y-6">
{/* Header */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<h1 className="text-2xl font-bold tracking-tight text-zinc-100 flex items-center gap-2.5">
<BarChart2 className="w-6 h-6 text-indigo-400" />
Outbound Analytics & Intelligence
</h1>
<p className="text-zinc-400 text-sm mt-1">
Global deliverability performance, engagement conversion funnels, and mailbox health telemetry.
</p>
</div>
<div className="flex items-center gap-3">
<select
value={dateRange}
onChange={(e) => setDateRange(e.target.value)}
className="bg-zinc-900 border border-zinc-800 rounded-lg py-2 px-3 text-xs text-zinc-200 focus:outline-none focus:border-indigo-500"
>
<option value="7d">Last 7 Days</option>
<option value="14d">Last 14 Days</option>
<option value="30d">Last 30 Days</option>
<option value="90d">Last 90 Days</option>
</select>
<CsvExportButton data={timelineData} filename="smartlead-analytics-export.csv" />
</div>
</div>
{/* KPI Conversion Cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<StatCard
title="Open Rate"
value="64.8%"
subtitle="4,890 unique opens"
icon={Mail}
iconColor="emerald"
change={{ value: "+3.4%", isPositive: true, period: "vs benchmark" }}
/>
<StatCard
title="Click Rate (CTR)"
value="18.2%"
subtitle="1,120 link clicks"
icon={ArrowUpRight}
iconColor="blue"
change={{ value: "+1.1%", isPositive: true, period: "last 30 days" }}
/>
<StatCard
title="Reply Rate"
value="6.2%"
subtitle="378 prospect responses"
icon={Inbox}
iconColor="purple"
change={{ value: "+0.8%", isPositive: true, period: "positive intent" }}
/>
<StatCard
title="Bounce Rate"
value="0.7%"
subtitle="Deliverability safe"
icon={ShieldCheck}
iconColor="teal"
change={{ value: "-0.2%", isPositive: true, period: "health optimal" }}
/>
</div>
{/* Timeline Chart */}
<div className="bg-white/5 border border-white/10 backdrop-blur-md rounded-xl p-5 shadow-[0_8px_32px_-8px_rgba(6,10,12,0.65)]">
<div className="flex items-center justify-between mb-6">
<div>
<h3 className="text-base font-semibold text-zinc-100">Cumulative Volume & Responses</h3>
<p className="text-xs text-zinc-400 mt-0.5">
Daily trajectory of outbound sends, unique opens, and replied prospects.
</p>
</div>
</div>
<div className="h-[320px] w-full">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={timelineData} margin={{ top: 5, right: 20, bottom: 5, left: 0 }}>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(246,248,242,0.05)" vertical={false} />
<XAxis dataKey="date" stroke="var(--nt-text-muted)" fontSize={12} tickLine={false} axisLine={false} />
<YAxis stroke="var(--nt-text-muted)" fontSize={12} tickLine={false} axisLine={false} tickFormatter={(val) => new Intl.NumberFormat('en-US').format(val)} className="font-mono" />
<Tooltip
contentStyle={{
backgroundColor: "rgba(6, 10, 12, 0.88)",
backdropFilter: "blur(12px)",
borderColor: "rgba(37,125,104,0.2)",
borderRadius: "12px",
fontSize: "12px",
color: "var(--nt-text-primary)"
}}
/>
<Legend wrapperStyle={{ fontSize: "12px", paddingTop: "10px" }} />
<Line type="monotone" dataKey="sent" name="Emails Sent" stroke="var(--nt-deep-teal-600)" strokeWidth={3} dot={{ r: 4, fill: 'var(--nt-deep-teal-600)', strokeWidth: 2, stroke: 'var(--nt-focus-ring-offset)' }} activeDot={{ r: 6, fill: 'var(--nt-deep-teal-700)' }} animationDuration={1500} />
<Line type="monotone" dataKey="opens" name="Opens" stroke="var(--nt-deep-teal-500)" strokeWidth={3} dot={{ r: 4, fill: 'var(--nt-deep-teal-500)', strokeWidth: 2, stroke: 'var(--nt-focus-ring-offset)' }} activeDot={{ r: 6, fill: 'var(--nt-deep-teal-700)' }} animationDuration={1500} />
<Line type="monotone" dataKey="replies" name="Replies" stroke="var(--nt-terracotta-500)" strokeWidth={3} dot={{ r: 4, fill: 'var(--nt-terracotta-500)', strokeWidth: 2, stroke: 'var(--nt-focus-ring-offset)' }} activeDot={{ r: 6, fill: 'var(--nt-terracotta-700)' }} animationDuration={1500} />
</LineChart>
</ResponsiveContainer>
</div>
</div>
{/* Comparison Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Top Campaigns Comparison */}
<div className="bg-white/5 border border-white/10 backdrop-blur-md rounded-xl p-5 shadow-[0_8px_32px_-8px_rgba(6,10,12,0.65)]">
<h3 className="text-base font-semibold text-zinc-100 mb-1">Top Campaign Performance</h3>
<p className="text-xs text-zinc-400 mb-4">Volume dispatched vs. engaged prospects.</p>
<div className="h-[280px] w-full">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={campaignComparisonData} layout="vertical" margin={{ top: 5, right: 20, bottom: 5, left: 40 }}>
<defs>
<linearGradient id="colorOpens" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stopColor="var(--nt-deep-teal-500)" stopOpacity={0.8}/>
<stop offset="100%" stopColor="var(--nt-deep-teal-700)" stopOpacity={1}/>
</linearGradient>
<linearGradient id="colorReplies" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stopColor="var(--nt-terracotta-400)" stopOpacity={0.8}/>
<stop offset="100%" stopColor="var(--nt-terracotta-700)" stopOpacity={1}/>
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(246,248,242,0.05)" horizontal={false} />
<XAxis type="number" stroke="var(--nt-text-muted)" fontSize={11} tickLine={false} axisLine={false} className="font-mono" />
<YAxis dataKey="name" type="category" stroke="var(--nt-text-muted)" fontSize={11} tickLine={false} axisLine={false} />
<Tooltip
contentStyle={{
backgroundColor: "rgba(6, 10, 12, 0.88)",
backdropFilter: "blur(12px)",
borderColor: "rgba(37,125,104,0.2)",
borderRadius: "12px",
fontSize: "12px",
}}
/>
<Bar dataKey="opens" name="Opens" fill="url(#colorOpens)" radius={[0, 4, 4, 0]} animationDuration={1500}>
</Bar>
<Bar dataKey="replies" name="Replies" fill="url(#colorReplies)" radius={[0, 4, 4, 0]} animationDuration={1500} />
</BarChart>
</ResponsiveContainer>
</div>
</div>
{/* Mailbox Deliverability Health Matrix */}
<div className="bg-white/5 border border-white/10 backdrop-blur-md rounded-xl p-5 shadow-[0_8px_32px_-8px_rgba(6,10,12,0.65)] flex flex-col justify-between">
<div>
<h3 className="text-base font-semibold text-zinc-100 mb-1">Mailbox Deliverability Matrix</h3>
<p className="text-xs text-zinc-400 mb-4">DNS authentication, warmup status, and health metrics.</p>
<div className="space-y-3">
{emailAccounts.map((acc: any) => (
<div
key={acc.id}
className="flex items-center justify-between p-3 bg-white/5 border border-white/10 rounded-lg text-xs hover:bg-white/10 transition-colors"
>
<div>
<span className="font-semibold text-zinc-200">{acc.from_email}</span>
<div className="text-[10px] text-zinc-400 mt-0.5">
SPF: PASS • DKIM: PASS • DMARC: PASS
</div>
</div>
<div className="flex items-center gap-3 font-mono">
<span className="text-emerald-400 font-bold drop-shadow-[0_0_8px_rgba(63,156,132,0.5)]">99.4%</span>
<span className="px-2 py-0.5 bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 rounded text-[10px]">
Optimal
</span>
</div>
</div>
))}
</div>
</div>
<div className="mt-4 pt-3 border-t border-white/10 text-[11px] text-zinc-400 flex items-center justify-between">
<span>Global Reputation: <span className="font-mono text-zinc-300">98.4 / 100</span></span>
<span className="text-emerald-400 flex items-center gap-1.5"><ShieldCheck className="w-3.5 h-3.5" /> All Nodes Secure</span>
</div>
</div>
</div>
</div>
);
}