mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 03:51:12 -05:00
Implement Review Lifecycle Tracking
This commit is contained in:
@@ -13,7 +13,9 @@ export type ActivityType =
|
||||
| 'account_deletion_confirmed'
|
||||
| 'account_deletion_cancelled'
|
||||
| 'user_banned'
|
||||
| 'user_unbanned';
|
||||
| 'user_unbanned'
|
||||
| 'review_created'
|
||||
| 'review_deleted';
|
||||
|
||||
export interface ActivityActor {
|
||||
id: string;
|
||||
@@ -84,6 +86,19 @@ export interface AccountLifecycleDetails {
|
||||
request_id?: string;
|
||||
}
|
||||
|
||||
export interface ReviewLifecycleDetails {
|
||||
review_id: string;
|
||||
user_id: string;
|
||||
username?: string;
|
||||
entity_type: 'park' | 'ride';
|
||||
entity_id: string;
|
||||
entity_name?: string;
|
||||
rating?: number;
|
||||
review_text?: string;
|
||||
deletion_reason?: string;
|
||||
was_moderated?: boolean;
|
||||
}
|
||||
|
||||
export type ActivityDetails =
|
||||
| EntityChangeDetails
|
||||
| AdminActionDetails
|
||||
@@ -91,7 +106,8 @@ export type ActivityDetails =
|
||||
| ReportResolutionDetails
|
||||
| ReviewModerationDetails
|
||||
| PhotoApprovalDetails
|
||||
| AccountLifecycleDetails;
|
||||
| AccountLifecycleDetails
|
||||
| ReviewLifecycleDetails;
|
||||
|
||||
export interface SystemActivity {
|
||||
id: string;
|
||||
@@ -572,6 +588,70 @@ export async function fetchSystemActivities(
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch review lifecycle events
|
||||
// 1. Review creations (recent 7 days)
|
||||
const { data: newReviews, error: reviewsError } = await supabase
|
||||
.from('reviews')
|
||||
.select('id, user_id, park_id, ride_id, rating, review_text, created_at')
|
||||
.gte('created_at', sevenDaysAgo.toISOString())
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(Math.ceil(limit / 2));
|
||||
|
||||
if (!reviewsError && newReviews) {
|
||||
for (const review of newReviews) {
|
||||
const entityType = review.park_id ? 'park' : 'ride';
|
||||
const entityId = review.park_id || review.ride_id;
|
||||
|
||||
activities.push({
|
||||
id: `review-${review.id}`,
|
||||
type: 'review_created',
|
||||
timestamp: review.created_at,
|
||||
actor_id: review.user_id,
|
||||
action: 'created review',
|
||||
details: {
|
||||
review_id: review.id,
|
||||
user_id: review.user_id,
|
||||
entity_type: entityType as 'park' | 'ride',
|
||||
entity_id: entityId!,
|
||||
rating: review.rating,
|
||||
review_text: review.review_text,
|
||||
} as ReviewLifecycleDetails,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Review deletions
|
||||
const { data: deletedReviews, error: deletionsError } = await supabase
|
||||
.from('review_deletions')
|
||||
.select('id, review_id, user_id, park_id, ride_id, rating, review_text, deleted_by, deleted_at, deletion_reason, was_moderated')
|
||||
.order('deleted_at', { ascending: false })
|
||||
.limit(limit);
|
||||
|
||||
if (!deletionsError && deletedReviews) {
|
||||
for (const deletion of deletedReviews) {
|
||||
const entityType = deletion.park_id ? 'park' : 'ride';
|
||||
const entityId = deletion.park_id || deletion.ride_id;
|
||||
|
||||
activities.push({
|
||||
id: deletion.id,
|
||||
type: 'review_deleted',
|
||||
timestamp: deletion.deleted_at!,
|
||||
actor_id: deletion.deleted_by,
|
||||
action: 'deleted review',
|
||||
details: {
|
||||
review_id: deletion.review_id,
|
||||
user_id: deletion.user_id,
|
||||
entity_type: entityType as 'park' | 'ride',
|
||||
entity_id: entityId!,
|
||||
rating: deletion.rating,
|
||||
review_text: deletion.review_text || undefined,
|
||||
deletion_reason: deletion.deletion_reason || undefined,
|
||||
was_moderated: deletion.was_moderated,
|
||||
} as ReviewLifecycleDetails,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort all activities by timestamp (newest first)
|
||||
activities.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
||||
|
||||
@@ -670,6 +750,86 @@ export async function fetchSystemActivities(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enrich review lifecycle users and entities
|
||||
const reviewUserIds = filteredActivities
|
||||
.filter(a => ['review_created', 'review_deleted'].includes(a.type))
|
||||
.map(a => (a.details as ReviewLifecycleDetails).user_id)
|
||||
.filter(Boolean) as string[];
|
||||
|
||||
if (reviewUserIds.length > 0) {
|
||||
const { data: reviewProfiles } = await supabase
|
||||
.from('profiles')
|
||||
.select('user_id, username')
|
||||
.in('user_id', reviewUserIds);
|
||||
|
||||
if (reviewProfiles) {
|
||||
const reviewProfileMap = new Map(reviewProfiles.map(p => [p.user_id, p]));
|
||||
|
||||
for (const activity of filteredActivities) {
|
||||
if (['review_created', 'review_deleted'].includes(activity.type)) {
|
||||
const details = activity.details as ReviewLifecycleDetails;
|
||||
if (details.user_id && !details.username) {
|
||||
const reviewProfile = reviewProfileMap.get(details.user_id);
|
||||
if (reviewProfile) {
|
||||
details.username = reviewProfile.username;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enrich review entity names
|
||||
const parkReviewIds = filteredActivities
|
||||
.filter(a => ['review_created', 'review_deleted'].includes(a.type) && (a.details as ReviewLifecycleDetails).entity_type === 'park')
|
||||
.map(a => (a.details as ReviewLifecycleDetails).entity_id)
|
||||
.filter(Boolean) as string[];
|
||||
|
||||
const rideReviewIds = filteredActivities
|
||||
.filter(a => ['review_created', 'review_deleted'].includes(a.type) && (a.details as ReviewLifecycleDetails).entity_type === 'ride')
|
||||
.map(a => (a.details as ReviewLifecycleDetails).entity_id)
|
||||
.filter(Boolean) as string[];
|
||||
|
||||
if (parkReviewIds.length > 0) {
|
||||
const { data: parks } = await supabase
|
||||
.from('parks')
|
||||
.select('id, name')
|
||||
.in('id', parkReviewIds);
|
||||
|
||||
if (parks) {
|
||||
const parkMap = new Map(parks.map(p => [p.id, p.name]));
|
||||
|
||||
for (const activity of filteredActivities) {
|
||||
if (['review_created', 'review_deleted'].includes(activity.type)) {
|
||||
const details = activity.details as ReviewLifecycleDetails;
|
||||
if (details.entity_type === 'park' && !details.entity_name) {
|
||||
details.entity_name = parkMap.get(details.entity_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rideReviewIds.length > 0) {
|
||||
const { data: rides } = await supabase
|
||||
.from('rides')
|
||||
.select('id, name')
|
||||
.in('id', rideReviewIds);
|
||||
|
||||
if (rides) {
|
||||
const rideMap = new Map(rides.map(r => [r.id, r.name]));
|
||||
|
||||
for (const activity of filteredActivities) {
|
||||
if (['review_created', 'review_deleted'].includes(activity.type)) {
|
||||
const details = activity.details as ReviewLifecycleDetails;
|
||||
if (details.entity_type === 'ride' && !details.entity_name) {
|
||||
details.entity_name = rideMap.get(details.entity_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user