/** * Query invalidation helpers for TanStack Query * * Use these helpers to invalidate cached queries when data changes. * This ensures UI stays in sync with backend state. */ import { useQueryClient } from '@tanstack/react-query'; import { queryKeys } from './queryKeys'; /** * Hook providing query invalidation helpers */ export function useQueryInvalidation() { const queryClient = useQueryClient(); return { /** * Invalidate user roles cache * Call this after assigning/revoking roles */ invalidateUserRoles: (userId?: string) => { if (userId) { queryClient.invalidateQueries({ queryKey: queryKeys.userRoles(userId) }); } else { queryClient.invalidateQueries({ queryKey: ['user-roles'] }); } }, /** * Invalidate user permissions cache * Call this after role changes that affect permissions */ invalidateUserPermissions: (userId?: string) => { if (userId) { queryClient.invalidateQueries({ queryKey: queryKeys.userPermissions(userId) }); } else { queryClient.invalidateQueries({ queryKey: ['user-permissions'] }); } }, /** * Invalidate both roles and permissions for a user * Use this as a convenience method after role updates */ invalidateUserAuth: (userId?: string) => { if (userId) { queryClient.invalidateQueries({ queryKey: queryKeys.userRoles(userId) }); queryClient.invalidateQueries({ queryKey: queryKeys.userPermissions(userId) }); } else { queryClient.invalidateQueries({ queryKey: ['user-roles'] }); queryClient.invalidateQueries({ queryKey: ['user-permissions'] }); } }, /** * Invalidate moderation queue * Call this after moderation actions */ invalidateModerationQueue: () => { queryClient.invalidateQueries({ queryKey: ['moderation-queue'] }); }, /** * Invalidate moderation stats * Call this after queue changes */ invalidateModerationStats: () => { queryClient.invalidateQueries({ queryKey: queryKeys.moderationStats() }); }, /** * Invalidate homepage data * Call this after creating/updating parks or rides */ invalidateHomepageData: (entityType?: 'parks' | 'rides' | 'all') => { if (!entityType || entityType === 'all') { queryClient.invalidateQueries({ queryKey: ['homepage'] }); } else if (entityType === 'parks') { queryClient.invalidateQueries({ queryKey: ['homepage'], predicate: (query) => { const key = query.queryKey[1] as string; return typeof key === 'string' && key.includes('parks'); } }); } else if (entityType === 'rides') { queryClient.invalidateQueries({ queryKey: ['homepage'], predicate: (query) => { const key = query.queryKey[1] as string; return typeof key === 'string' && key.includes('rides'); } }); } }, /** * Invalidate user profile cache * Call this after profile updates */ invalidateUserProfile: (userId: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.profile.detail(userId) }); }, /** * Invalidate profile stats cache * Call this after profile-related changes */ invalidateProfileStats: (userId: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.profile.stats(userId) }); }, /** * Invalidate profile activity cache * Call this after activity changes */ invalidateProfileActivity: (userId: string) => { queryClient.invalidateQueries({ queryKey: ['profile', 'activity', userId] }); }, /** * Invalidate user search results * Call this when display names change */ invalidateUserSearch: () => { queryClient.invalidateQueries({ queryKey: ['users', 'search'] }); }, /** * Invalidate admin settings cache * Call this after updating admin settings */ invalidateAdminSettings: () => { queryClient.invalidateQueries({ queryKey: queryKeys.admin.settings() }); }, /** * Invalidate audit logs cache * Call this after inserting audit log entries */ invalidateAuditLogs: (userId?: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.admin.auditLogs(userId) }); }, /** * Invalidate contact submissions cache * Call this after updating contact submissions */ invalidateContactSubmissions: () => { queryClient.invalidateQueries({ queryKey: ['admin-contact-submissions'] }); }, /** * Invalidate blog posts cache * Call this after creating/updating/deleting blog posts */ invalidateBlogPosts: () => { queryClient.invalidateQueries({ queryKey: queryKeys.admin.blogPosts() }); }, /** * Invalidate parks listing cache * Call this after creating/updating/deleting parks */ invalidateParks: () => { queryClient.invalidateQueries({ queryKey: ['parks'] }); }, /** * Invalidate rides listing cache * Call this after creating/updating/deleting rides */ invalidateRides: () => { queryClient.invalidateQueries({ queryKey: ['rides'] }); }, /** * Invalidate park detail cache * Call this after updating a park */ invalidateParkDetail: (slug: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.parks.detail(slug) }); }, /** * Invalidate ride detail cache * Call this after updating a ride */ invalidateRideDetail: (parkSlug: string, rideSlug: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.rides.detail(parkSlug, rideSlug) }); }, /** * Invalidate entity reviews * Call this after adding/updating/deleting reviews */ invalidateEntityReviews: (entityType: 'park' | 'ride', entityId: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.reviews.entity(entityType, entityId) }); }, /** * Invalidate user reviews * Call this after a user adds/updates/deletes their reviews */ invalidateUserReviews: (userId: string) => { queryClient.invalidateQueries({ queryKey: ['reviews', 'user', userId] }); }, /** * Invalidate entity photos * Call this after uploading/deleting photos */ invalidateEntityPhotos: (entityType: string, entityId: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.photos.entity(entityType, entityId) }); }, /** * Invalidate photo count * Call this after photo changes */ invalidatePhotoCount: (entityType: string, entityId: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.photos.count(entityType, entityId) }); }, /** * Invalidate search results * Call this after major data changes */ invalidateSearchResults: () => { queryClient.invalidateQueries({ queryKey: ['search'] }); }, /** * Invalidate similar rides * Call this after ride updates */ invalidateSimilarRides: (parkId: string, category: string) => { queryClient.invalidateQueries({ queryKey: ['rides', 'similar', parkId, category] }); }, /** * Invalidate featured parks * Call this after park updates that affect featured status */ invalidateFeaturedParks: () => { queryClient.invalidateQueries({ queryKey: ['homepage', 'featured-parks'] }); }, /** * Invalidate company detail cache * Call this after updating a company */ invalidateCompanyDetail: (slug: string, type: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.companies.detail(slug, type) }); }, /** * Invalidate company statistics cache * Call this after changes affecting company stats */ invalidateCompanyStatistics: (id: string, type: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.companies.statistics(id, type) }); }, /** * Invalidate company parks cache * Call this after park changes */ invalidateCompanyParks: (id: string, type: 'operator' | 'property_owner') => { queryClient.invalidateQueries({ queryKey: ['companies', 'parks', id, type] }); }, /** * Invalidate ride model detail cache * Call this after updating a ride model */ invalidateRideModelDetail: (manufacturerSlug: string, modelSlug: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.rideModels.detail(manufacturerSlug, modelSlug) }); }, /** * Invalidate ride model statistics cache * Call this after changes affecting model stats */ invalidateRideModelStatistics: (modelId: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.rideModels.statistics(modelId) }); }, /** * Invalidate model rides cache * Call this after ride changes */ invalidateModelRides: (modelId: string, limit?: number) => { queryClient.invalidateQueries({ queryKey: queryKeys.rideModels.rides(modelId, limit), }); }, /** * Invalidate entity name cache * Call this after updating an entity's name */ invalidateEntityName: (entityType: string, entityId: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.entities.name(entityType, entityId) }); }, /** * Invalidate blog post cache * Call this after updating a blog post */ invalidateBlogPost: (slug: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.blog.post(slug) }); }, /** * Invalidate coaster stats cache * Call this after updating ride statistics */ invalidateCoasterStats: (rideId: string) => { queryClient.invalidateQueries({ queryKey: queryKeys.stats.coaster(rideId) }); }, /** * Invalidate email change status cache * Call this after email change operations */ invalidateEmailChangeStatus: () => { queryClient.invalidateQueries({ queryKey: queryKeys.security.emailChangeStatus() }); }, /** * Invalidate sessions cache * Call this after session operations (login, logout, revoke) */ invalidateSessions: () => { queryClient.invalidateQueries({ queryKey: queryKeys.security.sessions() }); }, /** * Invalidate security queries * Call this after security-related changes (email, sessions) */ invalidateSecurityQueries: () => { queryClient.invalidateQueries({ queryKey: queryKeys.security.emailChangeStatus() }); queryClient.invalidateQueries({ queryKey: queryKeys.security.sessions() }); }, /** * Smart invalidation for related entities * Invalidates entity detail, photos, reviews, and name cache * Call this after any entity update */ invalidateRelatedEntities: (entityType: string, entityId: string) => { // Invalidate the entity itself if (entityType === 'park') { queryClient.invalidateQueries({ queryKey: queryKeys.parks.detail(entityId) }); } else if (entityType === 'ride') { queryClient.invalidateQueries({ queryKey: queryKeys.rides.detail('', entityId) }); } // Invalidate photos, reviews, and entity name queryClient.invalidateQueries({ queryKey: queryKeys.photos.entity(entityType, entityId) }); queryClient.invalidateQueries({ queryKey: queryKeys.reviews.entity(entityType as 'park' | 'ride', entityId) }); queryClient.invalidateQueries({ queryKey: queryKeys.entities.name(entityType, entityId) }); }, }; }