Implement real-time features

This commit is contained in:
gpt-engineer-app[bot]
2025-09-30 16:46:12 +00:00
parent 7bbf67156b
commit 7920bdb911
7 changed files with 313 additions and 85 deletions

View File

@@ -0,0 +1,126 @@
import { useEffect, useState } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { RealtimeChannel } from '@supabase/supabase-js';
interface ModerationStats {
pendingSubmissions: number;
openReports: number;
flaggedContent: number;
}
interface UseRealtimeModerationStatsOptions {
onStatsChange?: (stats: ModerationStats) => void;
enabled?: boolean;
debounceMs?: number;
}
export const useRealtimeModerationStats = (options: UseRealtimeModerationStatsOptions = {}) => {
const { onStatsChange, enabled = true, debounceMs = 1000 } = options;
const [stats, setStats] = useState<ModerationStats>({
pendingSubmissions: 0,
openReports: 0,
flaggedContent: 0,
});
const [channel, setChannel] = useState<RealtimeChannel | null>(null);
const [updateTimer, setUpdateTimer] = useState<NodeJS.Timeout | null>(null);
const fetchStats = async () => {
try {
const [submissionsResult, reportsResult, reviewsResult] = await Promise.all([
supabase
.from('content_submissions')
.select('id', { count: 'exact', head: true })
.eq('status', 'pending'),
supabase
.from('reports')
.select('id', { count: 'exact', head: true })
.eq('status', 'pending'),
supabase
.from('reviews')
.select('id', { count: 'exact', head: true })
.eq('moderation_status', 'flagged'),
]);
const newStats = {
pendingSubmissions: submissionsResult.count || 0,
openReports: reportsResult.count || 0,
flaggedContent: reviewsResult.count || 0,
};
setStats(newStats);
onStatsChange?.(newStats);
} catch (error) {
console.error('Error fetching moderation stats:', error);
}
};
const debouncedFetchStats = () => {
if (updateTimer) {
clearTimeout(updateTimer);
}
const timer = setTimeout(fetchStats, debounceMs);
setUpdateTimer(timer);
};
useEffect(() => {
if (!enabled) return;
// Initial fetch
fetchStats();
// Set up realtime subscriptions
const realtimeChannel = supabase
.channel('moderation-stats-changes')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'content_submissions',
},
() => {
console.log('Content submissions changed');
debouncedFetchStats();
}
)
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'reports',
},
() => {
console.log('Reports changed');
debouncedFetchStats();
}
)
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'reviews',
},
() => {
console.log('Reviews changed');
debouncedFetchStats();
}
)
.subscribe((status) => {
console.log('Moderation stats realtime status:', status);
});
setChannel(realtimeChannel);
return () => {
console.log('Cleaning up moderation stats realtime subscription');
if (updateTimer) {
clearTimeout(updateTimer);
}
supabase.removeChannel(realtimeChannel);
};
}, [enabled]);
return { stats, refresh: fetchStats };
};

View File

@@ -0,0 +1,46 @@
import { useEffect, useState } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { RealtimeChannel } from '@supabase/supabase-js';
interface UseRealtimeSubmissionItemsOptions {
submissionId?: string;
onUpdate?: (payload: any) => void;
enabled?: boolean;
}
export const useRealtimeSubmissionItems = (options: UseRealtimeSubmissionItemsOptions = {}) => {
const { submissionId, onUpdate, enabled = true } = options;
const [channel, setChannel] = useState<RealtimeChannel | null>(null);
useEffect(() => {
if (!enabled || !submissionId) return;
const realtimeChannel = supabase
.channel(`submission-items-${submissionId}`)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'submission_items',
filter: `submission_id=eq.${submissionId}`,
},
(payload) => {
console.log('Submission item updated:', payload);
onUpdate?.(payload);
}
)
.subscribe((status) => {
console.log('Submission items realtime status:', status);
});
setChannel(realtimeChannel);
return () => {
console.log('Cleaning up submission items realtime subscription');
supabase.removeChannel(realtimeChannel);
};
}, [submissionId, enabled, onUpdate]);
return { channel };
};

View File

@@ -0,0 +1,70 @@
import { useEffect, useState } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { RealtimeChannel } from '@supabase/supabase-js';
interface UseRealtimeSubmissionsOptions {
onInsert?: (payload: any) => void;
onUpdate?: (payload: any) => void;
onDelete?: (payload: any) => void;
enabled?: boolean;
}
export const useRealtimeSubmissions = (options: UseRealtimeSubmissionsOptions = {}) => {
const { onInsert, onUpdate, onDelete, enabled = true } = options;
const [channel, setChannel] = useState<RealtimeChannel | null>(null);
useEffect(() => {
if (!enabled) return;
const realtimeChannel = supabase
.channel('content-submissions-changes')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'content_submissions',
},
(payload) => {
console.log('Submission inserted:', payload);
onInsert?.(payload);
}
)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'content_submissions',
},
(payload) => {
console.log('Submission updated:', payload);
onUpdate?.(payload);
}
)
.on(
'postgres_changes',
{
event: 'DELETE',
schema: 'public',
table: 'content_submissions',
},
(payload) => {
console.log('Submission deleted:', payload);
onDelete?.(payload);
}
)
.subscribe((status) => {
console.log('Submissions realtime status:', status);
});
setChannel(realtimeChannel);
return () => {
console.log('Cleaning up submissions realtime subscription');
supabase.removeChannel(realtimeChannel);
};
}, [enabled, onInsert, onUpdate, onDelete]);
return { channel };
};