tahamajs/IE / Projects /front /src /components /analytics /AnalyticsView.tsx
tahamajs's picture
download
raw
6.88 kB
// src/components/analytics/AnalyticsView.tsx
import { useState } from "react";
import { Link } from "react-router-dom";
import {
LineChart, Line, AreaChart, Area, BarChart, Bar,
XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer,
PieChart, Pie, Cell
} from "recharts";
import { motion } from "framer-motion";
import { AnalyticsSummary, AnalyticsDataPoint, ArticleAnalytics } from "@/hooks/useAnalytics";
import { Calendar, TrendingUp, BookOpen, Users, ArrowUp } from "lucide-react";
interface AnalyticsViewProps {
data: AnalyticsSummary | null;
onRefresh: () => void;
}
const COLORS = ["#6366f1", "#ec4899", "#f59e0b", "#10b981"];
export function AnalyticsView({ data, onRefresh }: AnalyticsViewProps) {
const [chartType, setChartType] = useState<"line" | "area" | "bar">("line");
if (!data) return null;
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
className="space-y-8"
>
{/* Period selector (if needed) – we can leave it outside the component, but here it's fixed */}
<div className="flex justify-end items-center gap-2 text-sm">
<span className="text-gray-500">Chart type:</span>
<button
onClick={() => setChartType("line")}
className={`px-3 py-1 rounded-full ${chartType === "line" ? "bg-primary text-white" : "bg-gray-100 dark:bg-gray-800"}`}
>
Line
</button>
<button
onClick={() => setChartType("area")}
className={`px-3 py-1 rounded-full ${chartType === "area" ? "bg-primary text-white" : "bg-gray-100 dark:bg-gray-800"}`}
>
Area
</button>
<button
onClick={() => setChartType("bar")}
className={`px-3 py-1 rounded-full ${chartType === "bar" ? "bg-primary text-white" : "bg-gray-100 dark:bg-gray-800"}`}
>
Bar
</button>
</div>
{/* Daily trends chart */}
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow p-5">
<h3 className="font-semibold text-lg mb-4 flex items-center gap-2">
<TrendingUp className="w-5 h-5 text-primary" />
Daily engagement
</h3>
<div className="h-80">
<ResponsiveContainer width="100%" height="100%">
{chartType === "line" && (
<LineChart data={data.dailyData}>
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
<XAxis dataKey="date" stroke="#9ca3af" />
<YAxis stroke="#9ca3af" />
<Tooltip contentStyle={{ backgroundColor: "#1f2937", border: "none", borderRadius: "8px" }} />
<Legend />
<Line type="monotone" dataKey="views" stroke="#6366f1" name="Views" />
<Line type="monotone" dataKey="likes" stroke="#ec4899" name="Likes" />
<Line type="monotone" dataKey="comments" stroke="#10b981" name="Comments" />
</LineChart>
)}
{chartType === "area" && (
<AreaChart data={data.dailyData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Legend />
<Area type="monotone" dataKey="views" stackId="1" stroke="#6366f1" fill="#6366f1" fillOpacity={0.3} />
<Area type="monotone" dataKey="likes" stackId="2" stroke="#ec4899" fill="#ec4899" fillOpacity={0.3} />
<Area type="monotone" dataKey="comments" stackId="3" stroke="#10b981" fill="#10b981" fillOpacity={0.3} />
</AreaChart>
)}
{chartType === "bar" && (
<BarChart data={data.dailyData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="views" fill="#6366f1" />
<Bar dataKey="likes" fill="#ec4899" />
<Bar dataKey="comments" fill="#10b981" />
</BarChart>
)}
</ResponsiveContainer>
</div>
</div>
{/* Top performing articles */}
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow p-5">
<h3 className="font-semibold text-lg mb-4 flex items-center gap-2">
<BookOpen className="w-5 h-5 text-primary" />
Top performing articles
</h3>
{data.topPerforming.length === 0 ? (
<p className="text-gray-500 text-sm">No data yet. Publish articles to see stats.</p>
) : (
<div className="space-y-3">
{data.topPerforming.map((article, idx) => (
<Link
key={article.articleId}
to={`/articles/${article.articleId}`}
className="block p-3 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-700/50 transition"
>
<div className="flex flex-wrap justify-between items-start gap-2">
<div className="flex-1 min-w-0">
<div className="font-medium truncate">{article.title}</div>
<div className="text-xs text-gray-500 mt-1">
{article.views} views · {article.likes} likes · {article.comments} comments
</div>
</div>
{idx === 0 && <span className="text-yellow-500 text-xs font-bold">🏆 Top</span>}
</div>
</Link>
))}
</div>
)}
</div>
{/* Engagement distribution (pie) */}
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow p-5">
<h3 className="font-semibold text-lg mb-4 flex items-center gap-2">
<Users className="w-5 h-5 text-primary" />
Engagement breakdown
</h3>
<div className="h-64">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={[
{ name: "Views", value: data.totalViews },
{ name: "Likes", value: data.totalLikes },
{ name: "Comments", value: data.totalComments },
]}
cx="50%"
cy="50%"
labelLine={false}
label={({ name, percent }) => `${name}: ${((percent ?? 0) * 100).toFixed(0)}%`}
outerRadius={80}
fill="#8884d8"
dataKey="value"
>
{COLORS.map((color, i) => (
<Cell key={`cell-${i}`} fill={color} />
))}
</Pie>
<Tooltip />
</PieChart>
</ResponsiveContainer>
</div>
</div>
</motion.div>
);
}

Xet Storage Details

Size:
6.88 kB
·
Xet hash:
96f621cbeb79a446d05d6ee028fffe007c4d46f29e9402026540be35c37e7331

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.