Files
thrilltrack-explorer/src-old/lib/moderation/entities.ts

223 lines
6.5 KiB
TypeScript

/**
* Entity Resolution Utilities
*
* Functions for resolving entity names and display information
* from cached entity data used in moderation workflows.
*/
/**
* Entity cache structure (matching useEntityCache hook)
*/
interface EntityCache {
rides: Map<string, { id: string; name: string; park_id?: string }>;
parks: Map<string, { id: string; name: string }>;
companies: Map<string, { id: string; name: string }>;
}
/**
* Generic submission content type
*/
interface GenericSubmissionContent {
name?: string;
entity_id?: string;
entity_name?: string;
park_id?: string;
ride_id?: string;
company_id?: string;
manufacturer_id?: string;
designer_id?: string;
operator_id?: string;
property_owner_id?: string;
[key: string]: unknown;
}
/**
* Result of entity name resolution
*/
export interface ResolvedEntityNames {
entityName: string;
parkName?: string;
}
/**
* Resolve entity name and related park name from submission content
*
* This function determines what entity is being modified based on the
* submission type and content, then looks up cached entity data to
* resolve display names.
*
* @param submissionType - Type of submission (e.g., 'ride', 'park', 'manufacturer')
* @param content - Submission content containing entity IDs
* @param entityCache - Cache of entity data
* @returns Resolved entity and park names
*
* @example
* ```tsx
* const { entityName, parkName } = resolveEntityName(
* 'ride',
* { entity_id: 'ride-123' },
* entityCacheRef.current
* );
* // Returns: { entityName: "Steel Vengeance", parkName: "Cedar Point" }
* ```
*/
export function resolveEntityName(
submissionType: string,
content: GenericSubmissionContent | null | undefined,
entityCache: EntityCache
): ResolvedEntityNames {
let entityName = content?.name || 'Unknown';
let parkName: string | undefined;
// Handle ride submissions - look up ride name and park
if (submissionType === '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;
}
}
}
// Handle park submissions
else if (submissionType === 'park' && content?.entity_id) {
const park = entityCache.parks.get(content.entity_id);
if (park) entityName = park.name;
}
// Handle company submissions (manufacturer, operator, designer, property_owner)
else if (
['manufacturer', 'operator', 'designer', 'property_owner'].includes(submissionType) &&
content?.entity_id
) {
const company = entityCache.companies.get(content.entity_id);
if (company) entityName = company.name;
}
// Handle content with ride_id reference
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;
}
}
}
// Handle content with park_id reference
else if (content?.park_id) {
const park = entityCache.parks.get(content.park_id);
if (park) parkName = park.name;
}
return { entityName, parkName };
}
/**
* Get a display-ready entity identifier string
*
* Combines entity name and park name (if available) into a single
* human-readable string for display in the moderation interface.
*
* @param entityName - Primary entity name
* @param parkName - Optional related park name
* @returns Formatted display string
*
* @example
* ```tsx
* getEntityDisplayName("Steel Vengeance", "Cedar Point")
* // Returns: "Steel Vengeance at Cedar Point"
*
* getEntityDisplayName("Cedar Point")
* // Returns: "Cedar Point"
* ```
*/
export function getEntityDisplayName(
entityName: string,
parkName?: string
): string {
if (parkName) {
return `${entityName} at ${parkName}`;
}
return entityName;
}
/**
* Extract all entity IDs from a list of submissions
*
* Scans through submission content to find all referenced entity IDs,
* grouped by entity type (rides, parks, companies).
*
* @param submissions - Array of submission objects
* @returns Object containing Sets of IDs for each entity type
*/
export function extractEntityIds(submissions: Array<{ content: unknown; submission_type: string }>): {
rideIds: Set<string>;
parkIds: Set<string>;
companyIds: Set<string>;
} {
const rideIds = new Set<string>();
const parkIds = new Set<string>();
const companyIds = new Set<string>();
submissions.forEach(submission => {
const content = submission.content as GenericSubmissionContent | null | undefined;
if (content && typeof content === 'object') {
// Direct entity references
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);
// Entity ID based on submission type
if (content.entity_id) {
if (submission.submission_type === 'ride') {
rideIds.add(content.entity_id);
} else if (submission.submission_type === 'park') {
parkIds.add(content.entity_id);
} else if (
['manufacturer', 'operator', 'designer', 'property_owner'].includes(
submission.submission_type
)
) {
companyIds.add(content.entity_id);
}
}
// Company role references
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);
}
});
return { rideIds, parkIds, companyIds };
}
/**
* Determine submission type display label
*
* Converts internal submission type identifiers to human-readable labels.
*
* @param submissionType - Internal submission type
* @returns Human-readable label
*/
export function getSubmissionTypeLabel(submissionType: string): string {
const labels: Record<string, string> = {
park: 'Park',
ride: 'Ride',
manufacturer: 'Manufacturer',
operator: 'Operator',
designer: 'Designer',
property_owner: 'Property Owner',
ride_model: 'Ride Model',
photo: 'Photo',
photo_delete: 'Photo Deletion',
milestone: 'Timeline Event',
timeline_event: 'Timeline Event',
review: 'Review',
};
return labels[submissionType] || submissionType;
}