mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 09:31:12 -05:00
Implement all phases sequentially
This commit is contained in:
@@ -101,13 +101,32 @@ const parkTypes = [
|
|||||||
|
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
'Operating',
|
'Operating',
|
||||||
'Seasonal',
|
|
||||||
'Closed Temporarily',
|
'Closed Temporarily',
|
||||||
'Closed Permanently',
|
'Closed Permanently',
|
||||||
'Under Construction',
|
'Under Construction',
|
||||||
'Planned'
|
'Planned',
|
||||||
|
'Abandoned'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Status mappings
|
||||||
|
const STATUS_DISPLAY_TO_DB: Record<string, string> = {
|
||||||
|
'Operating': 'operating',
|
||||||
|
'Closed Temporarily': 'closed_temporarily',
|
||||||
|
'Closed Permanently': 'closed_permanently',
|
||||||
|
'Under Construction': 'under_construction',
|
||||||
|
'Planned': 'planned',
|
||||||
|
'Abandoned': 'abandoned'
|
||||||
|
};
|
||||||
|
|
||||||
|
const STATUS_DB_TO_DISPLAY: Record<string, string> = {
|
||||||
|
'operating': 'Operating',
|
||||||
|
'closed_temporarily': 'Closed Temporarily',
|
||||||
|
'closed_permanently': 'Closed Permanently',
|
||||||
|
'under_construction': 'Under Construction',
|
||||||
|
'planned': 'Planned',
|
||||||
|
'abandoned': 'Abandoned'
|
||||||
|
};
|
||||||
|
|
||||||
export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: ParkFormProps) {
|
export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: ParkFormProps) {
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
@@ -146,7 +165,7 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
slug: initialData?.slug || '',
|
slug: initialData?.slug || '',
|
||||||
description: initialData?.description || '',
|
description: initialData?.description || '',
|
||||||
park_type: initialData?.park_type || '',
|
park_type: initialData?.park_type || '',
|
||||||
status: initialData?.status || 'Operating',
|
status: initialData?.status || 'operating' as const, // Store DB value
|
||||||
opening_date: initialData?.opening_date || '',
|
opening_date: initialData?.opening_date || '',
|
||||||
closing_date: initialData?.closing_date || '',
|
closing_date: initialData?.closing_date || '',
|
||||||
location_id: initialData?.location_id || undefined,
|
location_id: initialData?.location_id || undefined,
|
||||||
@@ -275,16 +294,22 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Status *</Label>
|
<Label>Status *</Label>
|
||||||
<Select onValueChange={(value) => setValue('status', value)} defaultValue={initialData?.status || 'Operating'}>
|
<Select
|
||||||
|
onValueChange={(value) => setValue('status', value as any)}
|
||||||
|
defaultValue={initialData?.status || 'operating'}
|
||||||
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select status" />
|
<SelectValue placeholder="Select status" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{statusOptions.map((status) => (
|
{statusOptions.map((displayStatus) => {
|
||||||
<SelectItem key={status} value={status}>
|
const dbValue = STATUS_DISPLAY_TO_DB[displayStatus];
|
||||||
{status}
|
return (
|
||||||
|
<SelectItem key={dbValue} value={dbValue}>
|
||||||
|
{displayStatus}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
{errors.status && (
|
{errors.status && (
|
||||||
|
|||||||
@@ -64,12 +64,12 @@ const categories = [
|
|||||||
|
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
'Operating',
|
'Operating',
|
||||||
'Seasonal',
|
|
||||||
'Closed Temporarily',
|
'Closed Temporarily',
|
||||||
'Closed Permanently',
|
'Closed Permanently',
|
||||||
'Under Construction',
|
'Under Construction',
|
||||||
'Planned',
|
'Relocated',
|
||||||
'SBNO' // Standing But Not Operating
|
'Stored',
|
||||||
|
'Demolished'
|
||||||
];
|
];
|
||||||
|
|
||||||
const coasterTypes = [
|
const coasterTypes = [
|
||||||
@@ -99,20 +99,22 @@ const intensityLevels = [
|
|||||||
// Status value mappings between display (form) and database values
|
// Status value mappings between display (form) and database values
|
||||||
const STATUS_DISPLAY_TO_DB: Record<string, string> = {
|
const STATUS_DISPLAY_TO_DB: Record<string, string> = {
|
||||||
'Operating': 'operating',
|
'Operating': 'operating',
|
||||||
'Seasonal': 'operating',
|
'Closed Temporarily': 'closed_temporarily',
|
||||||
'Closed Temporarily': 'maintenance',
|
'Closed Permanently': 'closed_permanently',
|
||||||
'Closed Permanently': 'closed',
|
|
||||||
'Under Construction': 'under_construction',
|
'Under Construction': 'under_construction',
|
||||||
'Planned': 'under_construction',
|
'Relocated': 'relocated',
|
||||||
'SBNO': 'sbno'
|
'Stored': 'stored',
|
||||||
|
'Demolished': 'demolished'
|
||||||
};
|
};
|
||||||
|
|
||||||
const STATUS_DB_TO_DISPLAY: Record<string, string> = {
|
const STATUS_DB_TO_DISPLAY: Record<string, string> = {
|
||||||
'operating': 'Operating',
|
'operating': 'Operating',
|
||||||
'closed': 'Closed Permanently',
|
'closed_permanently': 'Closed Permanently',
|
||||||
|
'closed_temporarily': 'Closed Temporarily',
|
||||||
'under_construction': 'Under Construction',
|
'under_construction': 'Under Construction',
|
||||||
'maintenance': 'Closed Temporarily',
|
'relocated': 'Relocated',
|
||||||
'sbno': 'SBNO'
|
'stored': 'Stored',
|
||||||
|
'demolished': 'Demolished'
|
||||||
};
|
};
|
||||||
|
|
||||||
export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }: RideFormProps) {
|
export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }: RideFormProps) {
|
||||||
@@ -180,9 +182,7 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
description: initialData?.description || '',
|
description: initialData?.description || '',
|
||||||
category: initialData?.category || '',
|
category: initialData?.category || '',
|
||||||
ride_sub_type: initialData?.ride_sub_type || '',
|
ride_sub_type: initialData?.ride_sub_type || '',
|
||||||
status: initialData?.status
|
status: initialData?.status || 'operating' as const, // Store DB value directly
|
||||||
? STATUS_DB_TO_DISPLAY[initialData.status] || 'Operating'
|
|
||||||
: 'Operating',
|
|
||||||
opening_date: initialData?.opening_date || '',
|
opening_date: initialData?.opening_date || '',
|
||||||
opening_date_precision: initialData?.opening_date_precision || 'day',
|
opening_date_precision: initialData?.opening_date_precision || 'day',
|
||||||
closing_date: initialData?.closing_date || '',
|
closing_date: initialData?.closing_date || '',
|
||||||
@@ -223,13 +223,10 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
const handleFormSubmit = async (data: RideFormData) => {
|
const handleFormSubmit = async (data: RideFormData) => {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
try {
|
||||||
// Transform status from display value to DB value
|
|
||||||
const dbStatus = STATUS_DISPLAY_TO_DB[data.status] || 'operating';
|
|
||||||
|
|
||||||
// Convert form values back to metric for storage
|
// Convert form values back to metric for storage
|
||||||
const metricData = {
|
const metricData = {
|
||||||
...data,
|
...data,
|
||||||
status: dbStatus,
|
// Status is already in DB format
|
||||||
height_requirement: data.height_requirement
|
height_requirement: data.height_requirement
|
||||||
? convertValueToMetric(data.height_requirement, getDisplayUnit('cm', measurementSystem))
|
? convertValueToMetric(data.height_requirement, getDisplayUnit('cm', measurementSystem))
|
||||||
: undefined,
|
: undefined,
|
||||||
@@ -354,21 +351,21 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Status *</Label>
|
<Label>Status *</Label>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={(value) => setValue('status', value)}
|
onValueChange={(value) => setValue('status', value as any)}
|
||||||
defaultValue={initialData?.status
|
defaultValue={initialData?.status || 'operating'}
|
||||||
? STATUS_DB_TO_DISPLAY[initialData.status] || 'Operating'
|
|
||||||
: 'Operating'
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select status" />
|
<SelectValue placeholder="Select status" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{statusOptions.map((status) => (
|
{statusOptions.map((displayStatus) => {
|
||||||
<SelectItem key={status} value={status}>
|
const dbValue = STATUS_DISPLAY_TO_DB[displayStatus];
|
||||||
{status}
|
return (
|
||||||
|
<SelectItem key={dbValue} value={dbValue}>
|
||||||
|
{displayStatus}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
{errors.status && (
|
{errors.status && (
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export async function submitCompanyCreation(
|
|||||||
content: {
|
content: {
|
||||||
action: 'create'
|
action: 'create'
|
||||||
},
|
},
|
||||||
status: 'pending'
|
status: 'pending' as const
|
||||||
})
|
})
|
||||||
.select()
|
.select()
|
||||||
.single();
|
.single();
|
||||||
@@ -64,7 +64,7 @@ export async function submitCompanyCreation(
|
|||||||
company_type: companyType,
|
company_type: companyType,
|
||||||
images: processedImages as unknown as Json
|
images: processedImages as unknown as Json
|
||||||
},
|
},
|
||||||
status: 'pending',
|
status: 'pending' as const,
|
||||||
order_index: 0
|
order_index: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ export async function submitCompanyUpdate(
|
|||||||
action: 'edit',
|
action: 'edit',
|
||||||
company_id: companyId
|
company_id: companyId
|
||||||
},
|
},
|
||||||
status: 'pending'
|
status: 'pending' as const
|
||||||
})
|
})
|
||||||
.select()
|
.select()
|
||||||
.single();
|
.single();
|
||||||
@@ -143,7 +143,7 @@ export async function submitCompanyUpdate(
|
|||||||
images: processedImages as unknown as Json
|
images: processedImages as unknown as Json
|
||||||
},
|
},
|
||||||
original_data: JSON.parse(JSON.stringify(existingCompany)),
|
original_data: JSON.parse(JSON.stringify(existingCompany)),
|
||||||
status: 'pending',
|
status: 'pending' as const,
|
||||||
order_index: 0
|
order_index: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export async function resolveConflicts(
|
|||||||
async function linkToExistingEntity(itemId: string, entityId: string): Promise<void> {
|
async function linkToExistingEntity(itemId: string, entityId: string): Promise<void> {
|
||||||
await updateSubmissionItem(itemId, {
|
await updateSubmissionItem(itemId, {
|
||||||
approved_entity_id: entityId,
|
approved_entity_id: entityId,
|
||||||
status: 'approved',
|
status: 'approved' as const,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ async function cascadeRejectDependents(
|
|||||||
// Reject all collected dependents
|
// Reject all collected dependents
|
||||||
for (const depId of toReject) {
|
for (const depId of toReject) {
|
||||||
await updateSubmissionItem(depId, {
|
await updateSubmissionItem(depId, {
|
||||||
status: 'rejected',
|
status: 'rejected' as const,
|
||||||
rejection_reason: 'Parent dependency was rejected',
|
rejection_reason: 'Parent dependency was rejected',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -152,7 +152,7 @@ async function escalateForAdminReview(
|
|||||||
const { error } = await supabase
|
const { error } = await supabase
|
||||||
.from('content_submissions')
|
.from('content_submissions')
|
||||||
.update({
|
.update({
|
||||||
status: 'pending',
|
status: 'pending' as const,
|
||||||
escalation_reason: reason,
|
escalation_reason: reason,
|
||||||
escalated_by: userId,
|
escalated_by: userId,
|
||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const parkValidationSchema = z.object({
|
|||||||
slug: z.string().trim().min(1, 'Slug is required').regex(/^[a-z0-9-]+$/, 'Slug must contain only lowercase letters, numbers, and hyphens'),
|
slug: z.string().trim().min(1, 'Slug is required').regex(/^[a-z0-9-]+$/, 'Slug must contain only lowercase letters, numbers, and hyphens'),
|
||||||
description: z.string().trim().max(2000, 'Description must be less than 2000 characters').optional().or(z.literal('')),
|
description: z.string().trim().max(2000, 'Description must be less than 2000 characters').optional().or(z.literal('')),
|
||||||
park_type: z.string().min(1, 'Park type is required'),
|
park_type: z.string().min(1, 'Park type is required'),
|
||||||
status: z.string().min(1, 'Status is required'),
|
status: z.enum(['operating', 'closed_permanently', 'closed_temporarily', 'under_construction', 'planned', 'abandoned']),
|
||||||
opening_date: z.string().optional().or(z.literal('')).refine((val) => {
|
opening_date: z.string().optional().or(z.literal('')).refine((val) => {
|
||||||
if (!val) return true;
|
if (!val) return true;
|
||||||
const date = new Date(val);
|
const date = new Date(val);
|
||||||
@@ -73,7 +73,7 @@ export const rideValidationSchema = z.object({
|
|||||||
description: z.string().trim().max(2000, 'Description must be less than 2000 characters').optional().or(z.literal('')),
|
description: z.string().trim().max(2000, 'Description must be less than 2000 characters').optional().or(z.literal('')),
|
||||||
category: z.string().min(1, 'Category is required'),
|
category: z.string().min(1, 'Category is required'),
|
||||||
ride_sub_type: z.string().trim().max(100, 'Sub type must be less than 100 characters').optional().or(z.literal('')),
|
ride_sub_type: z.string().trim().max(100, 'Sub type must be less than 100 characters').optional().or(z.literal('')),
|
||||||
status: z.string().min(1, 'Status is required'),
|
status: z.enum(['operating', 'closed_permanently', 'closed_temporarily', 'under_construction', 'relocated', 'stored', 'demolished']),
|
||||||
park_id: z.string().uuid().optional().nullable(),
|
park_id: z.string().uuid().optional().nullable(),
|
||||||
designer_id: z.string().uuid().optional().nullable(),
|
designer_id: z.string().uuid().optional().nullable(),
|
||||||
opening_date: z.string().optional().or(z.literal('')),
|
opening_date: z.string().optional().or(z.literal('')),
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ export async function approvePhotoSubmission(
|
|||||||
const { error: updateError } = await supabase
|
const { error: updateError } = await supabase
|
||||||
.from('content_submissions')
|
.from('content_submissions')
|
||||||
.update({
|
.update({
|
||||||
status: 'approved',
|
status: 'approved' as const,
|
||||||
reviewer_id: config.moderatorId,
|
reviewer_id: config.moderatorId,
|
||||||
reviewed_at: new Date().toISOString(),
|
reviewed_at: new Date().toISOString(),
|
||||||
reviewer_notes: config.moderatorNotes,
|
reviewer_notes: config.moderatorNotes,
|
||||||
@@ -239,7 +239,7 @@ export async function rejectSubmissionItems(
|
|||||||
const { error: rejectError } = await supabase
|
const { error: rejectError } = await supabase
|
||||||
.from('submission_items')
|
.from('submission_items')
|
||||||
.update({
|
.update({
|
||||||
status: 'rejected',
|
status: 'rejected' as const,
|
||||||
rejection_reason: rejectionReason || 'Parent submission rejected',
|
rejection_reason: rejectionReason || 'Parent submission rejected',
|
||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const parkRuntimeSchema = z.object({
|
|||||||
slug: z.string(),
|
slug: z.string(),
|
||||||
description: z.string().nullable().optional(),
|
description: z.string().nullable().optional(),
|
||||||
park_type: z.string(),
|
park_type: z.string(),
|
||||||
status: z.string(),
|
status: z.enum(['operating', 'closed_permanently', 'closed_temporarily', 'under_construction', 'planned', 'abandoned']),
|
||||||
opening_date: z.string().nullable().optional(),
|
opening_date: z.string().nullable().optional(),
|
||||||
opening_date_precision: z.string().nullable().optional(),
|
opening_date_precision: z.string().nullable().optional(),
|
||||||
closing_date: z.string().nullable().optional(),
|
closing_date: z.string().nullable().optional(),
|
||||||
@@ -51,7 +51,7 @@ export const rideRuntimeSchema = z.object({
|
|||||||
description: z.string().nullable().optional(),
|
description: z.string().nullable().optional(),
|
||||||
category: z.string(),
|
category: z.string(),
|
||||||
ride_sub_type: z.string().nullable().optional(),
|
ride_sub_type: z.string().nullable().optional(),
|
||||||
status: z.string(),
|
status: z.enum(['operating', 'closed_permanently', 'closed_temporarily', 'under_construction', 'relocated', 'stored', 'demolished']),
|
||||||
park_id: z.string().uuid().nullable().optional(),
|
park_id: z.string().uuid().nullable().optional(),
|
||||||
manufacturer_id: z.string().uuid().nullable().optional(),
|
manufacturer_id: z.string().uuid().nullable().optional(),
|
||||||
designer_id: z.string().uuid().nullable().optional(),
|
designer_id: z.string().uuid().nullable().optional(),
|
||||||
|
|||||||
@@ -738,7 +738,7 @@ export default function RideDetail() {
|
|||||||
description: ride.description,
|
description: ride.description,
|
||||||
category: ride.category,
|
category: ride.category,
|
||||||
ride_sub_type: ride.ride_sub_type,
|
ride_sub_type: ride.ride_sub_type,
|
||||||
status: ride.status,
|
status: ride.status as any, // Type assertion for DB status
|
||||||
opening_date: ride.opening_date,
|
opening_date: ride.opening_date,
|
||||||
closing_date: ride.closing_date,
|
closing_date: ride.closing_date,
|
||||||
height_requirement: ride.height_requirement,
|
height_requirement: ride.height_requirement,
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ export interface AccountDeletionRequest {
|
|||||||
scheduled_deletion_at: string;
|
scheduled_deletion_at: string;
|
||||||
confirmation_code: string;
|
confirmation_code: string;
|
||||||
confirmation_code_sent_at?: string;
|
confirmation_code_sent_at?: string;
|
||||||
status: 'pending' | 'confirmed' | 'cancelled' | 'completed';
|
status: 'pending' | 'confirmed' | 'cancelled' | 'completed'; // Account deletion specific status
|
||||||
cancelled_at?: string;
|
cancelled_at?: string;
|
||||||
completed_at?: string;
|
completed_at?: string;
|
||||||
cancellation_reason?: string;
|
cancellation_reason?: string;
|
||||||
@@ -232,7 +232,7 @@ export interface Review {
|
|||||||
photos?: any;
|
photos?: any;
|
||||||
helpful_votes: number;
|
helpful_votes: number;
|
||||||
total_votes: number;
|
total_votes: number;
|
||||||
moderation_status: 'pending' | 'approved' | 'rejected' | 'flagged';
|
moderation_status: 'pending' | 'approved' | 'rejected' | 'flagged'; // Review moderation status
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,10 @@ export interface SubmissionItemData {
|
|||||||
id: string;
|
id: string;
|
||||||
submission_id: string;
|
submission_id: string;
|
||||||
item_type: EntityType | 'photo' | 'ride_model';
|
item_type: EntityType | 'photo' | 'ride_model';
|
||||||
|
action_type: 'create' | 'edit' | 'delete';
|
||||||
item_data: Record<string, unknown>;
|
item_data: Record<string, unknown>;
|
||||||
original_data?: Record<string, unknown>;
|
original_data?: Record<string, unknown>;
|
||||||
status: 'pending' | 'approved' | 'rejected';
|
status: 'pending' | 'approved' | 'rejected' | 'flagged' | 'skipped'; // Use ReviewStatus type
|
||||||
depends_on: string | null;
|
depends_on: string | null;
|
||||||
order_index: number;
|
order_index: number;
|
||||||
approved_entity_id: string | null;
|
approved_entity_id: string | null;
|
||||||
|
|||||||
Reference in New Issue
Block a user