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,21 @@
import { useQuery } from '@tanstack/react-query';
import { supabase } from '@/integrations/supabase/client';
import { queryKeys } from '@/lib/queryKeys';
import type { DatabaseStatistics } from '@/types/database-stats';
export function useAdminDatabaseStats() {
return useQuery({
queryKey: queryKeys.admin.databaseStats(),
queryFn: async () => {
const { data, error } = await supabase.rpc('get_database_statistics');
if (error) {
throw error;
}
return data as unknown as DatabaseStatistics;
},
staleTime: 5 * 60 * 1000, // 5 minutes
refetchInterval: 60 * 1000, // Auto-refetch every 60 seconds
});
}

View File

@@ -0,0 +1,74 @@
import { useQuery } from '@tanstack/react-query';
import { supabase } from '@/integrations/supabase/client';
import { queryKeys } from '@/lib/queryKeys';
import type { RecentAddition } from '@/types/database-stats';
import { useEffect } from 'react';
export function useRecentAdditions(limit: number = 50, entityTypeFilter?: string) {
const query = useQuery({
queryKey: queryKeys.admin.recentAdditions(limit),
queryFn: async () => {
const { data, error } = await supabase.rpc('get_recent_additions', {
limit_count: limit
});
if (error) {
throw error;
}
return data as unknown as RecentAddition[];
},
staleTime: 2 * 60 * 1000, // 2 minutes
refetchInterval: 30 * 1000, // Auto-refetch every 30 seconds
});
// Set up real-time subscriptions
useEffect(() => {
const channels = [
supabase
.channel('recent_additions_parks')
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'parks' }, () => {
query.refetch();
})
.subscribe(),
supabase
.channel('recent_additions_rides')
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'rides' }, () => {
query.refetch();
})
.subscribe(),
supabase
.channel('recent_additions_companies')
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'companies' }, () => {
query.refetch();
})
.subscribe(),
supabase
.channel('recent_additions_ride_models')
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'ride_models' }, () => {
query.refetch();
})
.subscribe(),
supabase
.channel('recent_additions_photos')
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'entity_photos' }, () => {
query.refetch();
})
.subscribe(),
];
return () => {
channels.forEach(channel => channel.unsubscribe());
};
}, [query]);
// Filter by entity type on client side
const filteredData = entityTypeFilter && query.data
? query.data.filter(item => item.entity_type === entityTypeFilter)
: query.data;
return {
...query,
data: filteredData,
};
}