Implement admin database stats dashboard

Add admin-only database statistics dashboard
- Introduces types for database statistics and recent additions
- Implements hooks to fetch statistics and recent additions via RPCs
- Adds UI components for stats cards and a recent additions table
- Integrates new AdminDatabaseStats page and routing under /admin/database-stats
- Updates admin sidebar and app routes to expose the new dashboard
- Enables real-time updates and export capabilities for recent additions
This commit is contained in:
gpt-engineer-app[bot]
2025-11-11 16:54:02 +00:00
parent 69db3c7743
commit f036776dce
11 changed files with 906 additions and 1 deletions

View File

@@ -0,0 +1,161 @@
import { Building2, Bike, Factory, Users, FileText, TrendingUp, Box, MapPin, Calendar, Image as ImageIcon } from 'lucide-react';
import { AdminLayout } from '@/components/layout/AdminLayout';
import { useAdminGuard } from '@/hooks/useAdminGuard';
import { DatabaseStatsCard } from '@/components/admin/database-stats/DatabaseStatsCard';
import { RecentAdditionsTable } from '@/components/admin/database-stats/RecentAdditionsTable';
import { useAdminDatabaseStats } from '@/hooks/useAdminDatabaseStats';
import { useRecentAdditions } from '@/hooks/useRecentAdditions';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { AlertCircle } from 'lucide-react';
export default function AdminDatabaseStats() {
const { isLoading, isAuthorized, needsMFA } = useAdminGuard();
const { data: stats, isLoading: statsLoading, error: statsError } = useAdminDatabaseStats();
const { data: recentAdditions, isLoading: additionsLoading } = useRecentAdditions(50);
if (isLoading || statsLoading) {
return (
<AdminLayout>
<div className="flex items-center justify-center h-64">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
</div>
</AdminLayout>
);
}
if (!isAuthorized || needsMFA) {
return null;
}
if (statsError) {
return (
<AdminLayout>
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
Failed to load database statistics. Please try again later.
</AlertDescription>
</Alert>
</AdminLayout>
);
}
const totalEntities = stats
? stats.parks.total + stats.rides.total + stats.companies.total + stats.ride_models.total + stats.locations.total
: 0;
const recentAdditions7d = stats
? stats.parks.added_7d + stats.rides.added_7d + stats.companies.added_7d + stats.ride_models.added_7d + stats.photos.added_7d
: 0;
const recentAdditions30d = stats
? stats.parks.added_30d + stats.rides.added_30d + stats.companies.added_30d + stats.ride_models.added_30d + stats.photos.added_30d
: 0;
return (
<AdminLayout>
<div className="space-y-6">
<div>
<h1 className="text-3xl font-bold tracking-tight">Database Statistics</h1>
<p className="text-muted-foreground mt-2">
Complete overview of database content and activity
</p>
</div>
{/* Stats Grid */}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<DatabaseStatsCard
title="Total Entities"
icon={Box}
iconClassName="text-blue-500"
stats={[
{ label: 'All Entities', value: totalEntities },
{ label: 'Parks', value: stats?.parks.total || 0 },
{ label: 'Rides', value: stats?.rides.total || 0 },
{ label: 'Companies', value: stats?.companies.total || 0 },
{ label: 'Ride Models', value: stats?.ride_models.total || 0 },
]}
/>
<DatabaseStatsCard
title="Recent Activity"
icon={TrendingUp}
iconClassName="text-green-500"
stats={[
{
label: 'Added (7 days)',
value: recentAdditions7d,
},
{
label: 'Added (30 days)',
value: recentAdditions30d,
},
]}
/>
<DatabaseStatsCard
title="Parks & Rides"
icon={Building2}
iconClassName="text-purple-500"
stats={[
{ label: 'Active Parks', value: stats?.parks.active || 0 },
{ label: 'Historical Parks', value: stats?.parks.historical || 0 },
{ label: 'Active Rides', value: stats?.rides.active || 0 },
{ label: 'Historical Rides', value: stats?.rides.historical || 0 },
]}
/>
<DatabaseStatsCard
title="Content"
icon={ImageIcon}
iconClassName="text-orange-500"
stats={[
{ label: 'Photos', value: stats?.photos.total || 0 },
{ label: 'Locations', value: stats?.locations.total || 0 },
{ label: 'Timeline Events', value: stats?.timeline_events.total || 0 },
]}
/>
<DatabaseStatsCard
title="Companies"
icon={Factory}
iconClassName="text-amber-500"
stats={[
{ label: 'Total', value: stats?.companies.total || 0 },
{ label: 'Manufacturers', value: stats?.companies.manufacturers || 0 },
{ label: 'Operators', value: stats?.companies.operators || 0 },
{ label: 'Designers', value: stats?.companies.designers || 0 },
]}
/>
<DatabaseStatsCard
title="User Activity"
icon={Users}
iconClassName="text-teal-500"
stats={[
{ label: 'Total Users', value: stats?.users.total || 0 },
{ label: 'Active (30 days)', value: stats?.users.active_30d || 0 },
]}
/>
<DatabaseStatsCard
title="Submissions"
icon={FileText}
iconClassName="text-pink-500"
stats={[
{ label: 'Pending', value: stats?.submissions.pending || 0 },
{ label: 'Approved', value: stats?.submissions.approved || 0 },
{ label: 'Rejected', value: stats?.submissions.rejected || 0 },
]}
/>
</div>
{/* Recent Additions Table */}
<RecentAdditionsTable
additions={recentAdditions || []}
isLoading={additionsLoading}
/>
</div>
</AdminLayout>
);
}