mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 17:11:13 -05:00
Reconstruct fetchItems function
This commit is contained in:
@@ -120,8 +120,284 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// TODO: Function body was accidentally removed - needs restoration
|
||||
console.error('fetchItems function body is missing!');
|
||||
try {
|
||||
// Set loading states
|
||||
if (!silent) {
|
||||
setLoading(true);
|
||||
} else {
|
||||
setIsRefreshing(true);
|
||||
}
|
||||
|
||||
// Build base query for content submissions
|
||||
let submissionsQuery = supabase
|
||||
.from('content_submissions')
|
||||
.select(`
|
||||
id,
|
||||
submission_type,
|
||||
status,
|
||||
content,
|
||||
created_at,
|
||||
user_id,
|
||||
reviewed_at,
|
||||
reviewer_id,
|
||||
reviewer_notes,
|
||||
escalated,
|
||||
priority,
|
||||
assigned_to,
|
||||
locked_until
|
||||
`)
|
||||
.order('priority', { ascending: false })
|
||||
.order('created_at', { ascending: true });
|
||||
|
||||
// Apply status filter
|
||||
if (statusFilter === 'all') {
|
||||
submissionsQuery = submissionsQuery.in('status', ['pending', 'approved', 'rejected', 'partially_approved']);
|
||||
} else if (statusFilter === 'pending') {
|
||||
submissionsQuery = submissionsQuery.in('status', ['pending', 'partially_approved']);
|
||||
} else {
|
||||
submissionsQuery = submissionsQuery.eq('status', statusFilter);
|
||||
}
|
||||
|
||||
// Apply entity type filter
|
||||
if (entityFilter === 'photos') {
|
||||
submissionsQuery = submissionsQuery.eq('submission_type', 'photo');
|
||||
} else if (entityFilter === 'submissions') {
|
||||
submissionsQuery = submissionsQuery.neq('submission_type', 'photo');
|
||||
}
|
||||
|
||||
const { data: submissions, error: submissionsError } = await submissionsQuery;
|
||||
|
||||
if (submissionsError) throw submissionsError;
|
||||
|
||||
// Get user IDs and fetch user profiles
|
||||
const userIds = submissions?.map(s => s.user_id).filter(Boolean) || [];
|
||||
const reviewerIds = submissions?.map(s => s.reviewer_id).filter((id): id is string => !!id) || [];
|
||||
const allUserIds = [...new Set([...userIds, ...reviewerIds])];
|
||||
|
||||
let userProfiles: any[] = [];
|
||||
if (allUserIds.length > 0) {
|
||||
const { data: profiles } = await supabase
|
||||
.from('profiles')
|
||||
.select('user_id, username, display_name, avatar_url')
|
||||
.in('user_id', allUserIds);
|
||||
|
||||
userProfiles = profiles || [];
|
||||
}
|
||||
|
||||
const userProfileMap = new Map(userProfiles.map(p => [p.user_id, p]));
|
||||
|
||||
// Collect entity IDs for bulk fetching
|
||||
const rideIds = new Set<string>();
|
||||
const parkIds = new Set<string>();
|
||||
const companyIds = new Set<string>();
|
||||
const rideModelIds = new Set<string>();
|
||||
|
||||
submissions?.forEach(submission => {
|
||||
const content = submission.content as any;
|
||||
if (content && typeof content === 'object') {
|
||||
if (content.ride_id) rideIds.add(content.ride_id);
|
||||
if (content.park_id) parkIds.add(content.park_id);
|
||||
if (content.company_id) companyIds.add(content.company_id);
|
||||
if (content.entity_id) {
|
||||
if (submission.submission_type === 'ride') rideIds.add(content.entity_id);
|
||||
if (submission.submission_type === 'park') parkIds.add(content.entity_id);
|
||||
if (['manufacturer', 'operator', 'designer', 'property_owner'].includes(submission.submission_type)) {
|
||||
companyIds.add(content.entity_id);
|
||||
}
|
||||
}
|
||||
if (content.manufacturer_id) companyIds.add(content.manufacturer_id);
|
||||
if (content.designer_id) companyIds.add(content.designer_id);
|
||||
if (content.operator_id) companyIds.add(content.operator_id);
|
||||
if (content.property_owner_id) companyIds.add(content.property_owner_id);
|
||||
if (content.ride_model_id) rideModelIds.add(content.ride_model_id);
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch entities only if we don't have them cached or if they're new
|
||||
const fetchPromises: Promise<{ type: string; data: any[] }>[] = [];
|
||||
|
||||
if (rideIds.size > 0) {
|
||||
const uncachedRideIds = Array.from(rideIds).filter(id => !entityCache.rides.has(id));
|
||||
if (uncachedRideIds.length > 0) {
|
||||
fetchPromises.push(
|
||||
Promise.resolve(
|
||||
supabase
|
||||
.from('rides')
|
||||
.select('id, name, park_id')
|
||||
.in('id', uncachedRideIds)
|
||||
).then(({ data }) => ({ type: 'rides', data: data || [] }))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (parkIds.size > 0) {
|
||||
const uncachedParkIds = Array.from(parkIds).filter(id => !entityCache.parks.has(id));
|
||||
if (uncachedParkIds.length > 0) {
|
||||
fetchPromises.push(
|
||||
Promise.resolve(
|
||||
supabase
|
||||
.from('parks')
|
||||
.select('id, name')
|
||||
.in('id', uncachedParkIds)
|
||||
).then(({ data }) => ({ type: 'parks', data: data || [] }))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (companyIds.size > 0) {
|
||||
const uncachedCompanyIds = Array.from(companyIds).filter(id => !entityCache.companies.has(id));
|
||||
if (uncachedCompanyIds.length > 0) {
|
||||
fetchPromises.push(
|
||||
Promise.resolve(
|
||||
supabase
|
||||
.from('companies')
|
||||
.select('id, name')
|
||||
.in('id', uncachedCompanyIds)
|
||||
).then(({ data }) => ({ type: 'companies', data: data || [] }))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all uncached entities
|
||||
const entityResults = await Promise.all(fetchPromises);
|
||||
|
||||
// Update entity cache
|
||||
entityResults.forEach(result => {
|
||||
if (result.type === 'rides') {
|
||||
result.data.forEach((ride: any) => {
|
||||
entityCache.rides.set(ride.id, ride);
|
||||
if (ride.park_id) parkIds.add(ride.park_id);
|
||||
});
|
||||
} else if (result.type === 'parks') {
|
||||
result.data.forEach((park: any) => {
|
||||
entityCache.parks.set(park.id, park);
|
||||
});
|
||||
} else if (result.type === 'companies') {
|
||||
result.data.forEach((company: any) => {
|
||||
entityCache.companies.set(company.id, company);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Helper function to create memo key
|
||||
const createMemoKey = (submission: any): string => {
|
||||
return JSON.stringify({
|
||||
id: submission.id,
|
||||
status: submission.status,
|
||||
content: submission.content,
|
||||
reviewed_at: submission.reviewed_at,
|
||||
reviewer_notes: submission.reviewer_notes,
|
||||
});
|
||||
};
|
||||
|
||||
// Map submissions to moderation items with memoization
|
||||
const moderationItems: ModerationItem[] = submissions?.map(submission => {
|
||||
const memoKey = createMemoKey(submission);
|
||||
const existingMemo = submissionMemo.get(submission.id);
|
||||
|
||||
// Check if we can reuse the memoized item
|
||||
if (existingMemo && createMemoKey(existingMemo) === memoKey) {
|
||||
return existingMemo as ModerationItem;
|
||||
}
|
||||
|
||||
// Resolve entity name
|
||||
const content = submission.content as any;
|
||||
let entityName = content?.name || 'Unknown';
|
||||
let parkName: string | undefined;
|
||||
|
||||
if (submission.submission_type === 'ride' && content?.entity_id) {
|
||||
const ride = entityCache.rides.get(content.entity_id);
|
||||
if (ride) {
|
||||
entityName = ride.name;
|
||||
if (ride.park_id) {
|
||||
const park = entityCache.parks.get(ride.park_id);
|
||||
if (park) parkName = park.name;
|
||||
}
|
||||
}
|
||||
} else if (submission.submission_type === 'park' && content?.entity_id) {
|
||||
const park = entityCache.parks.get(content.entity_id);
|
||||
if (park) entityName = park.name;
|
||||
} else if (['manufacturer', 'operator', 'designer', 'property_owner'].includes(submission.submission_type) && content?.entity_id) {
|
||||
const company = entityCache.companies.get(content.entity_id);
|
||||
if (company) entityName = company.name;
|
||||
} else if (content?.ride_id) {
|
||||
const ride = entityCache.rides.get(content.ride_id);
|
||||
if (ride) {
|
||||
entityName = ride.name;
|
||||
if (ride.park_id) {
|
||||
const park = entityCache.parks.get(ride.park_id);
|
||||
if (park) parkName = park.name;
|
||||
}
|
||||
}
|
||||
} else if (content?.park_id) {
|
||||
const park = entityCache.parks.get(content.park_id);
|
||||
if (park) parkName = park.name;
|
||||
}
|
||||
|
||||
const userProfile = userProfileMap.get(submission.user_id);
|
||||
const reviewerProfile = submission.reviewer_id ? userProfileMap.get(submission.reviewer_id) : undefined;
|
||||
|
||||
const item: ModerationItem = {
|
||||
id: submission.id,
|
||||
type: 'content_submission',
|
||||
content: submission.content,
|
||||
created_at: submission.created_at,
|
||||
user_id: submission.user_id,
|
||||
status: submission.status,
|
||||
submission_type: submission.submission_type,
|
||||
user_profile: userProfile ? {
|
||||
username: userProfile.username,
|
||||
display_name: userProfile.display_name,
|
||||
avatar_url: userProfile.avatar_url,
|
||||
} : undefined,
|
||||
entity_name: entityName,
|
||||
park_name: parkName,
|
||||
reviewed_at: submission.reviewed_at,
|
||||
reviewed_by: submission.reviewer_id,
|
||||
reviewer_notes: submission.reviewer_notes,
|
||||
reviewer_profile: reviewerProfile,
|
||||
};
|
||||
|
||||
return item;
|
||||
}) || [];
|
||||
|
||||
// Update memoization cache
|
||||
const newMemoMap = new Map<string, ModerationItem>();
|
||||
moderationItems.forEach(item => {
|
||||
newMemoMap.set(item.id, item);
|
||||
});
|
||||
setSubmissionMemo(newMemoMap);
|
||||
|
||||
// Apply smart merge for state updates
|
||||
const mergeResult = smartMergeArray(items, moderationItems, {
|
||||
compareFields: ['status', 'reviewed_at', 'reviewer_notes'],
|
||||
preserveOrder: silent && preserveInteraction,
|
||||
addToTop: false,
|
||||
});
|
||||
|
||||
if (!silent || mergeResult.hasChanges) {
|
||||
setItems(mergeResult.items);
|
||||
|
||||
// Track new items for toast notification
|
||||
if (silent && mergeResult.changes.added.length > 0) {
|
||||
setNewItemsCount(prev => prev + mergeResult.changes.added.length);
|
||||
} else if (!silent) {
|
||||
setNewItemsCount(0);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching moderation items:', error);
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: error.message || 'Failed to fetch moderation queue',
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setIsRefreshing(false);
|
||||
setIsInitialLoad(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Expose refresh method via ref
|
||||
|
||||
Reference in New Issue
Block a user