mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 07:31:12 -05:00
feat: Implement comprehensive plan
This commit is contained in:
@@ -7,12 +7,18 @@ import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { PhotoUpload } from '@/components/upload/PhotoUpload';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { DatePicker } from '@/components/ui/date-picker';
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Combobox } from '@/components/ui/combobox';
|
||||
import { toast } from '@/hooks/use-toast';
|
||||
import { Zap, Save, X } from 'lucide-react';
|
||||
import { Plus, Zap, Save, X } from 'lucide-react';
|
||||
import { useUnitPreferences } from '@/hooks/useUnitPreferences';
|
||||
import { useManufacturers, useRideModels } from '@/hooks/useAutocompleteData';
|
||||
import { ManufacturerForm } from './ManufacturerForm';
|
||||
import { RideModelForm } from './RideModelForm';
|
||||
import {
|
||||
convertSpeed,
|
||||
convertDistance,
|
||||
@@ -50,7 +56,10 @@ const rideSchema = z.object({
|
||||
max_g_force: z.number().optional(),
|
||||
former_names: z.string().optional(),
|
||||
coaster_stats: z.string().optional(),
|
||||
technical_specs: z.string().optional()
|
||||
technical_specs: z.string().optional(),
|
||||
// Manufacturer and model
|
||||
manufacturer_id: z.string().uuid().optional(),
|
||||
ride_model_id: z.string().uuid().optional()
|
||||
});
|
||||
|
||||
type RideFormData = z.infer<typeof rideSchema>;
|
||||
@@ -111,6 +120,20 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
const { preferences } = useUnitPreferences();
|
||||
const measurementSystem = preferences.measurement_system;
|
||||
|
||||
// Manufacturer and model state
|
||||
const [selectedManufacturerId, setSelectedManufacturerId] = useState<string>(
|
||||
initialData?.manufacturer_id || ''
|
||||
);
|
||||
const [selectedManufacturerName, setSelectedManufacturerName] = useState<string>('');
|
||||
const [tempNewManufacturer, setTempNewManufacturer] = useState<any>(null);
|
||||
const [tempNewRideModel, setTempNewRideModel] = useState<any>(null);
|
||||
const [isManufacturerModalOpen, setIsManufacturerModalOpen] = useState(false);
|
||||
const [isModelModalOpen, setIsModelModalOpen] = useState(false);
|
||||
|
||||
// Fetch data
|
||||
const { manufacturers, loading: manufacturersLoading } = useManufacturers();
|
||||
const { rideModels, loading: modelsLoading } = useRideModels(selectedManufacturerId);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
@@ -154,7 +177,9 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
max_g_force: initialData?.max_g_force || undefined,
|
||||
former_names: initialData?.former_names || '',
|
||||
coaster_stats: initialData?.coaster_stats || '',
|
||||
technical_specs: initialData?.technical_specs || ''
|
||||
technical_specs: initialData?.technical_specs || '',
|
||||
manufacturer_id: initialData?.manufacturer_id || undefined,
|
||||
ride_model_id: initialData?.ride_model_id || undefined
|
||||
}
|
||||
});
|
||||
|
||||
@@ -199,13 +224,36 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
image_url: rideImage || undefined
|
||||
};
|
||||
|
||||
await onSubmit(metricData);
|
||||
// Build composite submission if new entities were created
|
||||
const submissionContent: any = {
|
||||
ride: metricData,
|
||||
};
|
||||
|
||||
// Add new manufacturer if created
|
||||
if (tempNewManufacturer) {
|
||||
submissionContent.new_manufacturer = tempNewManufacturer;
|
||||
submissionContent.ride.manufacturer_id = null; // Clear since using new
|
||||
}
|
||||
|
||||
// Add new ride model if created
|
||||
if (tempNewRideModel) {
|
||||
submissionContent.new_ride_model = tempNewRideModel;
|
||||
submissionContent.ride.ride_model_id = null; // Clear since using new
|
||||
}
|
||||
|
||||
// Pass composite data to parent
|
||||
await onSubmit({
|
||||
...metricData,
|
||||
_compositeSubmission: submissionContent
|
||||
} as any);
|
||||
|
||||
toast({
|
||||
title: isEditing ? "Ride Updated" : "Ride Created",
|
||||
title: isEditing ? "Ride Updated" : "Submission Sent",
|
||||
description: isEditing
|
||||
? "The ride information has been updated successfully."
|
||||
: "The new ride has been created successfully."
|
||||
: tempNewManufacturer
|
||||
? "Ride, manufacturer, and model submitted for review"
|
||||
: "Ride submitted for review"
|
||||
});
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
@@ -320,6 +368,144 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Manufacturer & Model Selection */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold">Manufacturer & Model</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Manufacturer Column */}
|
||||
<div className="space-y-2">
|
||||
<Label>Manufacturer</Label>
|
||||
|
||||
{tempNewManufacturer ? (
|
||||
// Show temp manufacturer badge
|
||||
<div className="flex items-center gap-2 p-3 border rounded-md bg-blue-50 dark:bg-blue-950">
|
||||
<Badge variant="secondary">New</Badge>
|
||||
<span className="font-medium">{tempNewManufacturer.name}</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setTempNewManufacturer(null);
|
||||
setTempNewRideModel(null); // Clear model too
|
||||
}}
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setIsManufacturerModalOpen(true)}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
// Show combobox for existing manufacturers
|
||||
<Combobox
|
||||
options={manufacturers}
|
||||
value={watch('manufacturer_id')}
|
||||
onValueChange={(value) => {
|
||||
setValue('manufacturer_id', value);
|
||||
setSelectedManufacturerId(value);
|
||||
// Find and set manufacturer name
|
||||
const mfr = manufacturers.find(m => m.value === value);
|
||||
setSelectedManufacturerName(mfr?.label || '');
|
||||
// Clear model when manufacturer changes
|
||||
setValue('ride_model_id', undefined);
|
||||
setTempNewRideModel(null);
|
||||
}}
|
||||
placeholder="Select manufacturer"
|
||||
searchPlaceholder="Search manufacturers..."
|
||||
emptyText="No manufacturers found"
|
||||
loading={manufacturersLoading}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Create New Manufacturer Button */}
|
||||
{!tempNewManufacturer && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="w-full"
|
||||
onClick={() => setIsManufacturerModalOpen(true)}
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Create New Manufacturer
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Ride Model Column - Conditional */}
|
||||
{(selectedManufacturerId || tempNewManufacturer) && (
|
||||
<div className="space-y-2">
|
||||
<Label>Ride Model (Optional)</Label>
|
||||
|
||||
{tempNewRideModel ? (
|
||||
// Show temp model badge
|
||||
<div className="flex items-center gap-2 p-3 border rounded-md bg-purple-50 dark:bg-purple-950">
|
||||
<Badge variant="secondary">New</Badge>
|
||||
<span className="font-medium">{tempNewRideModel.name}</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setTempNewRideModel(null)}
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setIsModelModalOpen(true)}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
// Show combobox for existing models
|
||||
<>
|
||||
<Combobox
|
||||
options={rideModels}
|
||||
value={watch('ride_model_id')}
|
||||
onValueChange={(value) => setValue('ride_model_id', value)}
|
||||
placeholder="Select model"
|
||||
searchPlaceholder="Search models..."
|
||||
emptyText={tempNewManufacturer
|
||||
? "Create the manufacturer first to add models"
|
||||
: "No models found for this manufacturer"}
|
||||
loading={modelsLoading}
|
||||
disabled={!!tempNewManufacturer}
|
||||
/>
|
||||
|
||||
{/* Create New Model Button */}
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="w-full"
|
||||
onClick={() => setIsModelModalOpen(true)}
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Create New Model
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{tempNewManufacturer
|
||||
? "New models will be created after manufacturer approval"
|
||||
: "Select a specific model or leave blank"}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Dates */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
@@ -565,6 +751,61 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* Manufacturer Modal */}
|
||||
<Dialog open={isManufacturerModalOpen} onOpenChange={setIsManufacturerModalOpen}>
|
||||
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{tempNewManufacturer ? 'Edit New Manufacturer' : 'Create New Manufacturer'}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
This manufacturer will be submitted for moderation along with the ride.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<ManufacturerForm
|
||||
initialData={tempNewManufacturer}
|
||||
onSubmit={(data) => {
|
||||
setTempNewManufacturer(data);
|
||||
setSelectedManufacturerName(data.name);
|
||||
setIsManufacturerModalOpen(false);
|
||||
// Clear existing manufacturer selection
|
||||
setValue('manufacturer_id', undefined);
|
||||
setSelectedManufacturerId('');
|
||||
// Clear any existing model
|
||||
setValue('ride_model_id', undefined);
|
||||
setTempNewRideModel(null);
|
||||
}}
|
||||
onCancel={() => setIsManufacturerModalOpen(false)}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Ride Model Modal */}
|
||||
<Dialog open={isModelModalOpen} onOpenChange={setIsModelModalOpen}>
|
||||
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{tempNewRideModel ? 'Edit New Ride Model' : 'Create New Ride Model'}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Creating a model for: <strong>{selectedManufacturerName || tempNewManufacturer?.name}</strong>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<RideModelForm
|
||||
manufacturerName={selectedManufacturerName || tempNewManufacturer?.name}
|
||||
manufacturerId={selectedManufacturerId}
|
||||
initialData={tempNewRideModel}
|
||||
onSubmit={(data) => {
|
||||
setTempNewRideModel(data);
|
||||
setIsModelModalOpen(false);
|
||||
// Clear existing model selection
|
||||
setValue('ride_model_id', undefined);
|
||||
}}
|
||||
onCancel={() => setIsModelModalOpen(false)}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user