Yaltopia-Ticket-Admin/src/pages/admin/analytics/revenue.tsx
2026-04-08 09:28:01 +03:00

212 lines
8.6 KiB
TypeScript

import { useQuery } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from "recharts";
import { analyticsService } from "@/services";
import type { StorageByUser, StorageAnalytics } from "@/types/analytics.types";
import {
HardDrive,
FileText,
Database,
Users,
ChevronRight,
} from "lucide-react";
const COLORS = ["#111827", "#4B5563", "#9CA3AF", "#D1D5DB", "#E2E8F0"];
interface ChartDataItem {
name: string;
value: number;
}
export default function AnalyticsStoragePage() {
const { data: storage, isLoading } = useQuery<StorageAnalytics>({
queryKey: ["admin", "analytics", "storage"],
queryFn: () => analyticsService.getStorageAnalytics(),
});
const formatBytes = (bytes: number) => {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
};
const chartData: ChartDataItem[] =
storage?.byCategory?.map((cat) => ({
name: cat.category,
value: cat.size,
})) || [];
return (
<div className="space-y-8 max-w-7xl mx-auto bg-white p-4 min-h-screen">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h1 className="text-3xl font-bold text-gray-900 tracking-tight">
Storage Intelligence
</h1>
<p className="text-gray-500 mt-1">
Infrastructure resource allocation and data distribution registry.
</p>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<Card className="border shadow-none rounded-none bg-white">
<CardHeader className="border-b pb-4 bg-gray-50/30 flex flex-row items-center justify-between">
<CardTitle className="text-xs font-bold uppercase tracking-widest text-gray-400">
Resource Consumption
</CardTitle>
<HardDrive className="w-4 h-4 text-gray-300" />
</CardHeader>
<CardContent className="p-8 space-y-8">
{isLoading ? (
<div className="h-[300px] flex items-center justify-center text-gray-400 animate-pulse font-medium uppercase tracking-widest text-[10px]">
Quantifying resources...
</div>
) : (
<>
<div className="grid grid-cols-2 gap-8">
<div className="space-y-1">
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest">
Aggregate Payload
</p>
<p className="text-3xl font-bold text-gray-900 tracking-tighter">
{storage?.total ? formatBytes(storage.total.size) : "0 B"}
</p>
</div>
<div className="space-y-1">
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest">
Object Registry
</p>
<p className="text-3xl font-bold text-gray-900 tracking-tighter">
{storage?.total?.files?.toLocaleString() || 0}{" "}
<span className="text-xs text-gray-400 uppercase ml-1">
Items
</span>
</p>
</div>
</div>
<div className="pt-8 border-t">
<div className="flex items-center justify-between mb-4">
<span className="text-[10px] font-bold text-gray-900 uppercase tracking-widest">
Efficiency Status
</span>
<span className="px-2 py-0.5 text-[9px] font-bold uppercase bg-emerald-50 text-emerald-600 border border-emerald-100">
Optimal
</span>
</div>
<div className="w-full bg-gray-100 h-2">
<div className="bg-gray-900 h-full w-[42%]" />
</div>
<p className="text-[10px] text-gray-400 font-medium mt-2">
42% Cluster Capacity Utilized
</p>
</div>
</>
)}
</CardContent>
</Card>
<Card className="border shadow-none rounded-none bg-white">
<CardHeader className="border-b pb-4 bg-gray-50/30 flex flex-row items-center justify-between">
<CardTitle className="text-xs font-bold uppercase tracking-widest text-gray-400">
Distribution by Taxonomy
</CardTitle>
<Database className="w-4 h-4 text-gray-300" />
</CardHeader>
<CardContent className="p-8">
{isLoading ? (
<div className="h-[300px] flex items-center justify-center">
...
</div>
) : chartData.length > 0 ? (
<ResponsiveContainer width="100%" height={300}>
<PieChart>
<Pie
data={chartData}
cx="50%"
cy="50%"
innerRadius={60}
outerRadius={100}
paddingAngle={4}
stroke="none"
dataKey="value"
>
{chartData.map((_entry: ChartDataItem, index: number) => (
<Cell
key={`cell-${index}`}
fill={COLORS[index % COLORS.length]}
/>
))}
</Pie>
<Tooltip
contentStyle={{
backgroundColor: "#fff",
border: "1px solid #E2E8F0",
borderRadius: "0px",
boxShadow: "none",
fontSize: "10px",
fontWeight: "700",
textTransform: "uppercase",
}}
formatter={(value: number) => formatBytes(value)}
/>
</PieChart>
</ResponsiveContainer>
) : (
<div className="h-[300px] flex items-center justify-center text-gray-400 italic font-medium uppercase tracking-widest text-[10px]">
No category distribution.
</div>
)}
</CardContent>
</Card>
</div>
{storage?.topUsers && storage.topUsers.length > 0 && (
<Card className="border shadow-none rounded-none bg-white">
<CardHeader className="border-b pb-4 bg-gray-50/30 flex flex-row items-center justify-between">
<div className="flex items-center gap-2">
<Users className="w-4 h-4 text-gray-400" />
<CardTitle className="text-xs font-bold uppercase tracking-widest text-gray-400">
High-Consumption Operators
</CardTitle>
</div>
<FileText className="w-4 h-4 text-gray-300" />
</CardHeader>
<CardContent className="p-0">
<div className="divide-y divide-gray-100">
{storage.topUsers.map((user: StorageByUser, index: number) => (
<div
key={index}
className="flex items-center justify-between p-6 hover:bg-gray-50 transition-colors group"
>
<div className="flex items-center gap-4">
<div className="w-8 h-8 flex items-center justify-center bg-gray-50 border text-[10px] font-bold text-gray-400">
0{index + 1}
</div>
<div>
<p className="text-sm font-bold text-gray-900 tracking-tighter">
{user.userName || user.email}
</p>
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mt-0.5">
{user.documentCount.toLocaleString()} Object References
</p>
</div>
</div>
<div className="flex items-center gap-8">
<span className="text-sm font-bold text-gray-900 tabular-nums">
{formatBytes(user.storageUsed)}
</span>
<ChevronRight className="w-4 h-4 text-gray-300 opacity-0 group-hover:opacity-100 transition-all" />
</div>
</div>
))}
</div>
</CardContent>
</Card>
)}
</div>
);
}