= {
export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: ParkFormProps) {
const { isModerator } = useUserRole();
- const [submissionState, dispatch] = useReducer(submissionReducer, {
- status: 'draft' as const,
- data: initialData || {}
- });
// Validate that onSubmit uses submission helpers (dev mode only)
useEffect(() => {
@@ -184,18 +179,7 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
});
- const handleFormSubmit = async (data: ParkFormData) => {
- if (!canSubmit(submissionState)) {
- toast({
- title: 'Cannot submit',
- description: 'Please wait for the current operation to complete',
- variant: 'destructive',
- });
- return;
- }
-
- dispatch({ type: 'VALIDATE', payload: data });
-
+ const handleFormSubmit = async (data: ParkFormData) => {
try {
dispatch({ type: 'SUBMIT', payload: { submissionId: crypto.randomUUID() } });
@@ -535,12 +519,11 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
{/* Form Actions */}
{onCancel && (
diff --git a/src/components/admin/PropertyOwnerForm.tsx b/src/components/admin/PropertyOwnerForm.tsx
index 69906868..afd2207c 100644
--- a/src/components/admin/PropertyOwnerForm.tsx
+++ b/src/components/admin/PropertyOwnerForm.tsx
@@ -1,9 +1,8 @@
-import { useState, useReducer } from 'react';
+import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { entitySchemas } from '@/lib/entityValidationSchemas';
-import { submissionReducer, canSubmit } from '@/lib/submissionStateMachine';
import { getErrorMessage } from '@/lib/errorHandler';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -62,10 +61,6 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO
const { headquarters } = useCompanyHeadquarters();
const { user } = useAuth();
const navigate = useNavigate();
- const [submissionState, dispatch] = useReducer(submissionReducer, {
- status: 'draft' as const,
- data: initialData || {}
- });
const {
register,
@@ -104,16 +99,7 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO
return;
}
- if (!canSubmit(submissionState)) {
- toast.error('Cannot submit in current state');
- return;
- }
-
- dispatch({ type: 'VALIDATE', payload: data });
-
- try {
- dispatch({ type: 'SUBMIT', payload: { submissionId: crypto.randomUUID() } });
-
+ try {
const formData = {
...data,
company_type: 'property_owner' as const,
@@ -122,20 +108,12 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO
await onSubmit(formData);
- dispatch({ type: 'SUBMISSION_COMPLETE' });
-
// Only show success toast and close if not editing through moderation queue
if (!initialData?.id) {
toast.success('Property owner submitted for review');
onCancel();
}
} catch (error: unknown) {
- const errorMessage = getErrorMessage(error);
- if (errorMessage.includes('validation')) {
- dispatch({ type: 'VALIDATION_ERROR', payload: [{ field: 'general', message: errorMessage }] });
- } else {
- dispatch({ type: 'RESET' });
- }
handleError(error, {
action: initialData?.id ? 'Update Property Owner' : 'Create Property Owner',
metadata: { companyName: data.name }
@@ -262,18 +240,16 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO
diff --git a/src/components/admin/RideForm.tsx b/src/components/admin/RideForm.tsx
index 72b8a7e9..7a435df0 100644
--- a/src/components/admin/RideForm.tsx
+++ b/src/components/admin/RideForm.tsx
@@ -1,9 +1,8 @@
-import { useState, useEffect, useReducer } from 'react';
+import { useState, useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { validateSubmissionHandler } from '@/lib/entityFormValidation';
-import { submissionReducer, canSubmit } from '@/lib/submissionStateMachine';
import { getErrorMessage } from '@/lib/errorHandler';
import type { RideTechnicalSpec, RideCoasterStat, RideNameHistory } from '@/types/database';
import type { TempCompanyData, TempRideModelData } from '@/types/company';
@@ -121,10 +120,6 @@ const STATUS_DB_TO_DISPLAY: Record = {
export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }: RideFormProps) {
const { isModerator } = useUserRole();
- const [submissionState, dispatch] = useReducer(submissionReducer, {
- status: 'draft' as const,
- data: initialData || {}
- });
const { preferences } = useUnitPreferences();
const measurementSystem = preferences.measurement_system;
@@ -225,20 +220,8 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
const selectedCategory = watch('category');
- const handleFormSubmit = async (data: RideFormData) => {
- if (!canSubmit(submissionState)) {
- toast({
- title: 'Cannot submit',
- description: 'Please wait for the current operation to complete',
- variant: 'destructive',
- });
- return;
- }
-
- dispatch({ type: 'VALIDATE', payload: data });
-
+ const handleFormSubmit = async (data: RideFormData) => {
try {
- dispatch({ type: 'SUBMIT', payload: { submissionId: crypto.randomUUID() } });
// Convert form values back to metric for storage
const metricData = {
@@ -270,8 +253,6 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
// Pass clean data to parent with extended fields
await onSubmit(metricData);
- dispatch({ type: 'SUBMISSION_COMPLETE' });
-
toast({
title: isEditing ? "Ride Updated" : "Submission Sent",
description: isEditing
@@ -281,12 +262,6 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
: "Ride submitted for review"
});
} catch (error: unknown) {
- const errorMessage = getErrorMessage(error);
- if (errorMessage.includes('validation') || errorMessage.includes('required')) {
- dispatch({ type: 'VALIDATION_ERROR', payload: [{ field: 'general', message: errorMessage }] });
- } else {
- dispatch({ type: 'RESET' });
- }
handleError(error, {
action: isEditing ? 'Update Ride' : 'Create Ride',
metadata: {
@@ -806,12 +781,11 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
{/* Form Actions */}
{onCancel && (
diff --git a/src/components/admin/RideModelForm.tsx b/src/components/admin/RideModelForm.tsx
index 2851157b..c17bbf44 100644
--- a/src/components/admin/RideModelForm.tsx
+++ b/src/components/admin/RideModelForm.tsx
@@ -1,11 +1,11 @@
-import { useState, useReducer } from 'react';
+import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { Button } from '@/components/ui/button';
import type { RideModelTechnicalSpec } from '@/types/database';
-import { submissionReducer, canSubmit } from '@/lib/submissionStateMachine';
import { getErrorMessage } from '@/lib/errorHandler';
+import { handleError } from '@/lib/errorHandler';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Label } from '@/components/ui/label';
@@ -77,10 +77,6 @@ export function RideModelForm({
unit?: string;
display_order: number;
}[]>([]);
- const [submissionState, dispatch] = useReducer(submissionReducer, {
- status: 'draft' as const,
- data: initialData || {}
- });
const {
register,
@@ -101,30 +97,17 @@ export function RideModelForm({
});
- const handleFormSubmit = (data: RideModelFormData) => {
- if (!canSubmit(submissionState)) {
- return;
- }
-
- dispatch({ type: 'VALIDATE', payload: data });
-
+ const handleFormSubmit = (data: RideModelFormData) => {
try {
- dispatch({ type: 'SUBMIT', payload: { submissionId: crypto.randomUUID() } });
-
// Include relational technical specs with extended type
onSubmit({
...data,
_technical_specifications: technicalSpecs
});
-
- dispatch({ type: 'SUBMISSION_COMPLETE' });
} catch (error: unknown) {
- const errorMessage = getErrorMessage(error);
- if (errorMessage.includes('validation')) {
- dispatch({ type: 'VALIDATION_ERROR', payload: [{ field: 'general', message: errorMessage }] });
- } else {
- dispatch({ type: 'RESET' });
- }
+ handleError(error, {
+ action: initialData?.id ? 'Update Ride Model' : 'Create Ride Model'
+ });
}
};
@@ -248,17 +231,15 @@ export function RideModelForm({
type="button"
variant="outline"
onClick={onCancel}
- disabled={submissionState.status === 'validating' || submissionState.status === 'submitting'}
>
Cancel
diff --git a/src/lib/moderation/lockMonitor.ts b/src/lib/moderation/lockMonitor.ts
index 89c422d1..69bb49dc 100644
--- a/src/lib/moderation/lockMonitor.ts
+++ b/src/lib/moderation/lockMonitor.ts
@@ -45,11 +45,18 @@ export function useLockMonitor(
dispatch({ type: 'LOCK_EXPIRED' });
// Show toast with extension option
- toast({
- title: 'Lock Expiring Soon',
- description: 'Your lock on this submission will expire in less than 2 minutes. Click to extend.',
- variant: 'default',
- });
+ toast({
+ title: 'Lock Expiring Soon',
+ description: 'Your lock on this submission will expire in less than 2 minutes. Click below to extend.',
+ duration: Infinity,
+ });
+
+ // Also call extension function automatically after showing toast
+ if (itemId) {
+ setTimeout(() => {
+ handleExtendLock(itemId, dispatch);
+ }, 100);
+ }
}
}, 30000); // Check every 30 seconds
diff --git a/src/lib/submissionStateMachine.ts b/src/lib/submissionStateMachine.ts
index c0135580..8091ae45 100644
--- a/src/lib/submissionStateMachine.ts
+++ b/src/lib/submissionStateMachine.ts
@@ -64,7 +64,7 @@ export function submissionReducer(
}
return {
status: 'validation_error',
- data: (state as Extract).data,
+ data: state.data,
errors: action.payload
};
@@ -74,7 +74,7 @@ export function submissionReducer(
}
return {
status: 'submitting',
- data: (state as Extract).data,
+ data: state.data,
submissionId: action.payload.submissionId
};
@@ -84,7 +84,7 @@ export function submissionReducer(
}
return {
status: 'pending_moderation',
- submissionId: (state as Extract).submissionId
+ submissionId: state.submissionId
};
case 'LOCK':
@@ -93,7 +93,7 @@ export function submissionReducer(
}
return {
status: 'locked',
- submissionId: (state as Extract).submissionId,
+ submissionId: state.submissionId,
lockedBy: action.payload.lockedBy,
lockedUntil: action.payload.lockedUntil
};
@@ -104,7 +104,7 @@ export function submissionReducer(
}
return {
status: 'reviewing',
- submissionId: (state as Extract).submissionId,
+ submissionId: state.submissionId,
reviewerId: action.payload.reviewerId
};
@@ -114,7 +114,7 @@ export function submissionReducer(
}
return {
status: 'approved',
- submissionId: (state as Extract).submissionId,
+ submissionId: state.submissionId,
entityId: action.payload.entityId
};
@@ -124,7 +124,7 @@ export function submissionReducer(
}
return {
status: 'rejected',
- submissionId: (state as Extract).submissionId,
+ submissionId: state.submissionId,
reason: action.payload.reason
};
@@ -132,13 +132,10 @@ export function submissionReducer(
if (state.status !== 'reviewing' && state.status !== 'locked') {
throw new Error(`Illegal transition: ${state.status} → escalated`);
}
- const submissionId = state.status === 'reviewing'
- ? (state as Extract).submissionId
- : (state as Extract).submissionId;
return {
status: 'escalated',
- submissionId,
+ submissionId: state.submissionId,
escalatedBy: action.payload.escalatedBy,
reason: action.payload.reason
};