mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 20:11:12 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
209
src-old/types/audit-relational.ts
Normal file
209
src-old/types/audit-relational.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* TypeScript types for relational audit tables
|
||||
* Replaces JSONB columns with proper relational structures
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// ADMIN AUDIT DETAILS (replaces admin_audit_log.details)
|
||||
// ============================================================================
|
||||
|
||||
export interface AdminAuditDetail {
|
||||
id: string;
|
||||
audit_log_id: string;
|
||||
detail_key: string;
|
||||
detail_value: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface AdminAuditDetailInsert {
|
||||
audit_log_id: string;
|
||||
detail_key: string;
|
||||
detail_value: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// MODERATION AUDIT METADATA (replaces moderation_audit_log.metadata)
|
||||
// ============================================================================
|
||||
|
||||
export interface ModerationAuditMetadata {
|
||||
id: string;
|
||||
audit_log_id: string;
|
||||
metadata_key: string;
|
||||
metadata_value: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ModerationAuditMetadataInsert {
|
||||
audit_log_id: string;
|
||||
metadata_key: string;
|
||||
metadata_value: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PROFILE CHANGE FIELDS (replaces profile_audit_log.changes)
|
||||
// ============================================================================
|
||||
|
||||
export interface ProfileChangeField {
|
||||
id: string;
|
||||
audit_log_id: string;
|
||||
field_name: string;
|
||||
old_value: string | null;
|
||||
new_value: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ProfileChangeFieldInsert {
|
||||
audit_log_id: string;
|
||||
field_name: string;
|
||||
old_value?: string | null;
|
||||
new_value?: string | null;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ITEM CHANGE FIELDS (replaces item_edit_history.changes)
|
||||
// ============================================================================
|
||||
|
||||
export interface ItemChangeField {
|
||||
id: string;
|
||||
edit_history_id: string;
|
||||
field_name: string;
|
||||
old_value: string | null;
|
||||
new_value: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ItemChangeFieldInsert {
|
||||
edit_history_id: string;
|
||||
field_name: string;
|
||||
old_value?: string | null;
|
||||
new_value?: string | null;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// REQUEST BREADCRUMBS (replaces request_metadata.breadcrumbs)
|
||||
// ============================================================================
|
||||
|
||||
export interface RequestBreadcrumb {
|
||||
id: string;
|
||||
request_id: string;
|
||||
timestamp: string;
|
||||
category: string;
|
||||
message: string;
|
||||
level: 'debug' | 'info' | 'warn' | 'error';
|
||||
sequence_order: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface RequestBreadcrumbInsert {
|
||||
request_id: string;
|
||||
timestamp: string;
|
||||
category: string;
|
||||
message: string;
|
||||
level?: 'debug' | 'info' | 'warn' | 'error';
|
||||
sequence_order: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SUBMISSION METADATA (replaces content_submissions.content)
|
||||
// ============================================================================
|
||||
|
||||
export interface SubmissionMetadata {
|
||||
id: string;
|
||||
submission_id: string;
|
||||
metadata_key: string;
|
||||
metadata_value: string;
|
||||
value_type: 'string' | 'number' | 'boolean' | 'date' | 'url' | 'json';
|
||||
display_order: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface SubmissionMetadataInsert {
|
||||
submission_id: string;
|
||||
metadata_key: string;
|
||||
metadata_value: string;
|
||||
value_type?: 'string' | 'number' | 'boolean' | 'date' | 'url' | 'json';
|
||||
display_order?: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// REVIEW PHOTOS (replaces reviews.photos)
|
||||
// ============================================================================
|
||||
|
||||
export interface ReviewPhoto {
|
||||
id: string;
|
||||
review_id: string;
|
||||
cloudflare_image_id: string;
|
||||
cloudflare_image_url: string;
|
||||
caption: string | null;
|
||||
order_index: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface ReviewPhotoInsert {
|
||||
review_id: string;
|
||||
cloudflare_image_id: string;
|
||||
cloudflare_image_url: string;
|
||||
caption?: string | null;
|
||||
order_index?: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// NOTIFICATION EVENT DATA (replaces notification_logs.payload)
|
||||
// ============================================================================
|
||||
|
||||
export interface NotificationEventData {
|
||||
id: string;
|
||||
notification_log_id: string;
|
||||
event_key: string;
|
||||
event_value: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface NotificationEventDataInsert {
|
||||
notification_log_id: string;
|
||||
event_key: string;
|
||||
event_value: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CONFLICT DETAIL FIELDS (replaces conflict_resolutions.conflict_details)
|
||||
// ============================================================================
|
||||
|
||||
export interface ConflictDetailField {
|
||||
id: string;
|
||||
conflict_resolution_id: string;
|
||||
field_name: string;
|
||||
conflicting_value_1: string | null;
|
||||
conflicting_value_2: string | null;
|
||||
resolved_value: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ConflictDetailFieldInsert {
|
||||
conflict_resolution_id: string;
|
||||
field_name: string;
|
||||
conflicting_value_1?: string | null;
|
||||
conflicting_value_2?: string | null;
|
||||
resolved_value?: string | null;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// HELPER TYPES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Generic key-value structure for reading audit details
|
||||
*/
|
||||
export type AuditDetailsRecord = Record<string, string>;
|
||||
|
||||
/**
|
||||
* Generic change structure for reading change fields
|
||||
*/
|
||||
export interface ChangeRecord {
|
||||
old_value: string | null;
|
||||
new_value: string | null;
|
||||
}
|
||||
|
||||
export type ChangesRecord = Record<string, ChangeRecord>;
|
||||
122
src-old/types/auth.ts
Normal file
122
src-old/types/auth.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import type { Session, User } from '@supabase/supabase-js';
|
||||
|
||||
/**
|
||||
* Authenticator Assurance Levels (AAL)
|
||||
* - aal1: Basic authentication (password/OAuth/magic link)
|
||||
* - aal2: Multi-factor authentication completed
|
||||
*/
|
||||
export type AALLevel = 'aal1' | 'aal2';
|
||||
|
||||
/**
|
||||
* MFA Factor types supported by Supabase
|
||||
*/
|
||||
export type MFAFactorType = 'totp';
|
||||
|
||||
/**
|
||||
* MFA Factor status
|
||||
*/
|
||||
export type MFAFactorStatus = 'verified' | 'unverified';
|
||||
|
||||
/**
|
||||
* MFA Factor structure from Supabase
|
||||
*/
|
||||
export interface MFAFactor {
|
||||
id: string;
|
||||
factor_type: MFAFactorType;
|
||||
status: MFAFactorStatus;
|
||||
friendly_name?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of AAL step-up check
|
||||
*/
|
||||
export interface CheckAalResult {
|
||||
needsStepUp: boolean;
|
||||
hasMfaEnrolled: boolean;
|
||||
currentLevel: AALLevel | null;
|
||||
hasEnrolledFactors?: boolean;
|
||||
factorId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication method types
|
||||
*/
|
||||
export type AuthMethod = 'password' | 'oauth' | 'magiclink';
|
||||
|
||||
/**
|
||||
* Authentication session with AAL information
|
||||
*/
|
||||
export interface AuthSessionInfo {
|
||||
session: Session | null;
|
||||
user: User | null;
|
||||
aal: AALLevel;
|
||||
authMethod?: AuthMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* MFA Challenge result
|
||||
*/
|
||||
export interface MFAChallengeResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
newAal?: AALLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth service response
|
||||
*/
|
||||
export interface AuthServiceResponse<T = void> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication session from Supabase with hashed IP
|
||||
*/
|
||||
export interface AuthSession {
|
||||
id: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
refreshed_at: string | null;
|
||||
user_agent: string | null;
|
||||
ip: string | null; // Pre-hashed by database function
|
||||
not_after: string | null;
|
||||
aal: string | null; // Changed to string to match get_my_sessions() return type
|
||||
}
|
||||
|
||||
/**
|
||||
* Security-sensitive operations that may require additional verification
|
||||
*/
|
||||
export type SecurityOperation =
|
||||
| 'password_change'
|
||||
| 'identity_disconnect'
|
||||
| 'identity_connect'
|
||||
| 'session_revoke'
|
||||
| 'mfa_enroll'
|
||||
| 'mfa_unenroll';
|
||||
|
||||
/**
|
||||
* Rate limit information for security operations
|
||||
*/
|
||||
export interface RateLimitInfo {
|
||||
operation: SecurityOperation;
|
||||
allowed: boolean;
|
||||
attemptsRemaining: number;
|
||||
resetAt: Date;
|
||||
currentAttempts: number;
|
||||
maxAttempts: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Security operation context for logging
|
||||
*/
|
||||
export interface SecurityContext {
|
||||
operation: SecurityOperation;
|
||||
userId?: string;
|
||||
targetUserId?: string;
|
||||
requiresMFA?: boolean;
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
84
src-old/types/company-data.ts
Normal file
84
src-old/types/company-data.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Company Data Types
|
||||
*
|
||||
* Type-safe interfaces for company database records and form data.
|
||||
* These types prevent `as any` casts when working with company entities.
|
||||
*/
|
||||
|
||||
export interface CompanyDatabaseRecord {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string | null;
|
||||
company_type: 'manufacturer' | 'designer' | 'operator' | 'property_owner';
|
||||
person_type: 'company' | 'individual' | 'firm' | 'organization';
|
||||
website_url?: string | null;
|
||||
founded_year?: number | null;
|
||||
headquarters_location?: string | null;
|
||||
logo_url?: string | null;
|
||||
banner_image_url?: string | null;
|
||||
card_image_url?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
// Stats fields
|
||||
total_rides?: number;
|
||||
average_rating?: number;
|
||||
review_count?: number;
|
||||
}
|
||||
|
||||
export interface TimelineEventDatabaseRecord {
|
||||
id: string;
|
||||
entity_id: string;
|
||||
entity_type: 'park' | 'ride' | 'company' | 'ride_model';
|
||||
event_type: string;
|
||||
event_date: string;
|
||||
event_date_precision: 'day' | 'month' | 'year';
|
||||
title: string;
|
||||
description?: string | null;
|
||||
from_value?: string | null;
|
||||
to_value?: string | null;
|
||||
from_entity_id?: string | null;
|
||||
to_entity_id?: string | null;
|
||||
from_location_id?: string | null;
|
||||
to_location_id?: string | null;
|
||||
display_order: number;
|
||||
created_by?: string | null;
|
||||
approved_by?: string | null;
|
||||
submission_id?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface LocationData {
|
||||
country?: string;
|
||||
state_province?: string;
|
||||
city?: string;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard for company database records
|
||||
*/
|
||||
export function isCompanyRecord(data: unknown): data is CompanyDatabaseRecord {
|
||||
return (
|
||||
typeof data === 'object' &&
|
||||
data !== null &&
|
||||
'id' in data &&
|
||||
'name' in data &&
|
||||
'company_type' in data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard for timeline event records
|
||||
*/
|
||||
export function isTimelineEventRecord(data: unknown): data is TimelineEventDatabaseRecord {
|
||||
return (
|
||||
typeof data === 'object' &&
|
||||
data !== null &&
|
||||
'id' in data &&
|
||||
'entity_type' in data &&
|
||||
'event_type' in data
|
||||
);
|
||||
}
|
||||
101
src-old/types/company.ts
Normal file
101
src-old/types/company.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Company-related type definitions
|
||||
*/
|
||||
|
||||
import { ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
||||
|
||||
export interface UploadedImage {
|
||||
url: string;
|
||||
cloudflare_id?: string;
|
||||
file?: File;
|
||||
isLocal?: boolean;
|
||||
caption?: string;
|
||||
}
|
||||
|
||||
export interface CompanyFormData {
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string;
|
||||
company_type: 'manufacturer' | 'designer' | 'operator' | 'property_owner';
|
||||
person_type: 'company' | 'individual' | 'firm' | 'organization';
|
||||
website_url?: string;
|
||||
founded_year?: number;
|
||||
headquarters_location?: string;
|
||||
images?: ImageAssignments;
|
||||
}
|
||||
|
||||
export interface TempCompanyData {
|
||||
name: string;
|
||||
slug: string;
|
||||
company_type: 'manufacturer' | 'designer' | 'operator' | 'property_owner';
|
||||
person_type: 'company' | 'individual' | 'firm' | 'organization';
|
||||
description?: string;
|
||||
founded_year?: number;
|
||||
headquarters_location?: string;
|
||||
website_url?: string;
|
||||
}
|
||||
|
||||
export interface TempParkData {
|
||||
name: string;
|
||||
slug: string;
|
||||
park_type: string;
|
||||
status: string;
|
||||
description?: string;
|
||||
opening_date?: string;
|
||||
closing_date?: string;
|
||||
operator_id?: string;
|
||||
property_owner_id?: string;
|
||||
website_url?: string;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
location?: {
|
||||
name: string;
|
||||
city?: string;
|
||||
state_province?: string;
|
||||
country: string;
|
||||
postal_code?: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
};
|
||||
images?: {
|
||||
uploaded: UploadedImage[];
|
||||
banner_assignment?: number | null;
|
||||
card_assignment?: number | null;
|
||||
};
|
||||
_tempNewOperator?: TempCompanyData;
|
||||
_tempNewPropertyOwner?: TempCompanyData;
|
||||
}
|
||||
|
||||
export interface TempRideModelData {
|
||||
name: string;
|
||||
slug: string;
|
||||
category: string;
|
||||
ride_type: string;
|
||||
description?: string;
|
||||
images?: {
|
||||
uploaded: UploadedImage[];
|
||||
banner_assignment?: number | null;
|
||||
card_assignment?: number | null;
|
||||
};
|
||||
_technical_specifications?: TechnicalSpecification[];
|
||||
}
|
||||
|
||||
export interface TechnicalSpecification {
|
||||
id?: string;
|
||||
spec_name: string;
|
||||
spec_value: string;
|
||||
spec_type: 'string' | 'number' | 'boolean' | 'date';
|
||||
category?: string;
|
||||
unit?: string;
|
||||
display_order: number;
|
||||
}
|
||||
|
||||
export interface CoasterStat {
|
||||
id?: string;
|
||||
stat_type: string;
|
||||
value: string;
|
||||
unit?: string;
|
||||
}
|
||||
|
||||
export type CoasterStatValue = string | number | null;
|
||||
export type TechnicalSpecValue = string | number | null;
|
||||
40
src-old/types/composite-submission.ts
Normal file
40
src-old/types/composite-submission.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Type definitions for composite submissions
|
||||
* Used when creating multiple related entities in a single submission
|
||||
*/
|
||||
|
||||
import type { TempCompanyData } from './company';
|
||||
|
||||
/**
|
||||
* Composite submission structure for park creation with related entities
|
||||
*/
|
||||
export interface ParkCompositeSubmission {
|
||||
park: {
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string;
|
||||
park_type: string;
|
||||
status: string;
|
||||
opening_date?: string;
|
||||
closing_date?: string;
|
||||
location_id?: string;
|
||||
website_url?: string;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
operator_id?: string | null;
|
||||
property_owner_id?: string | null;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
new_operator?: TempCompanyData;
|
||||
new_property_owner?: TempCompanyData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic composite submission content type
|
||||
* Supports any entity type with optional related entities
|
||||
*/
|
||||
export interface CompositeSubmissionContent {
|
||||
[entityKey: string]: {
|
||||
[key: string]: unknown;
|
||||
} | TempCompanyData | undefined;
|
||||
}
|
||||
135
src-old/types/data-export.ts
Normal file
135
src-old/types/data-export.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Data Export Type Definitions
|
||||
*
|
||||
* Types for user data export, statistics, and activity logs.
|
||||
*/
|
||||
|
||||
/**
|
||||
* User statistics aggregated from various tables
|
||||
*/
|
||||
export interface UserStatistics {
|
||||
ride_count: number;
|
||||
coaster_count: number;
|
||||
park_count: number;
|
||||
review_count: number;
|
||||
reputation_score: number;
|
||||
photo_count: number;
|
||||
list_count: number;
|
||||
submission_count: number;
|
||||
account_created: string;
|
||||
last_updated: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual activity log entry from profile_audit_log
|
||||
*/
|
||||
export interface ActivityLogEntry {
|
||||
id: string;
|
||||
action: string;
|
||||
changes: Record<string, any>;
|
||||
created_at: string;
|
||||
changed_by: string;
|
||||
ip_address_hash?: string;
|
||||
user_agent?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Profile data for export
|
||||
*/
|
||||
export interface ExportProfileData {
|
||||
username: string;
|
||||
display_name: string | null;
|
||||
bio: string | null;
|
||||
preferred_pronouns: string | null;
|
||||
personal_location: string | null;
|
||||
timezone: string;
|
||||
preferred_language: string;
|
||||
theme_preference: string;
|
||||
privacy_level: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Review data for export
|
||||
*/
|
||||
export interface ExportReviewData {
|
||||
id: string;
|
||||
rating: number;
|
||||
review_text: string | null;
|
||||
ride_name?: string;
|
||||
park_name?: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* User list data for export
|
||||
*/
|
||||
export interface ExportListData {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
is_public: boolean;
|
||||
item_count: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete export data structure
|
||||
*/
|
||||
export interface ExportDataStructure {
|
||||
export_date: string;
|
||||
user_id: string;
|
||||
profile: ExportProfileData;
|
||||
statistics: UserStatistics;
|
||||
reviews: ExportReviewData[];
|
||||
lists: ExportListData[];
|
||||
activity_log: ActivityLogEntry[];
|
||||
preferences: {
|
||||
unit_preferences: any;
|
||||
accessibility_options: any;
|
||||
notification_preferences: any;
|
||||
privacy_settings: any;
|
||||
};
|
||||
metadata: {
|
||||
export_version: string;
|
||||
data_retention_info: string;
|
||||
instructions: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Export options
|
||||
*/
|
||||
export interface ExportOptions {
|
||||
include_reviews: boolean;
|
||||
include_lists: boolean;
|
||||
include_activity_log: boolean;
|
||||
include_preferences: boolean;
|
||||
format: 'json';
|
||||
}
|
||||
|
||||
/**
|
||||
* Export progress tracking
|
||||
*/
|
||||
export interface ExportProgress {
|
||||
stage: 'starting' | 'fetching_profile' | 'fetching_reviews' | 'fetching_lists' | 'fetching_activity' | 'packaging' | 'complete';
|
||||
progress: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data categories available for export
|
||||
*/
|
||||
export type DataCategory = 'profile' | 'reviews' | 'lists' | 'activity_log' | 'preferences';
|
||||
|
||||
/**
|
||||
* Export request result
|
||||
*/
|
||||
export interface ExportRequestResult {
|
||||
success: boolean;
|
||||
data?: ExportDataStructure;
|
||||
error?: string;
|
||||
rate_limited?: boolean;
|
||||
next_available_at?: string;
|
||||
}
|
||||
479
src-old/types/database.ts
Normal file
479
src-old/types/database.ts
Normal file
@@ -0,0 +1,479 @@
|
||||
export interface Location {
|
||||
id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
state_province?: string | null;
|
||||
city?: string | null;
|
||||
postal_code?: string | null;
|
||||
latitude?: number | null;
|
||||
longitude?: number | null;
|
||||
timezone?: string | null;
|
||||
}
|
||||
|
||||
export interface Company {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string | null;
|
||||
company_type: string; // Allow any string from database
|
||||
person_type?: string | null; // Database returns string, validated at form level
|
||||
website_url?: string | null;
|
||||
founded_year?: number | null; // Legacy field
|
||||
founded_date?: string | null;
|
||||
founded_date_precision?: string | null;
|
||||
headquarters_location?: string | null;
|
||||
logo_url?: string | null;
|
||||
banner_image_url?: string | null;
|
||||
banner_image_id?: string | null;
|
||||
card_image_url?: string | null;
|
||||
card_image_id?: string | null;
|
||||
average_rating: number | null;
|
||||
review_count: number | null;
|
||||
}
|
||||
|
||||
export interface Park {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string | null;
|
||||
status: string; // Allow any string from database
|
||||
park_type: string; // Allow any string from database
|
||||
opening_date?: string | null;
|
||||
opening_date_precision?: string | null;
|
||||
closing_date?: string | null;
|
||||
closing_date_precision?: string | null;
|
||||
website_url?: string | null;
|
||||
phone?: string | null;
|
||||
email?: string | null;
|
||||
location?: Location | null;
|
||||
operator?: Company | null;
|
||||
property_owner?: Company | null;
|
||||
banner_image_url?: string | null;
|
||||
banner_image_id?: string | null;
|
||||
card_image_url?: string | null;
|
||||
card_image_id?: string | null;
|
||||
average_rating: number | null;
|
||||
review_count: number | null;
|
||||
ride_count: number | null;
|
||||
coaster_count: number | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface RideTechnicalSpec {
|
||||
id: string;
|
||||
ride_id: string;
|
||||
spec_name: string;
|
||||
spec_value: string;
|
||||
spec_type: 'string' | 'number' | 'boolean' | 'date';
|
||||
category?: string;
|
||||
unit?: string;
|
||||
display_order: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface RideCoasterStat {
|
||||
id: string;
|
||||
ride_id: string;
|
||||
stat_name: string;
|
||||
stat_value: number;
|
||||
unit?: string;
|
||||
category?: string;
|
||||
description?: string;
|
||||
display_order: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface RideNameHistory {
|
||||
id: string;
|
||||
ride_id: string;
|
||||
former_name: string;
|
||||
date_changed?: string;
|
||||
date_changed_precision?: string;
|
||||
reason?: string;
|
||||
from_year?: number;
|
||||
to_year?: number;
|
||||
order_index: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface RideModelTechnicalSpec {
|
||||
id: string;
|
||||
ride_model_id: string;
|
||||
spec_name: string;
|
||||
spec_value: string;
|
||||
spec_type: 'string' | 'number' | 'boolean' | 'date';
|
||||
category?: string;
|
||||
unit?: string;
|
||||
display_order: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface RideModel {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
manufacturer_id: string;
|
||||
manufacturer?: Company;
|
||||
category: 'roller_coaster' | 'flat_ride' | 'water_ride' | 'dark_ride' | 'kiddie_ride' | 'transportation';
|
||||
ride_type?: string;
|
||||
description?: string;
|
||||
// Note: technical_specs deprecated - use ride_model_technical_specifications table
|
||||
// Load via useTechnicalSpecifications hook instead
|
||||
banner_image_url?: string;
|
||||
banner_image_id?: string;
|
||||
card_image_url?: string;
|
||||
card_image_id?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface Ride {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string | null;
|
||||
park?: Park | { name: string; slug: string; location?: Location }; // Allow partial park data
|
||||
ride_model?: RideModel;
|
||||
manufacturer?: Company | null;
|
||||
designer?: Company | null;
|
||||
category: string; // Allow any string from database
|
||||
ride_sub_type?: string | null; // Sub-category like "Flying Coaster", "Inverted Coaster", etc.
|
||||
status: string; // Allow any string from database
|
||||
opening_date?: string | null;
|
||||
opening_date_precision?: string | null;
|
||||
closing_date?: string | null;
|
||||
closing_date_precision?: string | null;
|
||||
height_requirement?: number | null;
|
||||
age_requirement?: number | null;
|
||||
capacity_per_hour?: number | null;
|
||||
duration_seconds?: number | null;
|
||||
max_speed_kmh?: number | null;
|
||||
max_height_meters?: number | null;
|
||||
length_meters?: number | null;
|
||||
inversions?: number | null;
|
||||
technical_specifications?: RideTechnicalSpec[];
|
||||
coaster_statistics?: RideCoasterStat[];
|
||||
name_history?: RideNameHistory[];
|
||||
average_rating: number | null;
|
||||
review_count: number | null;
|
||||
image_url?: string | null;
|
||||
banner_image_url?: string | null;
|
||||
banner_image_id?: string | null;
|
||||
card_image_url?: string | null;
|
||||
card_image_id?: string | null;
|
||||
// Roller coaster specific fields
|
||||
coaster_type?: string | null;
|
||||
seating_type?: string | null;
|
||||
intensity_level?: string | null;
|
||||
track_material?: string[] | null;
|
||||
support_material?: string[] | null;
|
||||
propulsion_method?: string[] | null;
|
||||
drop_height_meters?: number | null;
|
||||
max_g_force?: number | null;
|
||||
// Water ride specific fields
|
||||
water_depth_cm?: number | null;
|
||||
splash_height_meters?: number | null;
|
||||
wetness_level?: string | null;
|
||||
flume_type?: string | null;
|
||||
boat_capacity?: number | null;
|
||||
// Dark ride specific fields
|
||||
theme_name?: string | null;
|
||||
story_description?: string | null;
|
||||
show_duration_seconds?: number | null;
|
||||
animatronics_count?: number | null;
|
||||
projection_type?: string | null;
|
||||
ride_system?: string | null;
|
||||
scenes_count?: number | null;
|
||||
// Flat ride specific fields
|
||||
rotation_type?: string | null;
|
||||
motion_pattern?: string | null;
|
||||
platform_count?: number | null;
|
||||
swing_angle_degrees?: number | null;
|
||||
rotation_speed_rpm?: number | null;
|
||||
arm_length_meters?: number | null;
|
||||
max_height_reached_meters?: number | null;
|
||||
// Kiddie ride specific fields
|
||||
min_age?: number | null;
|
||||
max_age?: number | null;
|
||||
educational_theme?: string | null;
|
||||
character_theme?: string | null;
|
||||
// Transportation specific fields
|
||||
transport_type?: string | null;
|
||||
route_length_meters?: number | null;
|
||||
stations_count?: number | null;
|
||||
vehicle_capacity?: number | null;
|
||||
vehicles_count?: number | null;
|
||||
round_trip_duration_seconds?: number | null;
|
||||
}
|
||||
|
||||
export interface Profile {
|
||||
id: string;
|
||||
user_id: string;
|
||||
username: string;
|
||||
display_name?: string;
|
||||
bio?: string;
|
||||
avatar_url?: string;
|
||||
avatar_image_id?: string;
|
||||
preferred_pronouns?: string;
|
||||
show_pronouns?: boolean;
|
||||
timezone?: string;
|
||||
preferred_language?: string;
|
||||
location?: Location;
|
||||
location_id?: string;
|
||||
personal_location?: string;
|
||||
home_park_id?: string;
|
||||
date_of_birth?: string;
|
||||
privacy_level: 'public' | 'friends' | 'private';
|
||||
theme_preference: 'light' | 'dark' | 'system';
|
||||
ride_count: number;
|
||||
coaster_count: number;
|
||||
park_count: number;
|
||||
review_count: number;
|
||||
reputation_score: number;
|
||||
banned: boolean;
|
||||
deactivated: boolean;
|
||||
deactivated_at?: string;
|
||||
deactivation_reason?: string;
|
||||
oauth_provider?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface AccountDeletionRequest {
|
||||
id: string;
|
||||
user_id: string;
|
||||
requested_at: string;
|
||||
scheduled_deletion_at: string;
|
||||
confirmation_code: string;
|
||||
confirmation_code_sent_at?: string;
|
||||
status: 'pending' | 'confirmed' | 'cancelled' | 'completed'; // Account deletion specific status
|
||||
cancelled_at?: string;
|
||||
completed_at?: string;
|
||||
cancellation_reason?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface Review {
|
||||
id: string;
|
||||
user_id: string;
|
||||
park?: Park;
|
||||
ride?: Ride;
|
||||
rating: number;
|
||||
title?: string;
|
||||
content?: string;
|
||||
visit_date?: string;
|
||||
wait_time_minutes?: number;
|
||||
photos?: any;
|
||||
helpful_votes: number;
|
||||
total_votes: number;
|
||||
moderation_status: 'pending' | 'approved' | 'rejected' | 'flagged'; // Review moderation status
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface UserTopList {
|
||||
id: string;
|
||||
user_id: string;
|
||||
title: string;
|
||||
description?: string | null;
|
||||
list_type: 'parks' | 'rides' | 'coasters' | 'companies' | 'mixed';
|
||||
is_public: boolean | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
items?: UserTopListItem[]; // New relational data
|
||||
}
|
||||
|
||||
export interface UserTopListItem {
|
||||
id: string;
|
||||
list_id: string;
|
||||
entity_type: 'park' | 'ride' | 'company';
|
||||
entity_id: string;
|
||||
position: number;
|
||||
notes?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
// Populated via joins
|
||||
entity?: Park | Ride | Company;
|
||||
}
|
||||
|
||||
// Extended company interface with aggregated stats
|
||||
export interface CompanyWithStats extends Company {
|
||||
ride_count?: number;
|
||||
coaster_count?: number;
|
||||
model_count?: number;
|
||||
park_count?: number;
|
||||
}
|
||||
|
||||
// Audit log entry - matches actual profile_audit_log table structure
|
||||
export interface AuditLogEntry {
|
||||
id: string;
|
||||
user_id: string;
|
||||
changed_by: string;
|
||||
action: string;
|
||||
changes: Record<string, unknown> | null;
|
||||
ip_address_hash: string;
|
||||
user_agent: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
// Relational data structures (NO JSONB)
|
||||
export interface RideCoasterStat {
|
||||
id: string;
|
||||
ride_id: string;
|
||||
stat_name: string;
|
||||
stat_value: number;
|
||||
unit?: string;
|
||||
category?: string;
|
||||
description?: string;
|
||||
display_order: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface RideTechnicalSpecification {
|
||||
id: string;
|
||||
ride_id: string;
|
||||
spec_name: string;
|
||||
spec_value: string;
|
||||
spec_type: string;
|
||||
category?: string | null;
|
||||
unit?: string | null;
|
||||
display_order: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface RideModelTechnicalSpecification {
|
||||
id: string;
|
||||
ride_model_id: string;
|
||||
spec_name: string;
|
||||
spec_value: string;
|
||||
spec_type: string;
|
||||
category?: string | null;
|
||||
unit?: string | null;
|
||||
display_order: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ListItem {
|
||||
id: string;
|
||||
list_id: string;
|
||||
entity_type: 'park' | 'ride' | 'coaster';
|
||||
entity_id: string;
|
||||
position: number;
|
||||
notes?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
// User ride credit tracking
|
||||
export interface UserRideCredit {
|
||||
id: string;
|
||||
user_id: string;
|
||||
ride_id: string;
|
||||
first_ride_date?: string;
|
||||
last_ride_date?: string;
|
||||
ride_count: number;
|
||||
sort_order?: number;
|
||||
personal_notes?: string;
|
||||
personal_rating?: number;
|
||||
personal_photo_id?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
rides?: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
category: string;
|
||||
status: string;
|
||||
coaster_type?: string;
|
||||
seating_type?: string;
|
||||
intensity_level?: string;
|
||||
track_material?: string;
|
||||
max_speed_kmh?: number;
|
||||
max_height_meters?: number;
|
||||
length_meters?: number;
|
||||
inversions?: number;
|
||||
card_image_url?: string;
|
||||
average_rating: number;
|
||||
review_count: number;
|
||||
parks?: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
park_type: string;
|
||||
locations?: {
|
||||
country: string;
|
||||
state_province?: string;
|
||||
city?: string;
|
||||
};
|
||||
};
|
||||
manufacturer?: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
designer?: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Activity entry - discriminated union for different activity types
|
||||
export type ActivityEntry =
|
||||
| ReviewActivity
|
||||
| SubmissionActivity
|
||||
| RankingActivity
|
||||
| GenericActivity;
|
||||
|
||||
export interface ReviewActivity {
|
||||
id: string;
|
||||
type: 'review';
|
||||
created_at: string;
|
||||
rating?: number;
|
||||
parks?: { slug?: string; name?: string } | null;
|
||||
rides?: { slug?: string; name?: string; parks?: { slug?: string; name?: string } | null } | null;
|
||||
content?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface SubmissionActivity {
|
||||
id: string;
|
||||
type: 'submission';
|
||||
created_at: string;
|
||||
status?: string;
|
||||
submission_type?: string;
|
||||
entity_type?: string;
|
||||
action?: string;
|
||||
content?: {
|
||||
name?: string;
|
||||
slug?: string;
|
||||
description?: string;
|
||||
entity_id?: string;
|
||||
entity_slug?: string;
|
||||
park_slug?: string;
|
||||
park_name?: string;
|
||||
action?: string;
|
||||
};
|
||||
photo_count?: number;
|
||||
photo_preview?: string;
|
||||
}
|
||||
|
||||
export interface RankingActivity {
|
||||
id: string;
|
||||
type: 'ranking';
|
||||
created_at: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
list_type?: string;
|
||||
is_public?: boolean;
|
||||
}
|
||||
|
||||
export interface GenericActivity {
|
||||
id: string;
|
||||
type?: string;
|
||||
created_at: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
37
src-old/types/identity.ts
Normal file
37
src-old/types/identity.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Type definitions for user identity and OAuth provider management
|
||||
*/
|
||||
|
||||
export interface UserIdentity {
|
||||
id: string;
|
||||
user_id: string;
|
||||
identity_data: {
|
||||
email?: string;
|
||||
full_name?: string;
|
||||
avatar_url?: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
provider: 'google' | 'discord' | 'email' | 'github' | string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
last_sign_in_at: string;
|
||||
}
|
||||
|
||||
export type OAuthProvider = 'google' | 'discord';
|
||||
|
||||
export interface IdentitySafetyCheck {
|
||||
canDisconnect: boolean;
|
||||
reason?: 'last_identity' | 'no_password_backup' | 'safe';
|
||||
hasPasswordAuth: boolean;
|
||||
totalIdentities: number;
|
||||
oauthIdentities: number;
|
||||
}
|
||||
|
||||
export interface IdentityOperationResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
needsRelogin?: boolean;
|
||||
needsEmailConfirmation?: boolean;
|
||||
email?: string;
|
||||
requiresAAL2?: boolean;
|
||||
}
|
||||
84
src-old/types/location.ts
Normal file
84
src-old/types/location.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Location & Info Type Definitions
|
||||
*
|
||||
* Centralized types for location, personal info, accessibility, and unit preferences.
|
||||
*/
|
||||
|
||||
import type { UnitPreferences } from '@/lib/units';
|
||||
|
||||
/**
|
||||
* Accessibility options stored in user_preferences.accessibility_options
|
||||
*/
|
||||
export interface AccessibilityOptions {
|
||||
font_size: 'small' | 'medium' | 'large';
|
||||
high_contrast: boolean;
|
||||
reduced_motion: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Profile location and personal information
|
||||
*/
|
||||
export interface ProfileLocationInfo {
|
||||
personal_location: string | null;
|
||||
home_park_id: string | null;
|
||||
timezone: string;
|
||||
preferred_language: string;
|
||||
preferred_pronouns: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combined form data for Location tab
|
||||
*/
|
||||
export interface LocationFormData extends ProfileLocationInfo {}
|
||||
|
||||
/**
|
||||
* Park data for home park selection
|
||||
*/
|
||||
export interface ParkOption {
|
||||
id: string;
|
||||
name: string;
|
||||
location?: {
|
||||
city?: string;
|
||||
state_province?: string;
|
||||
country: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete location & info settings
|
||||
*/
|
||||
export interface LocationInfoSettings {
|
||||
profile: ProfileLocationInfo;
|
||||
accessibility: AccessibilityOptions;
|
||||
unitPreferences: UnitPreferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Location data structure
|
||||
*/
|
||||
export interface LocationData {
|
||||
street_address?: string;
|
||||
country?: string;
|
||||
state_province?: string;
|
||||
city?: string;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
postal_code?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard for location data
|
||||
*/
|
||||
export function isLocationData(data: unknown): data is LocationData {
|
||||
if (typeof data !== 'object' || data === null) return false;
|
||||
const loc = data as Record<string, unknown>;
|
||||
return (
|
||||
(loc.street_address === undefined || typeof loc.street_address === 'string') &&
|
||||
(loc.country === undefined || typeof loc.country === 'string') &&
|
||||
(loc.state_province === undefined || typeof loc.state_province === 'string') &&
|
||||
(loc.city === undefined || typeof loc.city === 'string') &&
|
||||
(loc.latitude === undefined || typeof loc.latitude === 'number') &&
|
||||
(loc.longitude === undefined || typeof loc.longitude === 'number') &&
|
||||
(loc.postal_code === undefined || typeof loc.postal_code === 'string')
|
||||
);
|
||||
}
|
||||
328
src-old/types/moderation.ts
Normal file
328
src-old/types/moderation.ts
Normal file
@@ -0,0 +1,328 @@
|
||||
/**
|
||||
* Moderation Queue Type Definitions
|
||||
*
|
||||
* This file contains all TypeScript types and interfaces used by the moderation queue system.
|
||||
* Extracted from ModerationQueue.tsx to improve maintainability and reusability.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Photo display interface for moderation queue
|
||||
*/
|
||||
export interface PhotoForDisplay {
|
||||
id: string;
|
||||
url: string;
|
||||
cloudflare_image_url: string;
|
||||
filename: string;
|
||||
caption?: string | null;
|
||||
title?: string | null;
|
||||
date_taken?: string | null;
|
||||
order_index: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Location data interface
|
||||
*/
|
||||
export interface LocationData {
|
||||
id: string;
|
||||
city?: string | null;
|
||||
state_province?: string | null;
|
||||
country: string;
|
||||
formatted_address?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Park submission item data
|
||||
*/
|
||||
export interface ParkItemData {
|
||||
park_id?: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string;
|
||||
park_type: string;
|
||||
status: string;
|
||||
location_id?: string;
|
||||
operator_id?: string;
|
||||
property_owner_id?: string;
|
||||
opening_date?: string;
|
||||
closing_date?: string;
|
||||
source_url?: string;
|
||||
submission_notes?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ride submission item data
|
||||
*/
|
||||
export interface RideItemData {
|
||||
ride_id?: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
park_id: string;
|
||||
manufacturer_id?: string;
|
||||
designer_id?: string;
|
||||
model_id?: string;
|
||||
ride_type: string;
|
||||
status: string;
|
||||
opening_date?: string;
|
||||
closing_date?: string;
|
||||
source_url?: string;
|
||||
submission_notes?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Company submission item data
|
||||
*/
|
||||
export interface CompanyItemData {
|
||||
company_id?: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
company_type: string;
|
||||
country?: string;
|
||||
founded_year?: number;
|
||||
source_url?: string;
|
||||
submission_notes?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ride model submission item data
|
||||
*/
|
||||
export interface RideModelItemData {
|
||||
model_id?: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
manufacturer_id: string;
|
||||
model_type: string;
|
||||
source_url?: string;
|
||||
submission_notes?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Photo submission item data
|
||||
*/
|
||||
export interface PhotoItemData {
|
||||
photo_url: string;
|
||||
cloudflare_image_id?: string;
|
||||
caption?: string;
|
||||
title?: string;
|
||||
order_index?: number;
|
||||
source_url?: string;
|
||||
submission_notes?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Union type for all submission item data
|
||||
*/
|
||||
export type SubmissionItemData =
|
||||
| ParkItemData
|
||||
| RideItemData
|
||||
| CompanyItemData
|
||||
| RideModelItemData
|
||||
| PhotoItemData;
|
||||
|
||||
/**
|
||||
* Submission item with resolved entity data from database view
|
||||
*/
|
||||
export interface SubmissionItem {
|
||||
id: string;
|
||||
item_type: string;
|
||||
action_type: 'create' | 'edit' | 'delete';
|
||||
status: string;
|
||||
order_index: number;
|
||||
depends_on: string | null;
|
||||
approved_entity_id: string | null;
|
||||
rejection_reason: string | null;
|
||||
|
||||
// Typed foreign key columns (one will be populated based on item_type)
|
||||
park_submission_id?: string | null;
|
||||
ride_submission_id?: string | null;
|
||||
photo_submission_id?: string | null;
|
||||
company_submission_id?: string | null;
|
||||
ride_model_submission_id?: string | null;
|
||||
timeline_event_submission_id?: string | null;
|
||||
|
||||
// Entity data from dynamic join (pre-loaded from view)
|
||||
entity_data?: {
|
||||
id: string;
|
||||
submission_id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
// Other fields vary by entity type
|
||||
[key: string]: any;
|
||||
} | null;
|
||||
|
||||
// Legacy support for moderation actions
|
||||
item_data?: SubmissionItemData;
|
||||
original_data?: SubmissionItemData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single item in the moderation queue.
|
||||
* Can be either a review or a content submission.
|
||||
*/
|
||||
export interface ModerationItem {
|
||||
/** Unique identifier for the item */
|
||||
id: string;
|
||||
|
||||
/** Type of moderation item */
|
||||
type: 'review' | 'content_submission';
|
||||
|
||||
/** Raw content data (structure varies by type) */
|
||||
content: any;
|
||||
|
||||
/** Timestamp when the item was created (primary field) */
|
||||
created_at: string;
|
||||
|
||||
/** Timestamp when the item was submitted (same as created_at) */
|
||||
submitted_at?: string;
|
||||
|
||||
/** Timestamp when the item was last updated */
|
||||
updated_at?: string;
|
||||
|
||||
/** ID of the user who submitted this item */
|
||||
user_id: string;
|
||||
|
||||
/** ID of the submitter (from view, same as user_id) */
|
||||
submitter_id?: string;
|
||||
|
||||
/** Current status of the item */
|
||||
status: string;
|
||||
|
||||
/** Type of submission (e.g., 'park', 'ride', 'review') */
|
||||
submission_type?: string;
|
||||
|
||||
/** Pre-loaded submitter profile from view (new structure) */
|
||||
submitter_profile?: {
|
||||
user_id: string;
|
||||
username: string;
|
||||
display_name?: string | null;
|
||||
avatar_url?: string | null;
|
||||
};
|
||||
|
||||
/** Pre-loaded reviewer profile from view (new structure) */
|
||||
reviewer_profile?: {
|
||||
user_id: string;
|
||||
username: string;
|
||||
display_name?: string | null;
|
||||
avatar_url?: string | null;
|
||||
};
|
||||
|
||||
/** Pre-loaded assigned moderator profile from view */
|
||||
assigned_profile?: {
|
||||
user_id: string;
|
||||
username: string;
|
||||
display_name?: string | null;
|
||||
avatar_url?: string | null;
|
||||
};
|
||||
|
||||
/** Legacy: Submitter (old field name for backward compatibility) */
|
||||
submitter?: {
|
||||
user_id: string;
|
||||
username: string;
|
||||
display_name?: string;
|
||||
avatar_url?: string;
|
||||
};
|
||||
|
||||
/** Legacy: Reviewer (old field name for backward compatibility) */
|
||||
reviewer?: {
|
||||
user_id: string;
|
||||
username: string;
|
||||
display_name?: string;
|
||||
avatar_url?: string;
|
||||
};
|
||||
|
||||
/** Legacy: Profile information of the submitting user */
|
||||
user_profile?: {
|
||||
username: string;
|
||||
display_name?: string | null;
|
||||
avatar_url?: string | null;
|
||||
};
|
||||
|
||||
/** Display name of the entity being modified */
|
||||
entity_name?: string;
|
||||
|
||||
/** Display name of the park (for ride submissions) */
|
||||
park_name?: string;
|
||||
|
||||
/** Timestamp when the item was reviewed */
|
||||
reviewed_at?: string;
|
||||
|
||||
/** ID of the moderator who reviewed this item */
|
||||
reviewer_id?: string;
|
||||
|
||||
/** Username of the moderator who reviewed this item */
|
||||
reviewed_by?: string;
|
||||
|
||||
/** Notes left by the reviewing moderator */
|
||||
reviewer_notes?: string;
|
||||
|
||||
/** Whether this submission has been escalated for senior review */
|
||||
escalated?: boolean;
|
||||
|
||||
/** ID of the moderator this item is assigned to */
|
||||
assigned_to?: string;
|
||||
|
||||
/** Timestamp until which this item is locked by a moderator */
|
||||
locked_until?: string;
|
||||
|
||||
/** Internal flag indicating item is being removed (optimistic update) */
|
||||
_removing?: boolean;
|
||||
|
||||
/** Pre-loaded submission items with entity data from view */
|
||||
submission_items?: SubmissionItem[];
|
||||
|
||||
/** Pre-loaded review photos from relational review_photos table */
|
||||
review_photos?: Array<{
|
||||
id: string;
|
||||
url: string;
|
||||
caption?: string | null;
|
||||
order_index: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter options for entity types in the moderation queue
|
||||
*/
|
||||
export type EntityFilter = 'all' | 'reviews' | 'submissions' | 'photos';
|
||||
|
||||
/**
|
||||
* Filter options for submission status in the moderation queue
|
||||
*/
|
||||
export type StatusFilter = 'all' | 'pending' | 'partially_approved' | 'flagged' | 'approved' | 'rejected';
|
||||
|
||||
/**
|
||||
* Available tabs in the moderation interface
|
||||
*/
|
||||
export type QueueTab = 'mainQueue' | 'archive';
|
||||
|
||||
/**
|
||||
* Available fields for sorting the moderation queue
|
||||
*/
|
||||
export type SortField = 'created_at' | 'submission_type' | 'status';
|
||||
|
||||
/**
|
||||
* Sort direction
|
||||
*/
|
||||
export type SortDirection = 'asc' | 'desc';
|
||||
|
||||
/**
|
||||
* Configuration for sorting the moderation queue
|
||||
*/
|
||||
export interface SortConfig {
|
||||
/** Field to sort by */
|
||||
field: SortField;
|
||||
/** Sort direction */
|
||||
direction: SortDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loading states for the moderation queue
|
||||
*/
|
||||
export type LoadingState = 'initial' | 'loading' | 'refreshing' | 'ready';
|
||||
|
||||
/**
|
||||
* Ref interface for the ModerationQueue component
|
||||
* Allows parent components to trigger actions imperatively
|
||||
*/
|
||||
export interface ModerationQueueRef {
|
||||
/** Manually refresh the queue data */
|
||||
refresh: () => void;
|
||||
}
|
||||
78
src-old/types/notifications.ts
Normal file
78
src-old/types/notifications.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Notification Type Definitions
|
||||
*
|
||||
* Centralized types for notification system.
|
||||
* Follows project patterns for type safety and validation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Channel preferences for receiving notifications
|
||||
*/
|
||||
export interface ChannelPreferences {
|
||||
in_app: boolean;
|
||||
email: boolean;
|
||||
push: boolean;
|
||||
sms: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-workflow notification preferences
|
||||
*/
|
||||
export interface WorkflowPreferences {
|
||||
[workflowId: string]: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification frequency control settings
|
||||
*/
|
||||
export interface FrequencySettings {
|
||||
digest: 'realtime' | 'hourly' | 'daily' | 'weekly';
|
||||
max_per_hour: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete notification preferences structure
|
||||
*/
|
||||
export interface NotificationPreferences {
|
||||
channelPreferences: ChannelPreferences;
|
||||
workflowPreferences: WorkflowPreferences;
|
||||
frequencySettings: FrequencySettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification template from database
|
||||
*/
|
||||
export interface NotificationTemplate {
|
||||
id: string;
|
||||
workflow_id: string;
|
||||
novu_workflow_id: string | null;
|
||||
name: string;
|
||||
description: string | null;
|
||||
category: string;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscriber data for Novu
|
||||
*/
|
||||
export interface SubscriberData {
|
||||
subscriberId: string;
|
||||
email?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
phone?: string;
|
||||
avatar?: string;
|
||||
data?: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification payload for triggering workflows
|
||||
*/
|
||||
export interface NotificationPayload {
|
||||
workflowId: string;
|
||||
subscriberId: string;
|
||||
payload: Record<string, any>;
|
||||
overrides?: Record<string, any>;
|
||||
}
|
||||
46
src-old/types/photo-submissions.ts
Normal file
46
src-old/types/photo-submissions.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
// TypeScript interfaces for the new photo submission relational tables
|
||||
|
||||
export interface PhotoSubmission {
|
||||
id: string;
|
||||
submission_id: string;
|
||||
entity_type: 'park' | 'ride' | 'manufacturer' | 'operator' | 'designer' | 'property_owner';
|
||||
entity_id: string;
|
||||
parent_id?: string;
|
||||
title?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface PhotoSubmissionItem {
|
||||
id: string;
|
||||
photo_submission_id: string;
|
||||
cloudflare_image_id: string;
|
||||
cloudflare_image_url: string;
|
||||
caption?: string | null;
|
||||
title?: string | null;
|
||||
filename?: string | null;
|
||||
order_index: number;
|
||||
file_size?: number | null;
|
||||
mime_type?: string | null;
|
||||
date_taken?: string | null;
|
||||
date_taken_precision?: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ReviewPhoto {
|
||||
id: string;
|
||||
review_id: string;
|
||||
cloudflare_image_id: string;
|
||||
cloudflare_image_url: string;
|
||||
caption?: string;
|
||||
order_index: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface PhotoSubmissionWithItems extends PhotoSubmission {
|
||||
items: PhotoSubmissionItem[];
|
||||
submission?: {
|
||||
user_id: string;
|
||||
status: string;
|
||||
};
|
||||
}
|
||||
53
src-old/types/photos.ts
Normal file
53
src-old/types/photos.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Photo-related type definitions
|
||||
*/
|
||||
|
||||
import type { PhotoSubmissionItem } from './photo-submissions';
|
||||
|
||||
// Re-export for convenience
|
||||
export type { PhotoSubmissionItem } from './photo-submissions';
|
||||
|
||||
// Core photo display interface
|
||||
export interface PhotoItem {
|
||||
id: string;
|
||||
url: string;
|
||||
filename: string;
|
||||
caption?: string | null;
|
||||
size?: number;
|
||||
type?: string;
|
||||
title?: string | null;
|
||||
date_taken?: string | null;
|
||||
}
|
||||
|
||||
// Normalized photo for consistent display
|
||||
export interface NormalizedPhoto {
|
||||
id: string;
|
||||
url: string;
|
||||
filename: string;
|
||||
caption?: string;
|
||||
title?: string;
|
||||
date_taken?: string;
|
||||
order_index: number;
|
||||
}
|
||||
|
||||
// Photo data source types
|
||||
export type PhotoDataSource =
|
||||
| { type: 'review'; photos: PhotoItem[] }
|
||||
| { type: 'submission_jsonb'; photos: PhotoItem[] }
|
||||
| { type: 'submission_items'; items: PhotoSubmissionItem[] };
|
||||
|
||||
// Type guard for photo arrays
|
||||
export function isPhotoItem(data: unknown): data is PhotoItem {
|
||||
return (
|
||||
typeof data === 'object' &&
|
||||
data !== null &&
|
||||
'id' in data &&
|
||||
'url' in data &&
|
||||
'filename' in data
|
||||
);
|
||||
}
|
||||
|
||||
// Type guard for photo arrays
|
||||
export function isPhotoItemArray(data: unknown): data is PhotoItem[] {
|
||||
return Array.isArray(data) && (data.length === 0 || isPhotoItem(data[0]));
|
||||
}
|
||||
58
src-old/types/privacy.ts
Normal file
58
src-old/types/privacy.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Privacy Types
|
||||
*
|
||||
* Centralized type definitions for user privacy settings.
|
||||
*
|
||||
* Storage:
|
||||
* - ProfilePrivacySettings: stored in profiles table
|
||||
* - PrivacySettings: stored in user_preferences.privacy_settings (JSONB)
|
||||
*
|
||||
* Security:
|
||||
* - All privacy settings are validated with Zod before database writes
|
||||
* - Changes are logged to profile_audit_log for compliance
|
||||
* - RLS policies ensure users can only modify their own settings
|
||||
*/
|
||||
|
||||
/**
|
||||
* Privacy settings stored in user_preferences.privacy_settings
|
||||
*/
|
||||
export interface PrivacySettings {
|
||||
activity_visibility: 'public' | 'private';
|
||||
search_visibility: boolean;
|
||||
show_location: boolean;
|
||||
show_age: boolean;
|
||||
show_avatar: boolean;
|
||||
show_bio: boolean;
|
||||
show_activity_stats: boolean;
|
||||
show_home_park: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Profile-level privacy settings
|
||||
*/
|
||||
export interface ProfilePrivacySettings {
|
||||
privacy_level: 'public' | 'private';
|
||||
show_pronouns: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combined form data for privacy tab
|
||||
*/
|
||||
export interface PrivacyFormData extends ProfilePrivacySettings, PrivacySettings {}
|
||||
|
||||
/**
|
||||
* User block information
|
||||
*/
|
||||
export interface UserBlock {
|
||||
id: string;
|
||||
blocker_id: string;
|
||||
blocked_id: string;
|
||||
reason?: string;
|
||||
created_at: string;
|
||||
blocked_profile?: {
|
||||
user_id: string;
|
||||
username: string;
|
||||
display_name?: string;
|
||||
avatar_url?: string;
|
||||
};
|
||||
}
|
||||
55
src-old/types/recharts.ts
Normal file
55
src-old/types/recharts.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Type definitions for Recharts payloads
|
||||
* Provides type-safe alternatives to `any` in chart components
|
||||
*/
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
/**
|
||||
* Generic chart payload item structure
|
||||
*/
|
||||
export interface ChartPayloadItem<T = unknown> {
|
||||
value?: number | string;
|
||||
name?: string;
|
||||
dataKey?: string;
|
||||
color?: string;
|
||||
fill?: string;
|
||||
payload?: T;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tooltip content props from Recharts
|
||||
*/
|
||||
export interface TooltipProps<T = unknown> {
|
||||
active?: boolean;
|
||||
payload?: ChartPayloadItem<T>[];
|
||||
label?: string | number;
|
||||
labelFormatter?: (value: unknown, payload: ChartPayloadItem<T>[]) => ReactNode;
|
||||
formatter?: (
|
||||
value: number | string,
|
||||
name: string,
|
||||
item: ChartPayloadItem<T>,
|
||||
index: number,
|
||||
payload: unknown
|
||||
) => ReactNode;
|
||||
className?: string;
|
||||
indicator?: 'line' | 'dot' | 'dashed';
|
||||
hideLabel?: boolean;
|
||||
hideIndicator?: boolean;
|
||||
color?: string;
|
||||
nameKey?: string;
|
||||
labelKey?: string;
|
||||
labelClassName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Legend content props from Recharts
|
||||
*/
|
||||
export interface LegendProps<T = unknown> {
|
||||
payload?: ChartPayloadItem<T>[];
|
||||
className?: string;
|
||||
hideIcon?: boolean;
|
||||
nameKey?: string;
|
||||
verticalAlign?: 'top' | 'bottom';
|
||||
}
|
||||
65
src-old/types/ride-credits.ts
Normal file
65
src-old/types/ride-credits.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
export interface RideCreditFilters {
|
||||
// Search
|
||||
searchQuery?: string;
|
||||
selectedSearchItems?: string[]; // Format: "ride:id", "park:id", "manufacturer:id"
|
||||
|
||||
// Category (Multi-select)
|
||||
categories?: string[];
|
||||
|
||||
// Geographic
|
||||
countries?: string[];
|
||||
statesProvinces?: string[];
|
||||
cities?: string[];
|
||||
|
||||
// Park
|
||||
parks?: string[];
|
||||
parkTypes?: string[];
|
||||
operators?: string[];
|
||||
propertyOwners?: string[];
|
||||
|
||||
// Ride Details
|
||||
manufacturers?: string[];
|
||||
designers?: string[];
|
||||
rideModels?: string[];
|
||||
coasterTypes?: string[];
|
||||
seatingTypes?: string[];
|
||||
intensityLevels?: string[];
|
||||
trackMaterial?: string[];
|
||||
|
||||
// Statistics
|
||||
minRideCount?: number;
|
||||
maxRideCount?: number;
|
||||
minSpeed?: number;
|
||||
maxSpeed?: number;
|
||||
minHeight?: number;
|
||||
maxHeight?: number;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
minInversions?: number;
|
||||
hasInversions?: boolean;
|
||||
|
||||
// Dates
|
||||
firstRideDateFrom?: Date;
|
||||
firstRideDateTo?: Date;
|
||||
lastRideDateFrom?: Date;
|
||||
lastRideDateTo?: Date;
|
||||
rideOpeningYearFrom?: number;
|
||||
rideOpeningYearTo?: number;
|
||||
|
||||
// User Experience
|
||||
hasNotes?: boolean;
|
||||
hasPhotos?: boolean;
|
||||
hasRating?: boolean;
|
||||
minUserRating?: number;
|
||||
|
||||
// Ride Status
|
||||
rideStatuses?: string[];
|
||||
}
|
||||
|
||||
export type FilterPreset =
|
||||
| 'mostRidden'
|
||||
| 'recentlyAdded'
|
||||
| 'singleRides'
|
||||
| 'needRating'
|
||||
| 'highlyRated'
|
||||
| 'all';
|
||||
27
src-old/types/ride-former-names.ts
Normal file
27
src-old/types/ride-former-names.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Ride Former Names Types
|
||||
* Relational replacement for JSONB former_names field
|
||||
*/
|
||||
|
||||
export interface RideFormerName {
|
||||
id: string;
|
||||
ride_id: string;
|
||||
name: string;
|
||||
used_from: string | null;
|
||||
used_until: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface RideFormerNameInsert {
|
||||
ride_id: string;
|
||||
name: string;
|
||||
used_from?: string | null;
|
||||
used_until?: string | null;
|
||||
}
|
||||
|
||||
export interface RideFormerNameUpdate {
|
||||
name?: string;
|
||||
used_from?: string | null;
|
||||
used_until?: string | null;
|
||||
}
|
||||
164
src-old/types/statuses.ts
Normal file
164
src-old/types/statuses.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* Type-Safe Status Enums
|
||||
* Provides exhaustive type checking for all entity statuses
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
// Submission statuses
|
||||
export type SubmissionStatus =
|
||||
| 'draft'
|
||||
| 'pending'
|
||||
| 'locked'
|
||||
| 'reviewing'
|
||||
| 'partially_approved'
|
||||
| 'approved'
|
||||
| 'rejected'
|
||||
| 'escalated';
|
||||
|
||||
export const SubmissionStatusSchema = z.enum([
|
||||
'draft',
|
||||
'pending',
|
||||
'locked',
|
||||
'reviewing',
|
||||
'partially_approved',
|
||||
'approved',
|
||||
'rejected',
|
||||
'escalated'
|
||||
]);
|
||||
|
||||
// Review statuses for individual items
|
||||
export type ReviewStatus =
|
||||
| 'pending'
|
||||
| 'approved'
|
||||
| 'rejected'
|
||||
| 'flagged'
|
||||
| 'skipped';
|
||||
|
||||
export const ReviewStatusSchema = z.enum([
|
||||
'pending',
|
||||
'approved',
|
||||
'rejected',
|
||||
'flagged',
|
||||
'skipped'
|
||||
]);
|
||||
|
||||
// Park operational statuses
|
||||
export type ParkStatus =
|
||||
| 'operating'
|
||||
| 'closed_permanently'
|
||||
| 'closed_temporarily'
|
||||
| 'under_construction'
|
||||
| 'planned'
|
||||
| 'abandoned';
|
||||
|
||||
export const ParkStatusSchema = z.enum([
|
||||
'operating',
|
||||
'closed_permanently',
|
||||
'closed_temporarily',
|
||||
'under_construction',
|
||||
'planned',
|
||||
'abandoned'
|
||||
]);
|
||||
|
||||
// Ride operational statuses
|
||||
export type RideStatus =
|
||||
| 'operating'
|
||||
| 'closed_permanently'
|
||||
| 'closed_temporarily'
|
||||
| 'under_construction'
|
||||
| 'relocated'
|
||||
| 'stored'
|
||||
| 'demolished';
|
||||
|
||||
export const RideStatusSchema = z.enum([
|
||||
'operating',
|
||||
'closed_permanently',
|
||||
'closed_temporarily',
|
||||
'under_construction',
|
||||
'relocated',
|
||||
'stored',
|
||||
'demolished'
|
||||
]);
|
||||
|
||||
// Account statuses
|
||||
export type AccountStatus =
|
||||
| 'active'
|
||||
| 'suspended'
|
||||
| 'pending_deletion'
|
||||
| 'deleted';
|
||||
|
||||
export const AccountStatusSchema = z.enum([
|
||||
'active',
|
||||
'suspended',
|
||||
'pending_deletion',
|
||||
'deleted'
|
||||
]);
|
||||
|
||||
// Lock statuses
|
||||
export type LockStatus =
|
||||
| 'unlocked'
|
||||
| 'locked'
|
||||
| 'expired';
|
||||
|
||||
export const LockStatusSchema = z.enum([
|
||||
'unlocked',
|
||||
'locked',
|
||||
'expired'
|
||||
]);
|
||||
|
||||
// Helper type guards
|
||||
export function isSubmissionStatus(value: unknown): value is SubmissionStatus {
|
||||
return SubmissionStatusSchema.safeParse(value).success;
|
||||
}
|
||||
|
||||
export function isReviewStatus(value: unknown): value is ReviewStatus {
|
||||
return ReviewStatusSchema.safeParse(value).success;
|
||||
}
|
||||
|
||||
export function isParkStatus(value: unknown): value is ParkStatus {
|
||||
return ParkStatusSchema.safeParse(value).success;
|
||||
}
|
||||
|
||||
export function isRideStatus(value: unknown): value is RideStatus {
|
||||
return RideStatusSchema.safeParse(value).success;
|
||||
}
|
||||
|
||||
// Status display helpers
|
||||
export const SUBMISSION_STATUS_LABELS: Record<SubmissionStatus, string> = {
|
||||
draft: 'Draft',
|
||||
pending: 'Pending Review',
|
||||
locked: 'Locked',
|
||||
reviewing: 'Under Review',
|
||||
partially_approved: 'Partially Approved',
|
||||
approved: 'Approved',
|
||||
rejected: 'Rejected',
|
||||
escalated: 'Escalated'
|
||||
};
|
||||
|
||||
export const REVIEW_STATUS_LABELS: Record<ReviewStatus, string> = {
|
||||
pending: 'Pending',
|
||||
approved: 'Approved',
|
||||
rejected: 'Rejected',
|
||||
flagged: 'Flagged',
|
||||
skipped: 'Skipped'
|
||||
};
|
||||
|
||||
export const PARK_STATUS_LABELS: Record<ParkStatus, string> = {
|
||||
operating: 'Operating',
|
||||
closed_permanently: 'Closed Permanently',
|
||||
closed_temporarily: 'Closed Temporarily',
|
||||
under_construction: 'Under Construction',
|
||||
planned: 'Planned',
|
||||
abandoned: 'Abandoned'
|
||||
};
|
||||
|
||||
export const RIDE_STATUS_LABELS: Record<RideStatus, string> = {
|
||||
operating: 'Operating',
|
||||
closed_permanently: 'Closed Permanently',
|
||||
closed_temporarily: 'Closed Temporarily',
|
||||
under_construction: 'Under Construction',
|
||||
relocated: 'Relocated',
|
||||
stored: 'Stored',
|
||||
demolished: 'Demolished'
|
||||
};
|
||||
166
src-old/types/submission-data.ts
Normal file
166
src-old/types/submission-data.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* Type definitions for submission data structures
|
||||
* These replace the `any` types in entityTransformers.ts
|
||||
*/
|
||||
|
||||
import type { LocationData } from './location';
|
||||
|
||||
export interface ParkSubmissionData {
|
||||
id?: string; // park_submission.id for location lookup
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string | null;
|
||||
park_type: string;
|
||||
status: string;
|
||||
opening_date?: string | null;
|
||||
opening_date_precision?: string | null;
|
||||
closing_date?: string | null;
|
||||
closing_date_precision?: string | null;
|
||||
website_url?: string | null;
|
||||
phone?: string | null;
|
||||
email?: string | null;
|
||||
operator_id?: string | null;
|
||||
property_owner_id?: string | null;
|
||||
location_id?: string | null;
|
||||
temp_location_data?: LocationData | null;
|
||||
banner_image_url?: string | null;
|
||||
banner_image_id?: string | null;
|
||||
card_image_url?: string | null;
|
||||
card_image_id?: string | null;
|
||||
source_url?: string;
|
||||
submission_notes?: string;
|
||||
is_test_data?: boolean;
|
||||
}
|
||||
|
||||
export interface RideSubmissionData {
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string | null;
|
||||
category: string;
|
||||
ride_sub_type?: string | null;
|
||||
status: string;
|
||||
park_id: string;
|
||||
ride_model_id?: string | null;
|
||||
manufacturer_id?: string | null;
|
||||
designer_id?: string | null;
|
||||
opening_date?: string | null;
|
||||
opening_date_precision?: string | null;
|
||||
closing_date?: string | null;
|
||||
closing_date_precision?: string | null;
|
||||
height_requirement?: number | null;
|
||||
age_requirement?: number | null;
|
||||
capacity_per_hour?: number | null;
|
||||
duration_seconds?: number | null;
|
||||
max_speed_kmh?: number | null;
|
||||
max_height_meters?: number | null;
|
||||
length_meters?: number | null;
|
||||
drop_height_meters?: number | null;
|
||||
inversions?: number | null;
|
||||
max_g_force?: number | null;
|
||||
coaster_type?: string | null;
|
||||
seating_type?: string | null;
|
||||
intensity_level?: string | null;
|
||||
track_material?: string[] | null;
|
||||
support_material?: string[] | null;
|
||||
propulsion_method?: string[] | null;
|
||||
// Water ride specific
|
||||
water_depth_cm?: number | null;
|
||||
splash_height_meters?: number | null;
|
||||
wetness_level?: string | null;
|
||||
flume_type?: string | null;
|
||||
boat_capacity?: number | null;
|
||||
// Dark ride specific
|
||||
theme_name?: string | null;
|
||||
story_description?: string | null;
|
||||
show_duration_seconds?: number | null;
|
||||
animatronics_count?: number | null;
|
||||
projection_type?: string | null;
|
||||
ride_system?: string | null;
|
||||
scenes_count?: number | null;
|
||||
// Flat ride specific
|
||||
rotation_type?: string | null;
|
||||
motion_pattern?: string | null;
|
||||
platform_count?: number | null;
|
||||
swing_angle_degrees?: number | null;
|
||||
rotation_speed_rpm?: number | null;
|
||||
arm_length_meters?: number | null;
|
||||
max_height_reached_meters?: number | null;
|
||||
// Kiddie ride specific
|
||||
min_age?: number | null;
|
||||
max_age?: number | null;
|
||||
educational_theme?: string | null;
|
||||
character_theme?: string | null;
|
||||
// Transportation ride specific
|
||||
transport_type?: string | null;
|
||||
route_length_meters?: number | null;
|
||||
stations_count?: number | null;
|
||||
vehicle_capacity?: number | null;
|
||||
vehicles_count?: number | null;
|
||||
round_trip_duration_seconds?: number | null;
|
||||
// Images
|
||||
banner_image_url?: string | null;
|
||||
banner_image_id?: string | null;
|
||||
card_image_url?: string | null;
|
||||
card_image_id?: string | null;
|
||||
image_url?: string | null;
|
||||
source_url?: string;
|
||||
submission_notes?: string;
|
||||
is_test_data?: boolean;
|
||||
}
|
||||
|
||||
export interface CompanySubmissionData {
|
||||
name: string;
|
||||
slug: string;
|
||||
company_type: 'manufacturer' | 'operator' | 'designer' | 'property_owner';
|
||||
description?: string | null;
|
||||
person_type?: 'company' | 'individual' | 'firm' | 'organization';
|
||||
founded_year?: number | null; // Legacy field
|
||||
founded_date?: string | null;
|
||||
founded_date_precision?: string | null;
|
||||
defunct_date?: string | null;
|
||||
defunct_date_precision?: string | null;
|
||||
headquarters_location?: string | null;
|
||||
website_url?: string | null;
|
||||
logo_url?: string | null;
|
||||
banner_image_url?: string | null;
|
||||
banner_image_id?: string | null;
|
||||
card_image_url?: string | null;
|
||||
card_image_id?: string | null;
|
||||
source_url?: string;
|
||||
submission_notes?: string;
|
||||
is_test_data?: boolean;
|
||||
}
|
||||
|
||||
export interface RideModelSubmissionData {
|
||||
name: string;
|
||||
slug: string;
|
||||
manufacturer_id: string;
|
||||
category: string;
|
||||
ride_type?: string | null;
|
||||
description?: string | null;
|
||||
banner_image_url?: string | null;
|
||||
banner_image_id?: string | null;
|
||||
card_image_url?: string | null;
|
||||
card_image_id?: string | null;
|
||||
source_url?: string;
|
||||
submission_notes?: string;
|
||||
is_test_data?: boolean;
|
||||
}
|
||||
|
||||
export interface TimelineEventItemData {
|
||||
entity_id: string;
|
||||
entity_type: 'park' | 'ride' | 'company' | 'ride_model';
|
||||
event_type: string;
|
||||
event_date: string; // ISO date
|
||||
event_date_precision: 'day' | 'month' | 'year';
|
||||
title: string;
|
||||
description?: string | null;
|
||||
from_value?: string | null;
|
||||
to_value?: string | null;
|
||||
from_entity_id?: string | null;
|
||||
to_entity_id?: string | null;
|
||||
from_location_id?: string | null;
|
||||
to_location_id?: string | null;
|
||||
display_order?: number;
|
||||
is_public?: boolean;
|
||||
}
|
||||
131
src-old/types/submission-item-data.ts
Normal file
131
src-old/types/submission-item-data.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Type-safe helpers for accessing submission item data
|
||||
* Provides type guards and type narrowing for Json fields
|
||||
*/
|
||||
|
||||
import type { Json } from '@/integrations/supabase/types';
|
||||
|
||||
/**
|
||||
* Base structure that all item_data objects should have
|
||||
*/
|
||||
export interface BaseItemData {
|
||||
name?: string;
|
||||
slug?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to safely check if Json is an object with a name property
|
||||
*/
|
||||
export function hasName(data: Json): data is { name: string } & Record<string, Json> {
|
||||
return typeof data === 'object' && data !== null && !Array.isArray(data) && 'name' in data && typeof (data as Record<string, Json>).name === 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if Json is a photos array
|
||||
*/
|
||||
export function hasPhotos(data: Json): data is { photos: Array<Record<string, Json>> } & Record<string, Json> {
|
||||
return typeof data === 'object' && data !== null && !Array.isArray(data) && 'photos' in data && Array.isArray((data as Record<string, Json>).photos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard for manufacturer data
|
||||
*/
|
||||
export function hasManufacturer(data: Json): data is { manufacturer_id: string; manufacturer_name: string } & Record<string, Json> {
|
||||
return (
|
||||
typeof data === 'object' &&
|
||||
data !== null &&
|
||||
!Array.isArray(data) &&
|
||||
'manufacturer_id' in data &&
|
||||
'manufacturer_name' in data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard for park data
|
||||
*/
|
||||
export function hasParkId(data: Json): data is { park_id: string } & Record<string, Json> {
|
||||
return typeof data === 'object' && data !== null && !Array.isArray(data) && 'park_id' in data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard for ride data
|
||||
*/
|
||||
export function hasRideId(data: Json): data is { ride_id: string } & Record<string, Json> {
|
||||
return typeof data === 'object' && data !== null && !Array.isArray(data) && 'ride_id' in data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard for company data
|
||||
*/
|
||||
export function hasCompanyId(data: Json): data is { company_id: string } & Record<string, Json> {
|
||||
return typeof data === 'object' && data !== null && !Array.isArray(data) && 'company_id' in data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard for ride model data
|
||||
*/
|
||||
export function hasRideModelId(data: Json): data is { ride_model_id: string } & Record<string, Json> {
|
||||
return typeof data === 'object' && data !== null && !Array.isArray(data) && 'ride_model_id' in data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely get name from item_data or entity_data
|
||||
* Works with both Json (legacy) and pre-loaded entity_data from view
|
||||
*/
|
||||
export function getItemName(data: Json | Record<string, any> | null | undefined): string {
|
||||
if (!data) return 'Unnamed';
|
||||
|
||||
// Handle entity_data object (from view)
|
||||
if (typeof data === 'object' && !Array.isArray(data) && 'name' in data) {
|
||||
return String(data.name);
|
||||
}
|
||||
|
||||
// Handle Json (legacy)
|
||||
if (hasName(data)) {
|
||||
return data.name;
|
||||
}
|
||||
|
||||
return 'Unnamed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely get photos from item_data or entity_data
|
||||
*/
|
||||
export function getItemPhotos(data: Json | Record<string, any> | null | undefined): Array<Record<string, Json>> {
|
||||
if (!data) return [];
|
||||
|
||||
// Handle entity_data object (from view)
|
||||
if (typeof data === 'object' && !Array.isArray(data) && 'photos' in data && Array.isArray(data.photos)) {
|
||||
return data.photos as Array<Record<string, Json>>;
|
||||
}
|
||||
|
||||
// Handle Json (legacy)
|
||||
if (hasPhotos(data)) {
|
||||
return data.photos;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Json to a record for form usage
|
||||
* Only use when you need to pass data to form components
|
||||
*/
|
||||
export function jsonToRecord(data: Json): Record<string, Json> {
|
||||
if (typeof data === 'object' && data !== null && !Array.isArray(data)) {
|
||||
return data as Record<string, Json>;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Type-safe way to access nested Json properties
|
||||
*/
|
||||
export function getProperty<T = Json>(data: Json, key: string): T | undefined {
|
||||
if (typeof data === 'object' && data !== null && !Array.isArray(data)) {
|
||||
const obj = data as Record<string, Json>;
|
||||
return obj[key] as T | undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
153
src-old/types/submissions.ts
Normal file
153
src-old/types/submissions.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
export type EntityType =
|
||||
| 'park'
|
||||
| 'ride'
|
||||
| 'manufacturer'
|
||||
| 'operator'
|
||||
| 'designer'
|
||||
| 'property_owner'
|
||||
| 'photo_edit'
|
||||
| 'photo_delete'
|
||||
| 'milestone'
|
||||
| 'timeline_event';
|
||||
|
||||
export interface PhotoSubmission {
|
||||
url: string;
|
||||
caption?: string;
|
||||
title?: string;
|
||||
date?: string;
|
||||
order: number;
|
||||
}
|
||||
|
||||
export interface PhotoSubmissionContent {
|
||||
title?: string;
|
||||
photos: PhotoSubmission[];
|
||||
context: EntityType;
|
||||
entity_id: string;
|
||||
// Legacy support
|
||||
park_id?: string;
|
||||
ride_id?: string;
|
||||
company_id?: string;
|
||||
}
|
||||
|
||||
export interface SubmissionItemData {
|
||||
id: string;
|
||||
submission_id: string;
|
||||
item_type: EntityType | 'photo' | 'ride_model';
|
||||
action_type: 'create' | 'edit' | 'delete';
|
||||
item_data: Record<string, unknown>;
|
||||
original_data?: Record<string, unknown>;
|
||||
status: 'pending' | 'approved' | 'rejected' | 'flagged' | 'skipped'; // Use ReviewStatus type
|
||||
depends_on: string | null;
|
||||
order_index: number;
|
||||
approved_entity_id: string | null;
|
||||
rejection_reason: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface EntityPhotoGalleryProps {
|
||||
entityId: string;
|
||||
entityType: EntityType;
|
||||
entityName: string;
|
||||
parentId?: string; // e.g., parkId for a ride
|
||||
}
|
||||
|
||||
export interface UppyPhotoSubmissionUploadProps {
|
||||
onSubmissionComplete?: () => void;
|
||||
entityId: string;
|
||||
entityType: EntityType;
|
||||
parentId?: string; // Optional parent (e.g., parkId for rides)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces minimal content structure for content_submissions.content
|
||||
* This type prevents accidental JSON blob storage
|
||||
*
|
||||
* RULE: content should ONLY contain action + max 2 reference IDs
|
||||
*/
|
||||
export interface ContentSubmissionContent {
|
||||
action: 'create' | 'edit' | 'delete';
|
||||
// Only reference IDs allowed - no actual data
|
||||
[key: string]: string | null | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to validate content structure
|
||||
* Prevents JSON blob violations at runtime
|
||||
*/
|
||||
export function isValidSubmissionContent(content: any): content is ContentSubmissionContent {
|
||||
if (!content || typeof content !== 'object') {
|
||||
// Security: Use logger instead of console.error to prevent PII exposure
|
||||
import('@/lib/logger').then(({ logger }) => {
|
||||
logger.error('Submission content validation failed', {
|
||||
violation: 'invalid_type',
|
||||
expected: 'object',
|
||||
received: typeof content
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!['create', 'edit', 'delete'].includes(content.action)) {
|
||||
import('@/lib/logger').then(({ logger }) => {
|
||||
logger.error('Submission content validation failed', {
|
||||
violation: 'invalid_action',
|
||||
expected: 'create | edit | delete',
|
||||
received: content.action
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const keys = Object.keys(content);
|
||||
if (keys.length > 3) {
|
||||
import('@/lib/logger').then(({ logger }) => {
|
||||
logger.error('Submission content validation failed', {
|
||||
violation: 'too_many_fields',
|
||||
count: keys.length,
|
||||
limit: 3
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for common violations
|
||||
const forbiddenKeys = ['name', 'description', 'photos', 'data', 'items', 'metadata'];
|
||||
const violations = keys.filter(k => forbiddenKeys.includes(k));
|
||||
if (violations.length > 0) {
|
||||
import('@/lib/logger').then(({ logger }) => {
|
||||
logger.error('Submission content validation failed', {
|
||||
violation: 'forbidden_keys',
|
||||
forbiddenKeys: violations,
|
||||
message: 'These should be in submission_items.item_data instead'
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create safe submission content
|
||||
* Use this to ensure compliance with the pattern
|
||||
*/
|
||||
export function createSubmissionContent(
|
||||
action: 'create' | 'edit' | 'delete',
|
||||
referenceIds: Record<string, string> = {}
|
||||
): ContentSubmissionContent {
|
||||
const idKeys = Object.keys(referenceIds);
|
||||
|
||||
if (idKeys.length > 2) {
|
||||
throw new Error(
|
||||
`Too many reference IDs (${idKeys.length}). Maximum is 2. ` +
|
||||
`Put additional data in submission_items.item_data instead.`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
action,
|
||||
...referenceIds
|
||||
};
|
||||
}
|
||||
|
||||
24
src-old/types/supabase-auth.ts
Normal file
24
src-old/types/supabase-auth.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Extended Supabase Auth Types
|
||||
* Supabase auth methods accept options objects not fully typed in official SDK
|
||||
*/
|
||||
|
||||
export interface SignInOptions {
|
||||
email: string;
|
||||
password: string;
|
||||
options?: {
|
||||
captchaToken?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SignUpOptions {
|
||||
email: string;
|
||||
password: string;
|
||||
options?: {
|
||||
data?: {
|
||||
username?: string;
|
||||
display_name?: string;
|
||||
};
|
||||
captchaToken?: string;
|
||||
};
|
||||
}
|
||||
35
src-old/types/supabase-session.ts
Normal file
35
src-old/types/supabase-session.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Extended Supabase Session Types
|
||||
*
|
||||
* Supabase session objects contain additional properties not in the official types.
|
||||
* These extended types provide type safety for AAL (Authentication Assurance Level) and other fields.
|
||||
*/
|
||||
|
||||
import type { Session } from '@supabase/supabase-js';
|
||||
|
||||
export type AALLevel = 'aal1' | 'aal2';
|
||||
|
||||
/**
|
||||
* Extended session type with AAL property
|
||||
* Supabase auth sessions include this property but it's not in the official types
|
||||
*/
|
||||
export interface ExtendedSession extends Session {
|
||||
aal?: AALLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if session has AAL property
|
||||
*/
|
||||
export function hasAAL(session: Session | null): session is ExtendedSession {
|
||||
return session !== null && 'aal' in session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely extract AAL level from session
|
||||
*/
|
||||
export function getSessionAAL(session: Session | null): AALLevel {
|
||||
if (hasAAL(session) && session.aal) {
|
||||
return session.aal;
|
||||
}
|
||||
return 'aal1';
|
||||
}
|
||||
84
src-old/types/timeline.ts
Normal file
84
src-old/types/timeline.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Timeline Event Types
|
||||
*
|
||||
* Type definitions for entity timeline/historical milestone system.
|
||||
* All timeline events flow through moderation queue before being stored.
|
||||
*/
|
||||
|
||||
export type TimelineEventType =
|
||||
| 'name_change'
|
||||
| 'operator_change'
|
||||
| 'owner_change'
|
||||
| 'location_change'
|
||||
| 'status_change'
|
||||
| 'opening'
|
||||
| 'closure'
|
||||
| 'reopening'
|
||||
| 'renovation'
|
||||
| 'expansion'
|
||||
| 'acquisition'
|
||||
| 'milestone'
|
||||
| 'other';
|
||||
|
||||
export type EntityType = 'park' | 'ride' | 'company' | 'ride_model';
|
||||
|
||||
export type DatePrecision = 'day' | 'month' | 'year';
|
||||
|
||||
/**
|
||||
* Timeline event stored in database after approval
|
||||
*/
|
||||
export interface TimelineEvent {
|
||||
id: string;
|
||||
entity_id: string;
|
||||
entity_type: EntityType;
|
||||
event_type: TimelineEventType;
|
||||
event_date: string;
|
||||
event_date_precision: DatePrecision;
|
||||
title: string;
|
||||
description?: string;
|
||||
|
||||
// Type-specific relational data
|
||||
from_value?: string;
|
||||
to_value?: string;
|
||||
from_entity_id?: string;
|
||||
to_entity_id?: string;
|
||||
from_location_id?: string;
|
||||
to_location_id?: string;
|
||||
|
||||
// Metadata
|
||||
display_order: number;
|
||||
created_by?: string;
|
||||
approved_by?: string;
|
||||
submission_id?: string;
|
||||
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form data for creating/editing timeline events
|
||||
*/
|
||||
export interface TimelineEventFormData {
|
||||
event_type: TimelineEventType;
|
||||
event_date: Date;
|
||||
event_date_precision: DatePrecision;
|
||||
title: string;
|
||||
description?: string;
|
||||
|
||||
// Conditional fields based on event_type
|
||||
from_value?: string;
|
||||
to_value?: string;
|
||||
from_entity_id?: string;
|
||||
to_entity_id?: string;
|
||||
from_location_id?: string;
|
||||
to_location_id?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete submission data for timeline events
|
||||
* Includes entity reference and all form data
|
||||
*/
|
||||
export interface TimelineSubmissionData extends TimelineEventFormData {
|
||||
entity_id: string;
|
||||
entity_type: EntityType;
|
||||
}
|
||||
207
src-old/types/versioning.ts
Normal file
207
src-old/types/versioning.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
/**
|
||||
* Universal Versioning System Types
|
||||
* Pure relational structure, no JSONB storage
|
||||
*/
|
||||
|
||||
export type EntityType = 'park' | 'ride' | 'company' | 'ride_model';
|
||||
|
||||
export type ChangeType = 'created' | 'updated' | 'deleted' | 'restored' | 'archived';
|
||||
|
||||
/**
|
||||
* Base version metadata common to all entity types
|
||||
* NOTE: Using version_id as the primary identifier (not 'id')
|
||||
* This matches the relational version table structure
|
||||
*/
|
||||
export interface BaseVersion {
|
||||
version_id: string;
|
||||
version_number: number;
|
||||
created_at: string;
|
||||
created_by: string | null;
|
||||
change_type: ChangeType;
|
||||
change_reason: string | null;
|
||||
submission_id: string | null;
|
||||
is_current: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extended version with profile data joined
|
||||
*/
|
||||
export interface BaseVersionWithProfile extends BaseVersion {
|
||||
profiles?: {
|
||||
username: string | null;
|
||||
display_name: string | null;
|
||||
avatar_url: string | null;
|
||||
} | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Park Version - exact mirror of parks table structure
|
||||
*/
|
||||
export interface ParkVersion extends BaseVersionWithProfile {
|
||||
park_id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
park_type: string;
|
||||
status: string;
|
||||
location_id: string | null;
|
||||
operator_id: string | null;
|
||||
property_owner_id: string | null;
|
||||
opening_date: string | null;
|
||||
closing_date: string | null;
|
||||
opening_date_precision: string | null;
|
||||
closing_date_precision: string | null;
|
||||
website_url: string | null;
|
||||
phone: string | null;
|
||||
email: string | null;
|
||||
banner_image_url: string | null;
|
||||
banner_image_id: string | null;
|
||||
card_image_url: string | null;
|
||||
card_image_id: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ride Version - exact mirror of ride_versions table structure
|
||||
* FIXED: Added missing fields, removed JSONB former_names
|
||||
*/
|
||||
export interface RideVersion extends BaseVersionWithProfile {
|
||||
ride_id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
category: string;
|
||||
status: string;
|
||||
park_id: string | null;
|
||||
manufacturer_id: string | null;
|
||||
designer_id: string | null;
|
||||
ride_model_id: string | null;
|
||||
opening_date: string | null;
|
||||
closing_date: string | null;
|
||||
opening_date_precision: string | null;
|
||||
closing_date_precision: string | null;
|
||||
height_requirement_cm: number | null;
|
||||
age_requirement: number | null;
|
||||
max_speed_kmh: number | null;
|
||||
duration_seconds: number | null;
|
||||
capacity_per_hour: number | null;
|
||||
gforce_max: number | null;
|
||||
inversions_count: number | null;
|
||||
length_meters: number | null;
|
||||
height_meters: number | null;
|
||||
drop_meters: number | null;
|
||||
banner_image_url: string | null;
|
||||
banner_image_id: string | null;
|
||||
card_image_url: string | null;
|
||||
card_image_id: string | null;
|
||||
image_url: string | null;
|
||||
ride_sub_type: string | null;
|
||||
coaster_type: string | null;
|
||||
seating_type: string | null;
|
||||
intensity_level: string | null;
|
||||
track_material: string[] | null;
|
||||
support_material: string[] | null;
|
||||
propulsion_method: string[] | null;
|
||||
// Water Ride Fields
|
||||
water_depth_cm: number | null;
|
||||
splash_height_meters: number | null;
|
||||
wetness_level: string | null;
|
||||
flume_type: string | null;
|
||||
boat_capacity: number | null;
|
||||
// Dark Ride Fields
|
||||
theme_name: string | null;
|
||||
story_description: string | null;
|
||||
show_duration_seconds: number | null;
|
||||
animatronics_count: number | null;
|
||||
projection_type: string | null;
|
||||
ride_system: string | null;
|
||||
scenes_count: number | null;
|
||||
// Flat Ride Fields
|
||||
rotation_type: string | null;
|
||||
motion_pattern: string | null;
|
||||
platform_count: number | null;
|
||||
swing_angle_degrees: number | null;
|
||||
rotation_speed_rpm: number | null;
|
||||
arm_length_meters: number | null;
|
||||
max_height_reached_meters: number | null;
|
||||
// Kiddie Ride Fields
|
||||
min_age: number | null;
|
||||
max_age: number | null;
|
||||
educational_theme: string | null;
|
||||
character_theme: string | null;
|
||||
// Transportation Fields
|
||||
transport_type: string | null;
|
||||
route_length_meters: number | null;
|
||||
stations_count: number | null;
|
||||
vehicle_capacity: number | null;
|
||||
vehicles_count: number | null;
|
||||
round_trip_duration_seconds: number | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Company Version - exact mirror of companies table structure
|
||||
*/
|
||||
export interface CompanyVersion extends BaseVersionWithProfile {
|
||||
company_id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
company_type: string;
|
||||
person_type: string | null;
|
||||
founded_year: number | null;
|
||||
founded_date: string | null;
|
||||
founded_date_precision: string | null;
|
||||
headquarters_location: string | null;
|
||||
website_url: string | null;
|
||||
logo_url: string | null;
|
||||
banner_image_url: string | null;
|
||||
banner_image_id: string | null;
|
||||
card_image_url: string | null;
|
||||
card_image_id: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ride Model Version - exact mirror of ride_models table structure
|
||||
*/
|
||||
export interface RideModelVersion extends BaseVersionWithProfile {
|
||||
ride_model_id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
manufacturer_id: string | null;
|
||||
category: string;
|
||||
ride_type: string;
|
||||
description: string | null;
|
||||
banner_image_url: string | null;
|
||||
banner_image_id: string | null;
|
||||
card_image_url: string | null;
|
||||
card_image_id: string | null;
|
||||
// Note: technical_specs removed - use ride_model_technical_specifications table
|
||||
}
|
||||
|
||||
/**
|
||||
* Discriminated union of all entity version types
|
||||
*/
|
||||
export type EntityVersion = ParkVersion | RideVersion | CompanyVersion | RideModelVersion;
|
||||
|
||||
/**
|
||||
* Version comparison diff structure
|
||||
*/
|
||||
export interface VersionDiff {
|
||||
[fieldName: string]: {
|
||||
from: any;
|
||||
to: any;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Version summary for list display
|
||||
*/
|
||||
export interface VersionSummary {
|
||||
version_id: string;
|
||||
version_number: number;
|
||||
created_at: string;
|
||||
created_by: string | null;
|
||||
change_type: ChangeType;
|
||||
changed_by_username?: string;
|
||||
changed_by_display_name?: string;
|
||||
entity_name: string;
|
||||
}
|
||||
Reference in New Issue
Block a user