"use client"; import { useMemo } from "react"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Progress } from "@/components/ui/progress"; import { Skeleton } from "@/components/ui/skeleton"; import { useTranslation } from "@/lib/i18n/client"; import { api } from "@/lib/trpc"; import { Archive, BarChart3, BookOpen, Clock, Database, FileText, Globe, Hash, Heart, Highlighter, Image, Link, List, TrendingUp, } from "lucide-react"; function formatBytes(bytes: number): string { if (bytes === 0) return "0 Bytes"; const k = 1024; const sizes = ["Bytes", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; } function formatNumber(num: number): string { if (num >= 1000000) { return (num / 1000000).toFixed(1) + "M"; } if (num >= 1000) { return (num / 1000).toFixed(1) + "K"; } return num.toString(); } const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; const hourLabels = Array.from({ length: 24 }, (_, i) => i === 0 ? "12 AM" : i < 12 ? `${i} AM` : i === 12 ? "12 PM" : `${i - 12} PM`, ); function SimpleBarChart({ data, maxValue, labels, }: { data: number[]; maxValue: number; labels: string[]; }) { return (
{data.map((value, index) => (
{labels[index]}
0 ? (value / maxValue) * 100 : 0}%`, }} />
{value}
))}
); } function StatCard({ title, value, icon, description, }: { title: string; value: string | number; icon: React.ReactNode; description?: string; }) { return ( {title} {icon}
{value}
{description && (

{description}

)}
); } export default function StatsPage() { const { t } = useTranslation(); const { data: stats, isLoading } = api.users.stats.useQuery(); const { data: userSettings } = api.users.settings.useQuery(); const maxHourlyActivity = useMemo(() => { if (!stats) return 0; return Math.max( ...stats.bookmarkingActivity.byHour.map( (h: { hour: number; count: number }) => h.count, ), ); }, [stats]); const maxDailyActivity = useMemo(() => { if (!stats) return 0; return Math.max( ...stats.bookmarkingActivity.byDayOfWeek.map( (d: { day: number; count: number }) => d.count, ), ); }, [stats]); if (isLoading) { return (

{t("settings.stats.usage_statistics")}

{t("settings.stats.insights_description")}

{Array.from({ length: 8 }).map((_, i) => ( ))}
); } if (!stats) { return (

{t("settings.stats.failed_to_load")}

); } return (

{t("settings.stats.usage_statistics")}

Insights into your bookmarking habits and collection {userSettings?.timezone && userSettings.timezone !== "UTC" && ( Times shown in {userSettings.timezone} timezone )}

{/* Overview Stats */}
} description={t("settings.stats.overview.all_saved_items")} /> } description={t("settings.stats.overview.starred_bookmarks")} /> } description={t("settings.stats.overview.archived_items")} /> } description={t("settings.stats.overview.unique_tags_created")} /> } description={t("settings.stats.overview.bookmark_collections")} /> } description={t("settings.stats.overview.text_highlights")} /> } description={t("settings.stats.overview.total_asset_storage")} /> } description={t("settings.stats.overview.bookmarks_added")} />
{/* Bookmark Types */} {t("settings.stats.bookmark_types.title")}
{t("settings.stats.bookmark_types.links")}
{stats.bookmarksByType.link}
0 ? (stats.bookmarksByType.link / stats.numBookmarks) * 100 : 0 } className="h-2" />
{t("settings.stats.bookmark_types.text_notes")}
{stats.bookmarksByType.text}
0 ? (stats.bookmarksByType.text / stats.numBookmarks) * 100 : 0 } className="h-2" />
{t("settings.stats.bookmark_types.assets")}
{stats.bookmarksByType.asset}
0 ? (stats.bookmarksByType.asset / stats.numBookmarks) * 100 : 0 } className="h-2" />
{/* Recent Activity */} {t("settings.stats.recent_activity.title")}
{stats.bookmarkingActivity.thisWeek}
{t("settings.stats.recent_activity.this_week")}
{stats.bookmarkingActivity.thisMonth}
{t("settings.stats.recent_activity.this_month")}
{stats.bookmarkingActivity.thisYear}
{t("settings.stats.recent_activity.this_year")}
{/* Top Domains */} {t("settings.stats.top_domains.title")} {stats.topDomains.length > 0 ? (
{stats.topDomains .slice(0, 8) .map( ( domain: { domain: string; count: number }, index: number, ) => (
{index + 1}
{domain.domain}
{domain.count}
), )}
) : (

{t("settings.stats.top_domains.no_domains_found")}

)}
{/* Top Tags */} {t("settings.stats.most_used_tags.title")} {stats.tagUsage.length > 0 ? (
{stats.tagUsage .slice(0, 8) .map( (tag: { name: string; count: number }, index: number) => (
{index + 1}
{tag.name}
{tag.count}
), )}
) : (

{t("settings.stats.most_used_tags.no_tags_found")}

)}
{/* Activity Patterns */}
{/* Hourly Activity */} {t("settings.stats.activity_patterns.activity_by_hour")} {userSettings?.timezone && userSettings.timezone !== "UTC" && ( ({userSettings.timezone}) )} h.count, )} maxValue={maxHourlyActivity} labels={hourLabels} /> {/* Daily Activity */} {t("settings.stats.activity_patterns.activity_by_day")} {userSettings?.timezone && userSettings.timezone !== "UTC" && ( ({userSettings.timezone}) )} d.count, )} maxValue={maxDailyActivity} labels={dayNames} />
{/* Asset Storage */} {stats.assetsByType.length > 0 && ( {t("settings.stats.storage_breakdown.title")}
{stats.assetsByType.map( (asset: { type: string; count: number; totalSize: number }) => (
{asset.type.replace(/([A-Z])/g, " $1").trim()} {asset.count}
{formatBytes(asset.totalSize)}
0 ? (asset.totalSize / stats.totalAssetSize) * 100 : 0 } className="h-2" />
), )}
)}
); }