Implement all phases sequentially

This commit is contained in:
gpt-engineer-app[bot]
2025-10-21 12:18:38 +00:00
parent a05c1017d3
commit 1138eea024
10 changed files with 77 additions and 54 deletions

View File

@@ -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 && (

View File

@@ -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 && (

View File

@@ -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
}); });

View File

@@ -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(),

View File

@@ -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('')),

View File

@@ -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(),
}) })

View File

@@ -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(),

View File

@@ -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,

View File

@@ -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;
} }

View File

@@ -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;