mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 11:11:14 -05:00
Refactor composite submission logic
This commit is contained in:
@@ -5,7 +5,7 @@ import * as z from 'zod';
|
||||
import { validateSubmissionHandler } from '@/lib/entityFormValidation';
|
||||
import { getErrorMessage } from '@/lib/errorHandler';
|
||||
import type { RideTechnicalSpec, RideCoasterStat, RideNameHistory } from '@/types/database';
|
||||
import type { TempCompanyData, TempRideModelData } from '@/types/company';
|
||||
import type { TempCompanyData, TempRideModelData, TempParkData } from '@/types/company';
|
||||
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -23,13 +23,14 @@ import { SlugField } from '@/components/ui/slug-field';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { toast } from '@/hooks/use-toast';
|
||||
import { handleError } from '@/lib/errorHandler';
|
||||
import { Plus, Zap, Save, X } from 'lucide-react';
|
||||
import { Plus, Zap, Save, X, Building2 } from 'lucide-react';
|
||||
import { toDateOnly, parseDateOnly } from '@/lib/dateUtils';
|
||||
import { useUnitPreferences } from '@/hooks/useUnitPreferences';
|
||||
import { useManufacturers, useRideModels } from '@/hooks/useAutocompleteData';
|
||||
import { useUserRole } from '@/hooks/useUserRole';
|
||||
import { ManufacturerForm } from './ManufacturerForm';
|
||||
import { RideModelForm } from './RideModelForm';
|
||||
import { ParkForm } from './ParkForm';
|
||||
import { TechnicalSpecsEditor, validateTechnicalSpecs } from './editors/TechnicalSpecsEditor';
|
||||
import { CoasterStatsEditor, validateCoasterStats } from './editors/CoasterStatsEditor';
|
||||
import { FormerNamesEditor } from './editors/FormerNamesEditor';
|
||||
@@ -45,12 +46,21 @@ import {
|
||||
type RideFormData = z.infer<typeof entitySchemas.ride>;
|
||||
|
||||
interface RideFormProps {
|
||||
onSubmit: (data: RideFormData) => Promise<void>;
|
||||
onSubmit: (data: RideFormData & {
|
||||
_tempNewPark?: TempParkData;
|
||||
_tempNewManufacturer?: TempCompanyData;
|
||||
_tempNewDesigner?: TempCompanyData;
|
||||
_tempNewRideModel?: TempRideModelData;
|
||||
}) => Promise<void>;
|
||||
onCancel?: () => void;
|
||||
initialData?: Partial<RideFormData & {
|
||||
id?: string;
|
||||
banner_image_url?: string;
|
||||
card_image_url?: string;
|
||||
_tempNewPark?: TempParkData;
|
||||
_tempNewManufacturer?: TempCompanyData;
|
||||
_tempNewDesigner?: TempCompanyData;
|
||||
_tempNewRideModel?: TempRideModelData;
|
||||
}>;
|
||||
isEditing?: boolean;
|
||||
}
|
||||
@@ -154,14 +164,18 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
validateSubmissionHandler(onSubmit, 'ride');
|
||||
}, [onSubmit]);
|
||||
|
||||
// Manufacturer and model state
|
||||
// Temp entity states
|
||||
const [tempNewPark, setTempNewPark] = useState<TempParkData | null>(initialData?._tempNewPark || null);
|
||||
const [selectedManufacturerId, setSelectedManufacturerId] = useState<string>(
|
||||
initialData?.manufacturer_id || ''
|
||||
);
|
||||
const [selectedManufacturerName, setSelectedManufacturerName] = useState<string>('');
|
||||
const [tempNewManufacturer, setTempNewManufacturer] = useState<TempCompanyData | null>(null);
|
||||
const [tempNewRideModel, setTempNewRideModel] = useState<TempRideModelData | null>(null);
|
||||
const [tempNewManufacturer, setTempNewManufacturer] = useState<TempCompanyData | null>(initialData?._tempNewManufacturer || null);
|
||||
const [tempNewDesigner, setTempNewDesigner] = useState<TempCompanyData | null>(initialData?._tempNewDesigner || null);
|
||||
const [tempNewRideModel, setTempNewRideModel] = useState<TempRideModelData | null>(initialData?._tempNewRideModel || null);
|
||||
const [isParkModalOpen, setIsParkModalOpen] = useState(false);
|
||||
const [isManufacturerModalOpen, setIsManufacturerModalOpen] = useState(false);
|
||||
const [isDesignerModalOpen, setIsDesignerModalOpen] = useState(false);
|
||||
const [isModelModalOpen, setIsModelModalOpen] = useState(false);
|
||||
|
||||
// Advanced editor state - using simplified interface for editors (DB fields added on submit)
|
||||
@@ -299,7 +313,9 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
_technical_specifications: technicalSpecs,
|
||||
_coaster_statistics: coasterStats,
|
||||
_name_history: formerNames,
|
||||
_tempNewPark: tempNewPark,
|
||||
_tempNewManufacturer: tempNewManufacturer,
|
||||
_tempNewDesigner: tempNewDesigner,
|
||||
_tempNewRideModel: tempNewRideModel
|
||||
};
|
||||
|
||||
@@ -1337,6 +1353,41 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* Park Modal - Add before Manufacturer Modal */}
|
||||
<Dialog open={isParkModalOpen} onOpenChange={setIsParkModalOpen}>
|
||||
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create New Park</DialogTitle>
|
||||
</DialogHeader>
|
||||
<ParkForm
|
||||
onSubmit={async (data) => {
|
||||
setTempNewPark(data as TempParkData);
|
||||
setIsParkModalOpen(false);
|
||||
setValue('park_id', undefined);
|
||||
}}
|
||||
onCancel={() => setIsParkModalOpen(false)}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Designer Modal */}
|
||||
<Dialog open={isDesignerModalOpen} onOpenChange={setIsDesignerModalOpen}>
|
||||
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create New Designer</DialogTitle>
|
||||
</DialogHeader>
|
||||
<ManufacturerForm
|
||||
initialData={tempNewDesigner}
|
||||
onSubmit={(data) => {
|
||||
setTempNewDesigner(data);
|
||||
setIsDesignerModalOpen(false);
|
||||
setValue('designer_id', undefined);
|
||||
}}
|
||||
onCancel={() => setIsDesignerModalOpen(false)}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Manufacturer Modal */}
|
||||
<Dialog open={isManufacturerModalOpen} onOpenChange={setIsManufacturerModalOpen}>
|
||||
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||
|
||||
61
src/components/admin/ride-form-park-designer-ui.tsx
Normal file
61
src/components/admin/ride-form-park-designer-ui.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* UI components for Park and Designer creation within RideForm
|
||||
* Extracted for clarity - import these into RideForm.tsx
|
||||
*/
|
||||
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Plus, Building2, X } from 'lucide-react';
|
||||
import type { TempParkData, TempCompanyData } from '@/types/company';
|
||||
|
||||
interface ParkSelectorProps {
|
||||
tempNewPark: TempParkData | null;
|
||||
onCreateNew: () => void;
|
||||
onEdit: () => void;
|
||||
onRemove: () => void;
|
||||
parkId?: string;
|
||||
onParkChange: (id: string) => void;
|
||||
}
|
||||
|
||||
interface DesignerSelectorProps {
|
||||
tempNewDesigner: TempCompanyData | null;
|
||||
onCreateNew: () => void;
|
||||
onEdit: () => void;
|
||||
onRemove: () => void;
|
||||
designerId?: string;
|
||||
onDesignerChange: (id: string) => void;
|
||||
}
|
||||
|
||||
export function RideParkSelector({ tempNewPark, onCreateNew, onEdit, onRemove }: ParkSelectorProps) {
|
||||
return tempNewPark ? (
|
||||
<div className="space-y-2">
|
||||
<Badge variant="secondary" className="gap-2">
|
||||
<Building2 className="h-3 w-3" />
|
||||
New: {tempNewPark.name}
|
||||
<button type="button" onClick={onRemove} className="ml-1 hover:text-destructive">×</button>
|
||||
</Badge>
|
||||
<Button type="button" variant="outline" size="sm" onClick={onEdit}>Edit New Park</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Button type="button" variant="outline" size="sm" onClick={onCreateNew}>
|
||||
<Plus className="h-4 w-4 mr-2" />Create New Park
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export function RideDesignerSelector({ tempNewDesigner, onCreateNew, onEdit, onRemove }: DesignerSelectorProps) {
|
||||
return tempNewDesigner ? (
|
||||
<div className="space-y-2">
|
||||
<Badge variant="secondary" className="gap-2">
|
||||
<Building2 className="h-3 w-3" />
|
||||
New: {tempNewDesigner.name}
|
||||
<button type="button" onClick={onRemove} className="ml-1 hover:text-destructive">×</button>
|
||||
</Badge>
|
||||
<Button type="button" variant="outline" size="sm" onClick={onEdit}>Edit New Designer</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Button type="button" variant="outline" size="sm" onClick={onCreateNew}>
|
||||
<Plus className="h-4 w-4 mr-2" />Create New Designer
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user