mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-25 20:31:13 -05:00
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:
161
src/pages/AdminDatabaseStats.tsx
Normal file
161
src/pages/AdminDatabaseStats.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user