import React from "react";
import { fetchStats, StatsResponse } from "../lib/api";
import { Card, CardContent, CardHeader, CardTitle } from "./components/ui";
import Link from "next/link";
export const dynamic = "force-dynamic";
function formatBytes(bytes: number): string {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
}
function formatNumber(n: number): string {
return n.toLocaleString("fr-FR");
}
// Donut chart via SVG
function DonutChart({ data, colors }: { data: { label: string; value: number; color: string }[]; colors?: string[] }) {
const total = data.reduce((sum, d) => sum + d.value, 0);
if (total === 0) return
Aucune donnée
;
const radius = 40;
const circumference = 2 * Math.PI * radius;
let offset = 0;
return (
{data.map((d, i) => {
const pct = d.value / total;
const dashLength = pct * circumference;
const currentOffset = offset;
offset += dashLength;
return (
);
})}
{formatNumber(total)}
{data.map((d, i) => (
{d.label}
{d.value}
))}
);
}
// Bar chart via pure CSS
function BarChart({ data, color = "var(--color-primary)" }: { data: { label: string; value: number }[]; color?: string }) {
const max = Math.max(...data.map((d) => d.value), 1);
if (data.length === 0) return Aucune donnée
;
return (
{data.map((d, i) => (
{d.value || ""}
{d.label}
))}
);
}
// Horizontal progress bar for library breakdown
function HorizontalBar({ label, value, max, subLabel, color = "var(--color-primary)" }: { label: string; value: number; max: number; subLabel?: string; color?: string }) {
const pct = max > 0 ? (value / max) * 100 : 0;
return (
{label}
{subLabel || formatNumber(value)}
);
}
export default async function DashboardPage() {
let stats: StatsResponse | null = null;
try {
stats = await fetchStats();
} catch (e) {
console.error("Failed to fetch stats:", e);
}
if (!stats) {
return (
StripStream Backoffice
Impossible de charger les statistiques. Vérifiez que l'API est en cours d'exécution.
);
}
const { overview, reading_status, by_format, by_language, by_library, top_series, additions_over_time } = stats;
const readingColors = ["hsl(220 13% 70%)", "hsl(45 93% 47%)", "hsl(142 60% 45%)"];
const formatColors = [
"hsl(198 78% 37%)", "hsl(142 60% 45%)", "hsl(45 93% 47%)",
"hsl(2 72% 48%)", "hsl(280 60% 50%)", "hsl(32 80% 50%)",
"hsl(170 60% 45%)", "hsl(220 60% 50%)",
];
const maxLibBooks = Math.max(...by_library.map((l) => l.book_count), 1);
return (
{/* Header */}
Tableau de bord
Aperçu de votre collection de bandes dessinées. Gérez vos bibliothèques, suivez votre progression de lecture et explorez vos livres et séries.
{/* Overview stat cards */}
{/* Charts row */}
{/* Reading status donut */}
Statut de lecture
{/* By format donut */}
Par format
({
label: (f.format || "Inconnu").toUpperCase(),
value: f.count,
color: formatColors[i % formatColors.length],
}))}
/>
{/* By library donut */}
Par bibliothèque
({
label: l.library_name,
value: l.book_count,
color: formatColors[i % formatColors.length],
}))}
/>
{/* Second row */}
{/* Monthly additions bar chart */}
Livres ajoutés (12 derniers mois)
({
label: m.month.slice(5), // "MM" from "YYYY-MM"
value: m.books_added,
}))}
color="hsl(198 78% 37%)"
/>
{/* Top series */}
Séries populaires
{top_series.slice(0, 8).map((s, i) => (
))}
{top_series.length === 0 && (
Aucune série pour le moment
)}
{/* Libraries breakdown */}
{by_library.length > 0 && (
Bibliothèques
{by_library.map((lib, i) => (
{lib.library_name}
{formatBytes(lib.size_bytes)}
{lib.book_count} livres
{lib.read_count} lu
{lib.reading_count} en cours
))}
)}
{/* Quick links */}
);
}
function StatCard({ icon, label, value, color }: { icon: string; label: string; value: string; color: string }) {
const icons: Record = {
book: ,
series: ,
library: ,
pages: ,
author: ,
size: ,
};
const colorClasses: Record = {
primary: "bg-primary/10 text-primary",
success: "bg-success/10 text-success",
warning: "bg-warning/10 text-warning",
};
return (
);
}
function QuickLinks() {
const links = [
{ href: "/libraries", label: "Bibliothèques", bg: "bg-primary/10", text: "text-primary", hoverBg: "group-hover:bg-primary", hoverText: "group-hover:text-primary-foreground", icon: },
{ href: "/books", label: "Livres", bg: "bg-success/10", text: "text-success", hoverBg: "group-hover:bg-success", hoverText: "group-hover:text-white", icon: },
{ href: "/series", label: "Séries", bg: "bg-warning/10", text: "text-warning", hoverBg: "group-hover:bg-warning", hoverText: "group-hover:text-white", icon: },
{ href: "/jobs", label: "Tâches", bg: "bg-destructive/10", text: "text-destructive", hoverBg: "group-hover:bg-destructive", hoverText: "group-hover:text-destructive-foreground", icon: },
];
return (
{links.map((l) => (
{l.icon}
{l.label}
))}
);
}