mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 17:46:06 +00:00
117 lines
5.0 KiB
TypeScript
117 lines
5.0 KiB
TypeScript
import { Metadata } from 'next';
|
|
import { db } from '@/lib/db';
|
|
import { auth } from '@/lib/auth';
|
|
import { redirect } from 'next/navigation';
|
|
import { getCachedTotalStorage } from '@/lib/admin-stats';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Users, Folder, Video, MessageSquare, Mic, HardDrive } from 'lucide-react';
|
|
|
|
export const metadata: Metadata = {
|
|
title: 'Admin Dashboard | OpenFrame',
|
|
description: 'Admin overview dashboard',
|
|
};
|
|
|
|
function formatBytes(bytes: number, decimals = 2) {
|
|
if (bytes < 0) return 'Error Fetching';
|
|
if (!+bytes) return '0 Bytes';
|
|
const k = 1024;
|
|
const dm = decimals < 0 ? 0 : decimals;
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
}
|
|
|
|
export default async function AdminDashboardPage() {
|
|
const session = await auth();
|
|
if (!session?.user?.isAdmin) {
|
|
redirect('/');
|
|
}
|
|
|
|
// 1. Database Stats
|
|
const [
|
|
totalUsers,
|
|
totalProjects,
|
|
totalVideos,
|
|
totalComments,
|
|
totalVoiceComments,
|
|
] = await Promise.all([
|
|
db.user.count(),
|
|
db.project.count(),
|
|
db.video.count(),
|
|
db.comment.count(),
|
|
db.comment.count({
|
|
where: { voiceUrl: { not: null } },
|
|
}),
|
|
]);
|
|
|
|
// 2. Storage Stats (Cached)
|
|
const totalStorageBytes = await getCachedTotalStorage();
|
|
|
|
return (
|
|
<div className="flex-1 space-y-4 px-4 md:px-8">
|
|
<div className="flex items-center justify-between space-y-2">
|
|
<h2 className="text-3xl font-bold tracking-tight">Dashboard Overview</h2>
|
|
</div>
|
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Total Users</CardTitle>
|
|
<Users className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{totalUsers}</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Workspaces & Projects</CardTitle>
|
|
<Folder className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{totalProjects}</div>
|
|
<p className="text-xs text-muted-foreground">
|
|
Total active projects on the platform
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Active Videos</CardTitle>
|
|
<Video className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{totalVideos}</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Total Comments</CardTitle>
|
|
<MessageSquare className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{totalComments}</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Voice Recordings</CardTitle>
|
|
<Mic className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{totalVoiceComments}</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Cloudflare R2 Storage</CardTitle>
|
|
<HardDrive className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{formatBytes(totalStorageBytes)}</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|