mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 12:31:26 -05:00
Add advanced form editors
This commit is contained in:
@@ -21,6 +21,9 @@ import { useManufacturers, useRideModels } from '@/hooks/useAutocompleteData';
|
|||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { ManufacturerForm } from './ManufacturerForm';
|
import { ManufacturerForm } from './ManufacturerForm';
|
||||||
import { RideModelForm } from './RideModelForm';
|
import { RideModelForm } from './RideModelForm';
|
||||||
|
import { TechnicalSpecsEditor } from './editors/TechnicalSpecsEditor';
|
||||||
|
import { CoasterStatsEditor } from './editors/CoasterStatsEditor';
|
||||||
|
import { FormerNamesEditor } from './editors/FormerNamesEditor';
|
||||||
import {
|
import {
|
||||||
convertSpeed,
|
convertSpeed,
|
||||||
convertDistance,
|
convertDistance,
|
||||||
@@ -144,6 +147,11 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
const [isManufacturerModalOpen, setIsManufacturerModalOpen] = useState(false);
|
const [isManufacturerModalOpen, setIsManufacturerModalOpen] = useState(false);
|
||||||
const [isModelModalOpen, setIsModelModalOpen] = useState(false);
|
const [isModelModalOpen, setIsModelModalOpen] = useState(false);
|
||||||
|
|
||||||
|
// Advanced editor state
|
||||||
|
const [technicalSpecs, setTechnicalSpecs] = useState<any[]>([]);
|
||||||
|
const [coasterStats, setCoasterStats] = useState<any[]>([]);
|
||||||
|
const [formerNames, setFormerNames] = useState<any[]>([]);
|
||||||
|
|
||||||
// Fetch data
|
// Fetch data
|
||||||
const { manufacturers, loading: manufacturersLoading } = useManufacturers();
|
const { manufacturers, loading: manufacturersLoading } = useManufacturers();
|
||||||
const { rideModels, loading: modelsLoading } = useRideModels(selectedManufacturerId);
|
const { rideModels, loading: modelsLoading } = useRideModels(selectedManufacturerId);
|
||||||
@@ -221,12 +229,20 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
: undefined,
|
: undefined,
|
||||||
drop_height_meters: data.drop_height_meters
|
drop_height_meters: data.drop_height_meters
|
||||||
? convertDistanceToMetric(data.drop_height_meters, measurementSystem)
|
? convertDistanceToMetric(data.drop_height_meters, measurementSystem)
|
||||||
: undefined
|
: undefined,
|
||||||
|
// Add structured data from advanced editors
|
||||||
|
technical_specs: JSON.stringify(technicalSpecs),
|
||||||
|
coaster_stats: JSON.stringify(coasterStats),
|
||||||
|
former_names: JSON.stringify(formerNames)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build composite submission if new entities were created
|
// Build composite submission if new entities were created
|
||||||
const submissionContent: any = {
|
const submissionContent: any = {
|
||||||
ride: metricData,
|
ride: metricData,
|
||||||
|
// Include structured data for relational tables
|
||||||
|
technical_specifications: technicalSpecs,
|
||||||
|
coaster_statistics: coasterStats,
|
||||||
|
name_history: formerNames
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add new manufacturer if created
|
// Add new manufacturer if created
|
||||||
@@ -706,6 +722,30 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Advanced Editors */}
|
||||||
|
<div className="space-y-6 border-t pt-6">
|
||||||
|
<TechnicalSpecsEditor
|
||||||
|
specs={technicalSpecs}
|
||||||
|
onChange={setTechnicalSpecs}
|
||||||
|
commonSpecs={['Track Material', 'Manufacturer', 'Train Type', 'Restraint System', 'Block Sections']}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{selectedCategory === 'roller_coaster' && (
|
||||||
|
<>
|
||||||
|
<CoasterStatsEditor
|
||||||
|
stats={coasterStats}
|
||||||
|
onChange={setCoasterStats}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormerNamesEditor
|
||||||
|
names={formerNames}
|
||||||
|
onChange={setFormerNames}
|
||||||
|
currentName={watch('name') || 'Unnamed Ride'}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Images */}
|
{/* Images */}
|
||||||
<EntityMultiImageUploader
|
<EntityMultiImageUploader
|
||||||
mode={isEditing ? 'edit' : 'create'}
|
mode={isEditing ? 'edit' : 'create'}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
@@ -12,6 +13,7 @@ import { SlugField } from '@/components/ui/slug-field';
|
|||||||
import { Layers, Save, X } from 'lucide-react';
|
import { Layers, Save, X } from 'lucide-react';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
||||||
|
import { TechnicalSpecsEditor } from './editors/TechnicalSpecsEditor';
|
||||||
|
|
||||||
const rideModelSchema = z.object({
|
const rideModelSchema = z.object({
|
||||||
name: z.string().min(1, 'Name is required'),
|
name: z.string().min(1, 'Name is required'),
|
||||||
@@ -60,6 +62,7 @@ export function RideModelForm({
|
|||||||
initialData
|
initialData
|
||||||
}: RideModelFormProps) {
|
}: RideModelFormProps) {
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
|
const [technicalSpecs, setTechnicalSpecs] = useState<any[]>([]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -166,15 +169,20 @@ export function RideModelForm({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Technical Specs */}
|
{/* Technical Specs */}
|
||||||
<div className="space-y-2">
|
<div className="border-t pt-6">
|
||||||
<Label htmlFor="technical_specs">Technical Specifications</Label>
|
<TechnicalSpecsEditor
|
||||||
<Textarea
|
specs={technicalSpecs}
|
||||||
id="technical_specs"
|
onChange={setTechnicalSpecs}
|
||||||
{...register('technical_specs')}
|
commonSpecs={[
|
||||||
placeholder="Enter technical specifications (e.g., track length, inversions, typical speed range)..."
|
'Typical Track Length',
|
||||||
rows={3}
|
'Typical Height',
|
||||||
|
'Typical Speed',
|
||||||
|
'Standard Train Configuration',
|
||||||
|
'Typical Capacity',
|
||||||
|
'Typical Duration'
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground mt-2">
|
||||||
General specifications for this model that apply to all installations
|
General specifications for this model that apply to all installations
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
-- Phase 3B: Performance Optimizations - Add Database Indexes
|
||||||
|
|
||||||
|
-- Full-Text Search Indexes (GIN indexes for fast text search)
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_parks_search
|
||||||
|
ON parks USING gin(to_tsvector('english', name || ' ' || COALESCE(description, '')));
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_search
|
||||||
|
ON rides USING gin(to_tsvector('english', name || ' ' || COALESCE(description, '')));
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_companies_search
|
||||||
|
ON companies USING gin(to_tsvector('english', name || ' ' || COALESCE(description, '')));
|
||||||
|
|
||||||
|
-- Technical Specifications Search Indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ride_technical_specs_search
|
||||||
|
ON ride_technical_specifications (ride_id, spec_name, spec_value);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ride_technical_specs_category
|
||||||
|
ON ride_technical_specifications (category) WHERE category IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ride_coaster_stats_search
|
||||||
|
ON ride_coaster_statistics (ride_id, stat_name, stat_value);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ride_coaster_stats_category
|
||||||
|
ON ride_coaster_statistics (category) WHERE category IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ride_name_history_ride
|
||||||
|
ON ride_name_history (ride_id, date_changed DESC);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ride_model_technical_specs_search
|
||||||
|
ON ride_model_technical_specifications (ride_model_id, spec_name, spec_value);
|
||||||
|
|
||||||
|
-- Filtering and Sorting Indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_parks_status
|
||||||
|
ON parks (status) WHERE status IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_parks_country
|
||||||
|
ON parks (location_id) WHERE location_id IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_parks_type
|
||||||
|
ON parks (park_type);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_category_status
|
||||||
|
ON rides (category, status);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_park
|
||||||
|
ON rides (park_id, status);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_manufacturer
|
||||||
|
ON rides (manufacturer_id) WHERE manufacturer_id IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_designer
|
||||||
|
ON rides (designer_id) WHERE designer_id IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_model
|
||||||
|
ON rides (ride_model_id) WHERE ride_model_id IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_companies_type
|
||||||
|
ON companies (company_type);
|
||||||
|
|
||||||
|
-- Rating and Review Indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_parks_rating
|
||||||
|
ON parks (average_rating DESC) WHERE average_rating > 0;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_rating
|
||||||
|
ON rides (average_rating DESC) WHERE average_rating > 0;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_companies_rating
|
||||||
|
ON companies (average_rating DESC) WHERE average_rating > 0;
|
||||||
|
|
||||||
|
-- Location-based Indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_locations_country_city
|
||||||
|
ON locations (country, city);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_locations_coordinates
|
||||||
|
ON locations (latitude, longitude) WHERE latitude IS NOT NULL AND longitude IS NOT NULL;
|
||||||
|
|
||||||
|
-- Performance Specs for Range Queries
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_speed
|
||||||
|
ON rides (max_speed_kmh) WHERE max_speed_kmh IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_height
|
||||||
|
ON rides (max_height_meters) WHERE max_height_meters IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_length
|
||||||
|
ON rides (length_meters) WHERE length_meters IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_inversions
|
||||||
|
ON rides (inversions) WHERE inversions > 0;
|
||||||
|
|
||||||
|
-- Review Indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_reviews_park
|
||||||
|
ON reviews (park_id, moderation_status) WHERE park_id IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_reviews_ride
|
||||||
|
ON reviews (ride_id, moderation_status) WHERE ride_id IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_reviews_user
|
||||||
|
ON reviews (user_id, moderation_status);
|
||||||
|
|
||||||
|
-- Submission System Indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_submission_items_status
|
||||||
|
ON submission_items (status, created_at DESC);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_submission_items_type
|
||||||
|
ON submission_items (item_type, status);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_content_submissions_user_status
|
||||||
|
ON content_submissions (user_id, status, created_at DESC);
|
||||||
|
|
||||||
|
-- Composite indexes for common query patterns
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_park_category
|
||||||
|
ON rides (park_id, category, status);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rides_manufacturer_category
|
||||||
|
ON rides (manufacturer_id, category) WHERE manufacturer_id IS NOT NULL;
|
||||||
Reference in New Issue
Block a user