mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 08:51:13 -05:00
Enable admin-only stats and update subscriptions
Implement plan to fix database RPCs by migrating to photos table, update hooks to enable only on admin pages, switch real-time subscriptions to the photos table, and apply to remaining forms. Also disable analytics in development.
This commit is contained in:
@@ -29,6 +29,11 @@ class AnalyticsErrorBoundary extends Component<
|
||||
}
|
||||
|
||||
export function AnalyticsWrapper() {
|
||||
// Disable analytics in development to reduce console noise
|
||||
if (import.meta.env.DEV) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<AnalyticsErrorBoundary>
|
||||
<Analytics />
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { queryKeys } from '@/lib/queryKeys';
|
||||
import type { DatabaseStatistics } from '@/types/database-stats';
|
||||
|
||||
export function useAdminDatabaseStats() {
|
||||
const location = useLocation();
|
||||
const isAdminPage = location.pathname.startsWith('/admin');
|
||||
|
||||
return useQuery({
|
||||
queryKey: queryKeys.admin.databaseStats(),
|
||||
queryFn: async () => {
|
||||
@@ -15,7 +19,8 @@ export function useAdminDatabaseStats() {
|
||||
|
||||
return data as unknown as DatabaseStatistics;
|
||||
},
|
||||
enabled: isAdminPage, // Only run query on admin pages
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
refetchInterval: 60 * 1000, // Auto-refetch every 60 seconds
|
||||
refetchInterval: isAdminPage ? 60 * 1000 : false, // Only refetch on admin pages
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
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 location = useLocation();
|
||||
const isAdminPage = location.pathname.startsWith('/admin');
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: queryKeys.admin.recentAdditions(limit),
|
||||
queryFn: async () => {
|
||||
@@ -18,8 +22,9 @@ export function useRecentAdditions(limit: number = 50, entityTypeFilter?: string
|
||||
|
||||
return data as unknown as RecentAddition[];
|
||||
},
|
||||
enabled: isAdminPage, // Only run query on admin pages
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
refetchInterval: 30 * 1000, // Auto-refetch every 30 seconds
|
||||
refetchInterval: isAdminPage ? 30 * 1000 : false, // Only refetch on admin pages
|
||||
});
|
||||
|
||||
// Set up real-time subscriptions
|
||||
@@ -51,7 +56,7 @@ export function useRecentAdditions(limit: number = 50, entityTypeFilter?: string
|
||||
.subscribe(),
|
||||
supabase
|
||||
.channel('recent_additions_photos')
|
||||
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'entity_photos' }, () => {
|
||||
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'photos' }, () => {
|
||||
query.refetch();
|
||||
})
|
||||
.subscribe(),
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
-- Fix get_database_statistics function to use correct table name 'photos' instead of 'entity_photos'
|
||||
CREATE OR REPLACE FUNCTION public.get_database_statistics()
|
||||
RETURNS jsonb
|
||||
LANGUAGE plpgsql
|
||||
STABLE SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $function$
|
||||
DECLARE
|
||||
v_stats jsonb;
|
||||
v_parks_total integer;
|
||||
v_parks_active integer;
|
||||
v_parks_historical integer;
|
||||
v_parks_7d integer;
|
||||
v_parks_30d integer;
|
||||
v_rides_total integer;
|
||||
v_rides_active integer;
|
||||
v_rides_historical integer;
|
||||
v_rides_7d integer;
|
||||
v_rides_30d integer;
|
||||
v_companies_total integer;
|
||||
v_manufacturers integer;
|
||||
v_operators integer;
|
||||
v_designers integer;
|
||||
v_companies_7d integer;
|
||||
v_companies_30d integer;
|
||||
v_ride_models_total integer;
|
||||
v_ride_models_7d integer;
|
||||
v_ride_models_30d integer;
|
||||
v_locations_total integer;
|
||||
v_timeline_events_total integer;
|
||||
v_photos_total integer;
|
||||
v_photos_7d integer;
|
||||
v_photos_30d integer;
|
||||
v_users_total integer;
|
||||
v_users_active_30d integer;
|
||||
v_submissions_pending integer;
|
||||
v_submissions_approved integer;
|
||||
v_submissions_rejected integer;
|
||||
BEGIN
|
||||
-- Parks statistics
|
||||
SELECT COUNT(*) INTO v_parks_total FROM parks;
|
||||
SELECT COUNT(*) INTO v_parks_active FROM parks WHERE status = 'operating';
|
||||
SELECT COUNT(*) INTO v_parks_historical FROM parks WHERE status IN ('closed', 'historical');
|
||||
SELECT COUNT(*) INTO v_parks_7d FROM parks WHERE created_at > NOW() - INTERVAL '7 days';
|
||||
SELECT COUNT(*) INTO v_parks_30d FROM parks WHERE created_at > NOW() - INTERVAL '30 days';
|
||||
|
||||
-- Rides statistics
|
||||
SELECT COUNT(*) INTO v_rides_total FROM rides;
|
||||
SELECT COUNT(*) INTO v_rides_active FROM rides WHERE status = 'operating';
|
||||
SELECT COUNT(*) INTO v_rides_historical FROM rides WHERE status IN ('closed', 'removed', 'relocated');
|
||||
SELECT COUNT(*) INTO v_rides_7d FROM rides WHERE created_at > NOW() - INTERVAL '7 days';
|
||||
SELECT COUNT(*) INTO v_rides_30d FROM rides WHERE created_at > NOW() - INTERVAL '30 days';
|
||||
|
||||
-- Companies statistics
|
||||
SELECT COUNT(*) INTO v_companies_total FROM companies;
|
||||
SELECT COUNT(*) INTO v_manufacturers FROM companies WHERE company_type = 'manufacturer';
|
||||
SELECT COUNT(*) INTO v_operators FROM companies WHERE company_type = 'operator';
|
||||
SELECT COUNT(*) INTO v_designers FROM companies WHERE company_type = 'designer';
|
||||
SELECT COUNT(*) INTO v_companies_7d FROM companies WHERE created_at > NOW() - INTERVAL '7 days';
|
||||
SELECT COUNT(*) INTO v_companies_30d FROM companies WHERE created_at > NOW() - INTERVAL '30 days';
|
||||
|
||||
-- Ride models statistics
|
||||
SELECT COUNT(*) INTO v_ride_models_total FROM ride_models;
|
||||
SELECT COUNT(*) INTO v_ride_models_7d FROM ride_models WHERE created_at > NOW() - INTERVAL '7 days';
|
||||
SELECT COUNT(*) INTO v_ride_models_30d FROM ride_models WHERE created_at > NOW() - INTERVAL '30 days';
|
||||
|
||||
-- Locations statistics
|
||||
SELECT COUNT(*) INTO v_locations_total FROM locations;
|
||||
|
||||
-- Timeline events statistics
|
||||
SELECT COUNT(*) INTO v_timeline_events_total FROM entity_timeline_events;
|
||||
|
||||
-- Photos statistics - FIXED: using 'photos' table instead of 'entity_photos'
|
||||
SELECT COUNT(*) INTO v_photos_total FROM photos;
|
||||
SELECT COUNT(*) INTO v_photos_7d FROM photos WHERE created_at > NOW() - INTERVAL '7 days';
|
||||
SELECT COUNT(*) INTO v_photos_30d FROM photos WHERE created_at > NOW() - INTERVAL '30 days';
|
||||
|
||||
-- Users statistics
|
||||
SELECT COUNT(*) INTO v_users_total FROM profiles;
|
||||
SELECT COUNT(*) INTO v_users_active_30d FROM profiles WHERE updated_at > NOW() - INTERVAL '30 days';
|
||||
|
||||
-- Submissions statistics
|
||||
SELECT COUNT(*) INTO v_submissions_pending FROM content_submissions WHERE status = 'pending';
|
||||
SELECT COUNT(*) INTO v_submissions_approved FROM content_submissions WHERE status = 'approved';
|
||||
SELECT COUNT(*) INTO v_submissions_rejected FROM content_submissions WHERE status = 'rejected';
|
||||
|
||||
-- Build result JSON
|
||||
v_stats := jsonb_build_object(
|
||||
'parks', jsonb_build_object(
|
||||
'total', v_parks_total,
|
||||
'active', v_parks_active,
|
||||
'historical', v_parks_historical,
|
||||
'added_7d', v_parks_7d,
|
||||
'added_30d', v_parks_30d
|
||||
),
|
||||
'rides', jsonb_build_object(
|
||||
'total', v_rides_total,
|
||||
'active', v_rides_active,
|
||||
'historical', v_rides_historical,
|
||||
'added_7d', v_rides_7d,
|
||||
'added_30d', v_rides_30d
|
||||
),
|
||||
'companies', jsonb_build_object(
|
||||
'total', v_companies_total,
|
||||
'manufacturers', v_manufacturers,
|
||||
'operators', v_operators,
|
||||
'designers', v_designers,
|
||||
'added_7d', v_companies_7d,
|
||||
'added_30d', v_companies_30d
|
||||
),
|
||||
'ride_models', jsonb_build_object(
|
||||
'total', v_ride_models_total,
|
||||
'added_7d', v_ride_models_7d,
|
||||
'added_30d', v_ride_models_30d
|
||||
),
|
||||
'locations', jsonb_build_object(
|
||||
'total', v_locations_total
|
||||
),
|
||||
'timeline_events', jsonb_build_object(
|
||||
'total', v_timeline_events_total
|
||||
),
|
||||
'photos', jsonb_build_object(
|
||||
'total', v_photos_total,
|
||||
'added_7d', v_photos_7d,
|
||||
'added_30d', v_photos_30d
|
||||
),
|
||||
'users', jsonb_build_object(
|
||||
'total', v_users_total,
|
||||
'active_30d', v_users_active_30d
|
||||
),
|
||||
'submissions', jsonb_build_object(
|
||||
'pending', v_submissions_pending,
|
||||
'approved', v_submissions_approved,
|
||||
'rejected', v_submissions_rejected
|
||||
)
|
||||
);
|
||||
|
||||
RETURN v_stats;
|
||||
END;
|
||||
$function$;
|
||||
|
||||
-- Fix get_recent_additions function to use correct table and column names
|
||||
CREATE OR REPLACE FUNCTION public.get_recent_additions(limit_count integer DEFAULT 50)
|
||||
RETURNS TABLE(entity_id uuid, entity_type text, entity_name text, entity_slug text, park_slug text, image_url text, created_at timestamp with time zone, created_by_id uuid, created_by_username text, created_by_avatar text)
|
||||
LANGUAGE plpgsql
|
||||
STABLE SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $function$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT * FROM (
|
||||
-- Parks
|
||||
SELECT
|
||||
p.id as entity_id,
|
||||
'park'::text as entity_type,
|
||||
p.name as entity_name,
|
||||
p.slug as entity_slug,
|
||||
NULL::text as park_slug,
|
||||
p.card_image_url as image_url,
|
||||
p.created_at,
|
||||
p.created_by as created_by_id,
|
||||
prof.username as created_by_username,
|
||||
prof.avatar_url as created_by_avatar
|
||||
FROM parks p
|
||||
LEFT JOIN profiles prof ON prof.user_id = p.created_by
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Rides
|
||||
SELECT
|
||||
r.id as entity_id,
|
||||
'ride'::text as entity_type,
|
||||
r.name as entity_name,
|
||||
r.slug as entity_slug,
|
||||
pk.slug as park_slug,
|
||||
r.card_image_url as image_url,
|
||||
r.created_at,
|
||||
r.created_by as created_by_id,
|
||||
prof.username as created_by_username,
|
||||
prof.avatar_url as created_by_avatar
|
||||
FROM rides r
|
||||
LEFT JOIN parks pk ON pk.id = r.park_id
|
||||
LEFT JOIN profiles prof ON prof.user_id = r.created_by
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Companies
|
||||
SELECT
|
||||
c.id as entity_id,
|
||||
'company'::text as entity_type,
|
||||
c.name as entity_name,
|
||||
c.slug as entity_slug,
|
||||
NULL::text as park_slug,
|
||||
c.card_image_url as image_url,
|
||||
c.created_at,
|
||||
c.created_by as created_by_id,
|
||||
prof.username as created_by_username,
|
||||
prof.avatar_url as created_by_avatar
|
||||
FROM companies c
|
||||
LEFT JOIN profiles prof ON prof.user_id = c.created_by
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Ride Models
|
||||
SELECT
|
||||
rm.id as entity_id,
|
||||
'ride_model'::text as entity_type,
|
||||
rm.name as entity_name,
|
||||
rm.slug as entity_slug,
|
||||
NULL::text as park_slug,
|
||||
rm.card_image_url as image_url,
|
||||
rm.created_at,
|
||||
rm.created_by as created_by_id,
|
||||
prof.username as created_by_username,
|
||||
prof.avatar_url as created_by_avatar
|
||||
FROM ride_models rm
|
||||
LEFT JOIN profiles prof ON prof.user_id = rm.created_by
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Locations
|
||||
SELECT
|
||||
l.id as entity_id,
|
||||
'location'::text as entity_type,
|
||||
COALESCE(l.city || ', ' || l.country, l.country, 'Location') as entity_name,
|
||||
NULL::text as entity_slug,
|
||||
NULL::text as park_slug,
|
||||
NULL::text as image_url,
|
||||
l.created_at,
|
||||
NULL::uuid as created_by_id,
|
||||
NULL::text as created_by_username,
|
||||
NULL::text as created_by_avatar
|
||||
FROM locations l
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Timeline Events
|
||||
SELECT
|
||||
te.id as entity_id,
|
||||
'timeline_event'::text as entity_type,
|
||||
te.event_title as entity_name,
|
||||
NULL::text as entity_slug,
|
||||
NULL::text as park_slug,
|
||||
NULL::text as image_url,
|
||||
te.created_at,
|
||||
te.created_by as created_by_id,
|
||||
prof.username as created_by_username,
|
||||
prof.avatar_url as created_by_avatar
|
||||
FROM entity_timeline_events te
|
||||
LEFT JOIN profiles prof ON prof.user_id = te.created_by
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Photos - FIXED: using 'photos' table and correct column names
|
||||
SELECT
|
||||
p.id as entity_id,
|
||||
'photo'::text as entity_type,
|
||||
COALESCE(p.title, 'Photo') as entity_name,
|
||||
NULL::text as entity_slug,
|
||||
NULL::text as park_slug,
|
||||
p.cloudflare_image_url as image_url,
|
||||
p.created_at as created_at,
|
||||
p.submitted_by as created_by_id,
|
||||
prof.username as created_by_username,
|
||||
prof.avatar_url as created_by_avatar
|
||||
FROM photos p
|
||||
LEFT JOIN profiles prof ON prof.user_id = p.submitted_by
|
||||
) combined
|
||||
ORDER BY created_at DESC
|
||||
LIMIT limit_count;
|
||||
END;
|
||||
$function$;
|
||||
Reference in New Issue
Block a user