mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 22:11:11 -05:00
Fix real-time stats and submission refresh
This commit is contained in:
@@ -348,12 +348,89 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
|||||||
|
|
||||||
// Set up realtime subscriptions
|
// Set up realtime subscriptions
|
||||||
const { connectionState: submissionsConnectionState, reconnect: reconnectSubmissions } = useRealtimeSubmissions({
|
const { connectionState: submissionsConnectionState, reconnect: reconnectSubmissions } = useRealtimeSubmissions({
|
||||||
onInsert: (payload) => {
|
onInsert: async (payload) => {
|
||||||
toast({
|
const newSubmission = payload.new;
|
||||||
title: 'New Submission',
|
|
||||||
description: 'A new content submission has been added',
|
// Only add if it matches current filters
|
||||||
});
|
const matchesStatusFilter =
|
||||||
fetchItems(activeEntityFilter, activeStatusFilter);
|
activeStatusFilter === 'all' ||
|
||||||
|
(activeStatusFilter === 'pending' && (newSubmission.status === 'pending' || newSubmission.status === 'partially_approved')) ||
|
||||||
|
activeStatusFilter === newSubmission.status;
|
||||||
|
|
||||||
|
const matchesEntityFilter =
|
||||||
|
activeEntityFilter === 'all' ||
|
||||||
|
(activeEntityFilter === 'submissions' && newSubmission.submission_type !== 'photo') ||
|
||||||
|
(activeEntityFilter === 'photos' && newSubmission.submission_type === 'photo');
|
||||||
|
|
||||||
|
if (!matchesStatusFilter || !matchesEntityFilter) return;
|
||||||
|
|
||||||
|
// Fetch minimal data for the new submission
|
||||||
|
try {
|
||||||
|
const { data: profile } = await supabase
|
||||||
|
.from('profiles')
|
||||||
|
.select('user_id, username, display_name, avatar_url')
|
||||||
|
.eq('user_id', newSubmission.user_id)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
// Fetch entity name if photo submission
|
||||||
|
let entity_name, park_name;
|
||||||
|
if (newSubmission.submission_type === 'photo' && newSubmission.content) {
|
||||||
|
const contentObj = newSubmission.content as any;
|
||||||
|
const contextType = typeof contentObj.context === 'string' ? contentObj.context : null;
|
||||||
|
const entityId = contentObj.entity_id || contentObj.ride_id || contentObj.park_id || contentObj.company_id;
|
||||||
|
|
||||||
|
if (contextType === 'ride' && entityId) {
|
||||||
|
const { data: rideData } = await supabase
|
||||||
|
.from('rides')
|
||||||
|
.select('name, parks:park_id(name)')
|
||||||
|
.eq('id', entityId)
|
||||||
|
.single();
|
||||||
|
if (rideData) {
|
||||||
|
entity_name = rideData.name;
|
||||||
|
park_name = rideData.parks?.name;
|
||||||
|
}
|
||||||
|
} else if (contextType === 'park' && entityId) {
|
||||||
|
const { data: parkData } = await supabase
|
||||||
|
.from('parks')
|
||||||
|
.select('name')
|
||||||
|
.eq('id', entityId)
|
||||||
|
.single();
|
||||||
|
if (parkData) entity_name = parkData.name;
|
||||||
|
} else if (['manufacturer', 'operator', 'designer', 'property_owner'].includes(contextType) && entityId) {
|
||||||
|
const { data: companyData } = await supabase
|
||||||
|
.from('companies')
|
||||||
|
.select('name')
|
||||||
|
.eq('id', entityId)
|
||||||
|
.single();
|
||||||
|
if (companyData) entity_name = companyData.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new item and prepend to list
|
||||||
|
const newItem: ModerationItem = {
|
||||||
|
id: newSubmission.id,
|
||||||
|
type: 'content_submission',
|
||||||
|
content: newSubmission.submission_type === 'photo' ? newSubmission.content : newSubmission,
|
||||||
|
created_at: newSubmission.created_at,
|
||||||
|
user_id: newSubmission.user_id,
|
||||||
|
status: newSubmission.status,
|
||||||
|
submission_type: newSubmission.submission_type,
|
||||||
|
user_profile: profile || undefined,
|
||||||
|
entity_name,
|
||||||
|
park_name,
|
||||||
|
};
|
||||||
|
|
||||||
|
setItems(prevItems => [newItem, ...prevItems]);
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: 'New Submission',
|
||||||
|
description: 'A new content submission has been added',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding new submission to queue:', error);
|
||||||
|
// Fallback to full refresh on error
|
||||||
|
fetchItems(activeEntityFilter, activeStatusFilter);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onUpdate: (payload) => {
|
onUpdate: (payload) => {
|
||||||
// Update items state directly for better UX
|
// Update items state directly for better UX
|
||||||
|
|||||||
@@ -108,36 +108,189 @@ export const useRealtimeModerationStats = (options: UseRealtimeModerationStatsOp
|
|||||||
.on(
|
.on(
|
||||||
'postgres_changes',
|
'postgres_changes',
|
||||||
{
|
{
|
||||||
event: '*',
|
event: 'INSERT',
|
||||||
schema: 'public',
|
schema: 'public',
|
||||||
table: 'content_submissions',
|
table: 'content_submissions',
|
||||||
},
|
},
|
||||||
() => {
|
(payload) => {
|
||||||
console.log('Content submissions changed');
|
console.log('Content submission inserted');
|
||||||
|
// Optimistic update: increment pending submissions
|
||||||
|
if (payload.new.status === 'pending') {
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
pendingSubmissions: prev.pendingSubmissions + 1
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// Debounced sync as backup
|
||||||
debouncedFetchStats();
|
debouncedFetchStats();
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.on(
|
.on(
|
||||||
'postgres_changes',
|
'postgres_changes',
|
||||||
{
|
{
|
||||||
event: '*',
|
event: 'UPDATE',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'content_submissions',
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Content submission updated');
|
||||||
|
// Optimistic update: adjust counter based on status change
|
||||||
|
const oldStatus = payload.old.status;
|
||||||
|
const newStatus = payload.new.status;
|
||||||
|
|
||||||
|
if (oldStatus === 'pending' && newStatus !== 'pending') {
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
pendingSubmissions: Math.max(0, prev.pendingSubmissions - 1)
|
||||||
|
}));
|
||||||
|
} else if (oldStatus !== 'pending' && newStatus === 'pending') {
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
pendingSubmissions: prev.pendingSubmissions + 1
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
debouncedFetchStats();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: 'DELETE',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'content_submissions',
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Content submission deleted');
|
||||||
|
// Optimistic update: decrement if was pending
|
||||||
|
if (payload.old.status === 'pending') {
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
pendingSubmissions: Math.max(0, prev.pendingSubmissions - 1)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
debouncedFetchStats();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: 'INSERT',
|
||||||
schema: 'public',
|
schema: 'public',
|
||||||
table: 'reports',
|
table: 'reports',
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
console.log('Reports changed');
|
console.log('Report inserted');
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
openReports: prev.openReports + 1
|
||||||
|
}));
|
||||||
debouncedFetchStats();
|
debouncedFetchStats();
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.on(
|
.on(
|
||||||
'postgres_changes',
|
'postgres_changes',
|
||||||
{
|
{
|
||||||
event: '*',
|
event: 'UPDATE',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'reports',
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Report updated');
|
||||||
|
const oldStatus = payload.old.status;
|
||||||
|
const newStatus = payload.new.status;
|
||||||
|
|
||||||
|
if (oldStatus === 'pending' && newStatus !== 'pending') {
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
openReports: Math.max(0, prev.openReports - 1)
|
||||||
|
}));
|
||||||
|
} else if (oldStatus !== 'pending' && newStatus === 'pending') {
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
openReports: prev.openReports + 1
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
debouncedFetchStats();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: 'DELETE',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'reports',
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Report deleted');
|
||||||
|
if (payload.old.status === 'pending') {
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
openReports: Math.max(0, prev.openReports - 1)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
debouncedFetchStats();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: 'INSERT',
|
||||||
schema: 'public',
|
schema: 'public',
|
||||||
table: 'reviews',
|
table: 'reviews',
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
console.log('Reviews changed');
|
console.log('Review inserted');
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
flaggedContent: prev.flaggedContent + 1
|
||||||
|
}));
|
||||||
|
debouncedFetchStats();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: 'UPDATE',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'reviews',
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Review updated');
|
||||||
|
const oldStatus = payload.old.moderation_status;
|
||||||
|
const newStatus = payload.new.moderation_status;
|
||||||
|
|
||||||
|
if (oldStatus === 'flagged' && newStatus !== 'flagged') {
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
flaggedContent: Math.max(0, prev.flaggedContent - 1)
|
||||||
|
}));
|
||||||
|
} else if (oldStatus !== 'flagged' && newStatus === 'flagged') {
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
flaggedContent: prev.flaggedContent + 1
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
debouncedFetchStats();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: 'DELETE',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'reviews',
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Review deleted');
|
||||||
|
if (payload.old.moderation_status === 'flagged') {
|
||||||
|
setStats(prev => ({
|
||||||
|
...prev,
|
||||||
|
flaggedContent: Math.max(0, prev.flaggedContent - 1)
|
||||||
|
}));
|
||||||
|
}
|
||||||
debouncedFetchStats();
|
debouncedFetchStats();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user