mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-29 05:07:05 -05:00
Compare commits
3 Commits
2f579b08ba
...
67ce8b5a88
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67ce8b5a88 | ||
|
|
99c8c94e47 | ||
|
|
9a3fbb2f78 |
@@ -17,7 +17,7 @@ import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-
|
|||||||
import { SlugField } from '@/components/ui/slug-field';
|
import { SlugField } from '@/components/ui/slug-field';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
import { handleError } from '@/lib/errorHandler';
|
import { handleError } from '@/lib/errorHandler';
|
||||||
import { MapPin, Save, X, Plus, AlertCircle } from 'lucide-react';
|
import { MapPin, Save, X, Plus, AlertCircle, Info } from 'lucide-react';
|
||||||
import { toDateOnly, parseDateOnly, toDateWithPrecision } from '@/lib/dateUtils';
|
import { toDateOnly, parseDateOnly, toDateWithPrecision } from '@/lib/dateUtils';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Combobox } from '@/components/ui/combobox';
|
import { Combobox } from '@/components/ui/combobox';
|
||||||
@@ -30,6 +30,7 @@ import { LocationSearch } from './LocationSearch';
|
|||||||
import { OperatorForm } from './OperatorForm';
|
import { OperatorForm } from './OperatorForm';
|
||||||
import { PropertyOwnerForm } from './PropertyOwnerForm';
|
import { PropertyOwnerForm } from './PropertyOwnerForm';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import { SubmissionHelpDialog } from '@/components/help/SubmissionHelpDialog';
|
||||||
|
|
||||||
const parkSchema = z.object({
|
const parkSchema = z.object({
|
||||||
name: z.string().min(1, 'Park name is required'),
|
name: z.string().min(1, 'Park name is required'),
|
||||||
@@ -314,10 +315,13 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
return (
|
return (
|
||||||
<Card className="w-full max-w-4xl mx-auto">
|
<Card className="w-full max-w-4xl mx-auto">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2">
|
<div className="flex items-center justify-between">
|
||||||
<MapPin className="w-5 h-5" />
|
<CardTitle className="flex items-center gap-2">
|
||||||
{isEditing ? 'Edit Park' : 'Create New Park'}
|
<MapPin className="w-5 h-5" />
|
||||||
</CardTitle>
|
{isEditing ? 'Edit Park' : 'Create New Park'}
|
||||||
|
</CardTitle>
|
||||||
|
<SubmissionHelpDialog type="park" variant="icon" />
|
||||||
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<form onSubmit={handleSubmit(handleFormSubmit)} className="space-y-6">
|
<form onSubmit={handleSubmit(handleFormSubmit)} className="space-y-6">
|
||||||
@@ -370,6 +374,10 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
<div className="flex items-start gap-2 text-xs text-muted-foreground">
|
||||||
|
<Info className="h-3.5 w-3.5 mt-0.5 flex-shrink-0" />
|
||||||
|
<p>Choose the primary classification. Theme parks have themed areas, while amusement parks focus on rides.</p>
|
||||||
|
</div>
|
||||||
{errors.park_type && (
|
{errors.park_type && (
|
||||||
<p className="text-sm text-destructive">{errors.park_type.message}</p>
|
<p className="text-sm text-destructive">{errors.park_type.message}</p>
|
||||||
)}
|
)}
|
||||||
@@ -395,6 +403,10 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
})}
|
})}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
<div className="flex items-start gap-2 text-xs text-muted-foreground">
|
||||||
|
<Info className="h-3.5 w-3.5 mt-0.5 flex-shrink-0" />
|
||||||
|
<p>Current operational status. Use "Closed Temporarily" for seasonal closures or renovations.</p>
|
||||||
|
</div>
|
||||||
{errors.status && (
|
{errors.status && (
|
||||||
<p className="text-sm text-destructive">{errors.status.message}</p>
|
<p className="text-sm text-destructive">{errors.status.message}</p>
|
||||||
)}
|
)}
|
||||||
@@ -446,6 +458,10 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
}}
|
}}
|
||||||
initialLocationId={watch('location_id')}
|
initialLocationId={watch('location_id')}
|
||||||
/>
|
/>
|
||||||
|
<div className="flex items-start gap-2 text-xs text-muted-foreground">
|
||||||
|
<Info className="h-3.5 w-3.5 mt-0.5 flex-shrink-0" />
|
||||||
|
<p>Search by park name, address, or city. Select from results to auto-fill coordinates and timezone.</p>
|
||||||
|
</div>
|
||||||
{errors.location && (
|
{errors.location && (
|
||||||
<p className="text-sm text-destructive flex items-center gap-1">
|
<p className="text-sm text-destructive flex items-center gap-1">
|
||||||
<AlertCircle className="w-4 h-4" />
|
<AlertCircle className="w-4 h-4" />
|
||||||
@@ -462,6 +478,10 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
{/* Operator & Property Owner Selection */}
|
{/* Operator & Property Owner Selection */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-lg font-semibold">Operator & Property Owner</h3>
|
<h3 className="text-lg font-semibold">Operator & Property Owner</h3>
|
||||||
|
<div className="flex items-start gap-2 text-xs text-muted-foreground mb-3">
|
||||||
|
<Info className="h-3.5 w-3.5 mt-0.5 flex-shrink-0" />
|
||||||
|
<p>The operator runs the park, while the property owner owns the land. Often the same entity.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2 mb-4">
|
<div className="flex items-center space-x-2 mb-4">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|||||||
@@ -21,9 +21,10 @@ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } f
|
|||||||
import { Combobox } from '@/components/ui/combobox';
|
import { Combobox } from '@/components/ui/combobox';
|
||||||
import { SlugField } from '@/components/ui/slug-field';
|
import { SlugField } from '@/components/ui/slug-field';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
import { handleError } from '@/lib/errorHandler';
|
import { handleError } from '@/lib/errorHandler';
|
||||||
import { Plus, Zap, Save, X, Building2, AlertCircle } from 'lucide-react';
|
import { Plus, Zap, Save, X, Building2, AlertCircle, Info, HelpCircle } from 'lucide-react';
|
||||||
import { toDateOnly, parseDateOnly, toDateWithPrecision } from '@/lib/dateUtils';
|
import { toDateOnly, parseDateOnly, toDateWithPrecision } from '@/lib/dateUtils';
|
||||||
import { useUnitPreferences } from '@/hooks/useUnitPreferences';
|
import { useUnitPreferences } from '@/hooks/useUnitPreferences';
|
||||||
import { useManufacturers, useRideModels, useParks } from '@/hooks/useAutocompleteData';
|
import { useManufacturers, useRideModels, useParks } from '@/hooks/useAutocompleteData';
|
||||||
@@ -34,6 +35,7 @@ import { ParkForm } from './ParkForm';
|
|||||||
import { TechnicalSpecsEditor, validateTechnicalSpecs } from './editors/TechnicalSpecsEditor';
|
import { TechnicalSpecsEditor, validateTechnicalSpecs } from './editors/TechnicalSpecsEditor';
|
||||||
import { CoasterStatsEditor, validateCoasterStats } from './editors/CoasterStatsEditor';
|
import { CoasterStatsEditor, validateCoasterStats } from './editors/CoasterStatsEditor';
|
||||||
import { FormerNamesEditor } from './editors/FormerNamesEditor';
|
import { FormerNamesEditor } from './editors/FormerNamesEditor';
|
||||||
|
import { SubmissionHelpDialog } from '@/components/help/SubmissionHelpDialog';
|
||||||
import {
|
import {
|
||||||
convertValueToMetric,
|
convertValueToMetric,
|
||||||
convertValueFromMetric,
|
convertValueFromMetric,
|
||||||
@@ -381,15 +383,19 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="w-full max-w-4xl mx-auto">
|
<TooltipProvider>
|
||||||
<CardHeader>
|
<Card className="w-full max-w-4xl mx-auto">
|
||||||
<CardTitle className="flex items-center gap-2">
|
<CardHeader>
|
||||||
<Zap className="w-5 h-5" />
|
<div className="flex items-center justify-between">
|
||||||
{isEditing ? 'Edit Ride' : 'Create New Ride'}
|
<CardTitle className="flex items-center gap-2">
|
||||||
</CardTitle>
|
<Zap className="w-5 h-5" />
|
||||||
</CardHeader>
|
{isEditing ? 'Edit Ride' : 'Create New Ride'}
|
||||||
<CardContent>
|
</CardTitle>
|
||||||
<form onSubmit={handleSubmit(handleFormSubmit)} className="space-y-6">
|
<SubmissionHelpDialog type="ride" variant="icon" />
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form onSubmit={handleSubmit(handleFormSubmit)} className="space-y-6">
|
||||||
{/* Basic Information */}
|
{/* Basic Information */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -529,6 +535,10 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
<div className="flex items-start gap-2 text-xs text-muted-foreground">
|
||||||
|
<Info className="h-3.5 w-3.5 mt-0.5 flex-shrink-0" />
|
||||||
|
<p>Primary ride type. Choose roller coaster for any coaster, flat ride for spinners/swings, water ride for flumes/rapids.</p>
|
||||||
|
</div>
|
||||||
{errors.category && (
|
{errors.category && (
|
||||||
<p className="text-sm text-destructive">{errors.category.message}</p>
|
<p className="text-sm text-destructive">{errors.category.message}</p>
|
||||||
)}
|
)}
|
||||||
@@ -541,6 +551,10 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
{...register('ride_sub_type')}
|
{...register('ride_sub_type')}
|
||||||
placeholder="e.g. Inverted Coaster, Log Flume"
|
placeholder="e.g. Inverted Coaster, Log Flume"
|
||||||
/>
|
/>
|
||||||
|
<div className="flex items-start gap-2 text-xs text-muted-foreground">
|
||||||
|
<Info className="h-3.5 w-3.5 mt-0.5 flex-shrink-0" />
|
||||||
|
<p>Specific type within category (e.g., "Inverted Coaster", "Flume").</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -563,6 +577,10 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
})}
|
})}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
<div className="flex items-start gap-2 text-xs text-muted-foreground">
|
||||||
|
<Info className="h-3.5 w-3.5 mt-0.5 flex-shrink-0" />
|
||||||
|
<p>Current state. Use "Relocated" if moved to another park.</p>
|
||||||
|
</div>
|
||||||
{errors.status && (
|
{errors.status && (
|
||||||
<p className="text-sm text-destructive">{errors.status.message}</p>
|
<p className="text-sm text-destructive">{errors.status.message}</p>
|
||||||
)}
|
)}
|
||||||
@@ -572,6 +590,10 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
{/* Manufacturer & Model Selection */}
|
{/* Manufacturer & Model Selection */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-lg font-semibold">Manufacturer & Model</h3>
|
<h3 className="text-lg font-semibold">Manufacturer & Model</h3>
|
||||||
|
<div className="flex items-start gap-2 text-xs text-muted-foreground mb-3">
|
||||||
|
<Info className="h-3.5 w-3.5 mt-0.5 flex-shrink-0" />
|
||||||
|
<p>The company that built the ride. Model is the specific product line (e.g., "B&M" makes "Inverted Coaster" models).</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
{/* Manufacturer Column */}
|
{/* Manufacturer Column */}
|
||||||
@@ -747,6 +769,10 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
{...register('height_requirement', { setValueAs: (v) => v === "" ? undefined : parseFloat(v) })}
|
{...register('height_requirement', { setValueAs: (v) => v === "" ? undefined : parseFloat(v) })}
|
||||||
placeholder={measurementSystem === 'imperial' ? 'e.g. 47' : 'e.g. 120'}
|
placeholder={measurementSystem === 'imperial' ? 'e.g. 47' : 'e.g. 120'}
|
||||||
/>
|
/>
|
||||||
|
<div className="flex items-start gap-2 text-xs text-muted-foreground">
|
||||||
|
<Info className="h-3.5 w-3.5 mt-0.5 flex-shrink-0" />
|
||||||
|
<p>Minimum height to ride. Values automatically convert to {measurementSystem === 'imperial' ? 'inches' : 'cm'}.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -758,6 +784,10 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
{...register('age_requirement', { setValueAs: (v) => v === "" ? undefined : parseFloat(v) })}
|
{...register('age_requirement', { setValueAs: (v) => v === "" ? undefined : parseFloat(v) })}
|
||||||
placeholder="e.g. 8"
|
placeholder="e.g. 8"
|
||||||
/>
|
/>
|
||||||
|
<div className="flex items-start gap-2 text-xs text-muted-foreground">
|
||||||
|
<Info className="h-3.5 w-3.5 mt-0.5 flex-shrink-0" />
|
||||||
|
<p>Minimum age in years, if different from height requirement.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -765,6 +795,10 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
{selectedCategory === 'roller_coaster' && (
|
{selectedCategory === 'roller_coaster' && (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-lg font-semibold">Roller Coaster Details</h3>
|
<h3 className="text-lg font-semibold">Roller Coaster Details</h3>
|
||||||
|
<div className="flex items-start gap-2 text-xs text-muted-foreground mb-3">
|
||||||
|
<Info className="h-3.5 w-3.5 mt-0.5 flex-shrink-0" />
|
||||||
|
<p>Specific attributes for roller coasters. Track/support materials help classify hybrid coasters.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -816,7 +850,17 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<Label>Track Material(s)</Label>
|
<div className="flex items-center gap-2">
|
||||||
|
<Label>Track Material(s)</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<HelpCircle className="h-4 w-4 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="max-w-xs">
|
||||||
|
<p>Material used for the track. Select multiple if hybrid (e.g., wood track with steel supports).</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
<p className="text-sm text-muted-foreground">Select all materials used in the track</p>
|
<p className="text-sm text-muted-foreground">Select all materials used in the track</p>
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
{TRACK_MATERIALS.map((material) => (
|
{TRACK_MATERIALS.map((material) => (
|
||||||
@@ -842,7 +886,17 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<Label>Support Material(s)</Label>
|
<div className="flex items-center gap-2">
|
||||||
|
<Label>Support Material(s)</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<HelpCircle className="h-4 w-4 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="max-w-xs">
|
||||||
|
<p>Material used for the support structure. Can be different from track material (e.g., wood track on steel supports).</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
<p className="text-sm text-muted-foreground">Select all materials used in the supports</p>
|
<p className="text-sm text-muted-foreground">Select all materials used in the supports</p>
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
{SUPPORT_MATERIALS.map((material) => (
|
{SUPPORT_MATERIALS.map((material) => (
|
||||||
@@ -868,7 +922,23 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<Label>Propulsion Method(s)</Label>
|
<div className="flex items-center gap-2">
|
||||||
|
<Label>Propulsion Method(s)</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<HelpCircle className="h-4 w-4 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="max-w-xs">
|
||||||
|
<p className="font-semibold mb-1">Common methods:</p>
|
||||||
|
<ul className="text-xs space-y-1">
|
||||||
|
<li>• <strong>LSM Launch:</strong> Linear Synchronous Motor (smooth, modern)</li>
|
||||||
|
<li>• <strong>Chain Lift:</strong> Traditional lift hill</li>
|
||||||
|
<li>• <strong>Hydraulic Launch:</strong> Fast, powerful (e.g., Kingda Ka)</li>
|
||||||
|
<li>• <strong>Gravity:</strong> Free-fall or terrain-based</li>
|
||||||
|
</ul>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
<p className="text-sm text-muted-foreground">Select all propulsion methods used</p>
|
<p className="text-sm text-muted-foreground">Select all propulsion methods used</p>
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
{PROPULSION_METHODS.map((method) => (
|
{PROPULSION_METHODS.map((method) => (
|
||||||
@@ -1574,5 +1644,6 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import { Plus, Trash2 } from "lucide-react";
|
import { Plus, Trash2, HelpCircle } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Card } from "@/components/ui/card";
|
import { Card } from "@/components/ui/card";
|
||||||
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
import { useUnitPreferences } from "@/hooks/useUnitPreferences";
|
import { useUnitPreferences } from "@/hooks/useUnitPreferences";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import {
|
||||||
@@ -126,14 +127,25 @@ export function TechnicalSpecsEditor({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<TooltipProvider>
|
||||||
<div className="flex items-center justify-between">
|
<div className="space-y-4">
|
||||||
<Label>Technical Specifications</Label>
|
<div className="flex items-center justify-between">
|
||||||
<Button type="button" variant="outline" size="sm" onClick={addSpec}>
|
<div className="flex items-center gap-2">
|
||||||
<Plus className="h-4 w-4 mr-2" />
|
<Label>Technical Specifications</Label>
|
||||||
Add Specification
|
<Tooltip>
|
||||||
</Button>
|
<TooltipTrigger asChild>
|
||||||
</div>
|
<HelpCircle className="h-4 w-4 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="max-w-xs">
|
||||||
|
<p>Add custom specifications like track material (Steel, Wood), propulsion method (LSM Launch, Chain Lift), train type, etc. Use metric units only.</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<Button type="button" variant="outline" size="sm" onClick={addSpec}>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
Add Specification
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{specs.length === 0 ? (
|
{specs.length === 0 ? (
|
||||||
<Card className="p-6 text-center text-muted-foreground">
|
<Card className="p-6 text-center text-muted-foreground">
|
||||||
@@ -145,7 +157,24 @@ export function TechnicalSpecsEditor({
|
|||||||
<Card key={index} className="p-4">
|
<Card key={index} className="p-4">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-3">
|
||||||
<div className="lg:col-span-2">
|
<div className="lg:col-span-2">
|
||||||
<Label className="text-xs">Specification Name</Label>
|
<div className="flex items-center gap-1">
|
||||||
|
<Label className="text-xs">Specification Name</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<HelpCircle className="h-3 w-3 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="max-w-xs">
|
||||||
|
<p className="font-semibold mb-1">Examples:</p>
|
||||||
|
<ul className="text-xs space-y-1">
|
||||||
|
<li>• Track Material (Steel/Wood)</li>
|
||||||
|
<li>• Propulsion Method (LSM Launch, Chain Lift)</li>
|
||||||
|
<li>• Train Type (Sit-down, Inverted)</li>
|
||||||
|
<li>• Restraint System (Lap bar, OTSR)</li>
|
||||||
|
<li>• Launch Speed (km/h)</li>
|
||||||
|
</ul>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
<Input
|
<Input
|
||||||
value={spec.spec_name}
|
value={spec.spec_name}
|
||||||
onChange={(e) => updateSpec(index, 'spec_name', e.target.value)}
|
onChange={(e) => updateSpec(index, 'spec_name', e.target.value)}
|
||||||
@@ -189,7 +218,22 @@ export function TechnicalSpecsEditor({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-xs">Type</Label>
|
<div className="flex items-center gap-1">
|
||||||
|
<Label className="text-xs">Type</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<HelpCircle className="h-3 w-3 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="max-w-xs">
|
||||||
|
<ul className="text-xs space-y-1">
|
||||||
|
<li>• <strong>Text:</strong> Material names, methods (e.g., "Steel", "LSM Launch")</li>
|
||||||
|
<li>• <strong>Number:</strong> Measurements with units (e.g., speed, length)</li>
|
||||||
|
<li>• <strong>Yes/No:</strong> Features (e.g., "Has VR")</li>
|
||||||
|
<li>• <strong>Date:</strong> Installation dates</li>
|
||||||
|
</ul>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
<Select
|
<Select
|
||||||
value={spec.spec_type}
|
value={spec.spec_type}
|
||||||
onValueChange={(value) => updateSpec(index, 'spec_type', value)}
|
onValueChange={(value) => updateSpec(index, 'spec_type', value)}
|
||||||
@@ -225,7 +269,23 @@ export function TechnicalSpecsEditor({
|
|||||||
|
|
||||||
<div className="flex items-end gap-2">
|
<div className="flex items-end gap-2">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<Label className="text-xs">Unit</Label>
|
<div className="flex items-center gap-1">
|
||||||
|
<Label className="text-xs">Unit</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<HelpCircle className="h-3 w-3 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="max-w-xs">
|
||||||
|
<p className="font-semibold mb-1">Metric units only:</p>
|
||||||
|
<ul className="text-xs space-y-1">
|
||||||
|
<li>• Speed: km/h (not mph)</li>
|
||||||
|
<li>• Distance: m, km, cm (not ft, mi, in)</li>
|
||||||
|
<li>• Weight: kg, g (not lb, oz)</li>
|
||||||
|
<li>• Leave empty for text values</li>
|
||||||
|
</ul>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
<Input
|
<Input
|
||||||
value={spec.unit || ''}
|
value={spec.unit || ''}
|
||||||
onChange={(e) => updateSpec(index, 'unit', e.target.value)}
|
onChange={(e) => updateSpec(index, 'unit', e.target.value)}
|
||||||
@@ -257,7 +317,8 @@ export function TechnicalSpecsEditor({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
385
src/components/help/SubmissionHelpDialog.tsx
Normal file
385
src/components/help/SubmissionHelpDialog.tsx
Normal file
@@ -0,0 +1,385 @@
|
|||||||
|
import { HelpCircle } from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from "@/components/ui/accordion";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
|
||||||
|
interface SubmissionHelpDialogProps {
|
||||||
|
type: 'park' | 'ride';
|
||||||
|
variant?: 'button' | 'icon';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SubmissionHelpDialog({ type, variant = 'button' }: SubmissionHelpDialogProps) {
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
{variant === 'button' ? (
|
||||||
|
<Button type="button" variant="outline" size="sm">
|
||||||
|
<HelpCircle className="h-4 w-4 mr-2" />
|
||||||
|
Submission Guide
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button type="button" variant="ghost" size="icon">
|
||||||
|
<HelpCircle className="h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="max-w-3xl max-h-[90vh]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>
|
||||||
|
{type === 'park' ? 'Park' : 'Ride'} Submission Guide
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Everything you need to know about submitting {type === 'park' ? 'parks' : 'rides'} to ThrillWiki
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<ScrollArea className="h-[60vh] pr-4">
|
||||||
|
<Accordion type="multiple" className="w-full">
|
||||||
|
{/* Date Precision */}
|
||||||
|
<AccordionItem value="date-precision">
|
||||||
|
<AccordionTrigger>Date Precision Options</AccordionTrigger>
|
||||||
|
<AccordionContent className="space-y-3">
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Choose how precise your date information is. This helps maintain accuracy when exact dates aren't known.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Exact Day</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Use when you know the specific date (e.g., June 15, 2010)</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Example: Opening day announcement</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Month & Year</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Use when you only know the month (e.g., June 2010)</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Example: "Opened in summer 2010"</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Year Only</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Use when you only know the year (e.g., 2010)</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Example: Historical records show "1985"</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Decade</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Use for events in a general decade (e.g., 1980s)</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Example: "Built in the early 1970s"</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Century</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Use for very old dates spanning a century</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Example: "19th century fairground"</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Approximate</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Use when the date is uncertain or estimated</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Example: "circa 2005"</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
|
||||||
|
{type === 'park' && (
|
||||||
|
<>
|
||||||
|
{/* Park Types */}
|
||||||
|
<AccordionItem value="park-types">
|
||||||
|
<AccordionTrigger>Park Types Explained</AccordionTrigger>
|
||||||
|
<AccordionContent className="space-y-3">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Theme Park</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Has distinct themed areas with immersive experiences and storytelling</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Examples: Disneyland, Universal Studios</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Amusement Park</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Focuses on rides and attractions without heavy theming</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Examples: Cedar Point, Six Flags</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Water Park</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Water-based attractions like slides, wave pools, lazy rivers</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Examples: Schlitterbahn, Aquatica</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Family Entertainment Center</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Indoor facilities with arcade games, mini golf, go-karts</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Examples: Chuck E. Cheese, Dave & Buster's</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
|
||||||
|
{/* Operator vs Owner */}
|
||||||
|
<AccordionItem value="operator-owner">
|
||||||
|
<AccordionTrigger>Operator vs. Property Owner</AccordionTrigger>
|
||||||
|
<AccordionContent className="space-y-3">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="border-l-2 border-green-500 pl-3">
|
||||||
|
<p className="font-semibold text-sm">Operator</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
The company that runs day-to-day operations, manages staff, and operates the park
|
||||||
|
</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Example: Six Flags operates many parks</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-blue-500 pl-3">
|
||||||
|
<p className="font-semibold text-sm">Property Owner</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
The entity that owns the land and physical property
|
||||||
|
</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Example: Real estate investment company</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-muted p-3 rounded-md mt-3">
|
||||||
|
<p className="font-semibold text-sm mb-1">💡 Pro Tip</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Often the operator and owner are the same company (check the "Operator is also the property owner" box).
|
||||||
|
But sometimes they're different - for example, a park might lease land from a property owner.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{type === 'ride' && (
|
||||||
|
<>
|
||||||
|
{/* Ride Categories */}
|
||||||
|
<AccordionItem value="ride-categories">
|
||||||
|
<AccordionTrigger>Ride Categories</AccordionTrigger>
|
||||||
|
<AccordionContent className="space-y-3">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Roller Coaster</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Any type of coaster with a track and gravity-based movement</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Includes: Steel, Wood, Inverted, Flying</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Flat Ride</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Spinning, swinging, or rotating rides at ground level</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Examples: Tilt-A-Whirl, Scrambler, Top Spin</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Water Ride</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Rides involving water, splashing, or getting wet</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Examples: Log Flume, River Rapids, Shoot-the-Chute</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Dark Ride</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Indoor rides with controlled lighting and theming</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Examples: Haunted Mansion, Pirates of the Caribbean</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
|
||||||
|
{/* Manufacturer vs Designer */}
|
||||||
|
<AccordionItem value="manufacturer-designer">
|
||||||
|
<AccordionTrigger>Manufacturer vs. Designer</AccordionTrigger>
|
||||||
|
<AccordionContent className="space-y-3">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="border-l-2 border-green-500 pl-3">
|
||||||
|
<p className="font-semibold text-sm">Manufacturer</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
The company that physically built and engineered the ride
|
||||||
|
</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Examples: Intamin, B&M, Vekoma, RMC</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-blue-500 pl-3">
|
||||||
|
<p className="font-semibold text-sm">Designer (Optional)</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
The design firm or consultant that created the ride concept and layout
|
||||||
|
</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Examples: Werner Stengel, Ride Centerline</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-muted p-3 rounded-md mt-3">
|
||||||
|
<p className="font-semibold text-sm mb-1">💡 Pro Tip</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Most rides only need a manufacturer. Add a designer only if they're notably different
|
||||||
|
(e.g., Werner Stengel designed layouts for many B&M coasters).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
|
||||||
|
{/* Technical Specs */}
|
||||||
|
<AccordionItem value="technical-specs">
|
||||||
|
<AccordionTrigger>Technical Specifications</AccordionTrigger>
|
||||||
|
<AccordionContent className="space-y-3">
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Add custom specifications beyond the standard fields. Use for unique features.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Common Spec Examples</p>
|
||||||
|
<ul className="text-xs text-muted-foreground space-y-1 mt-1">
|
||||||
|
<li>• Track Material: "Steel" or "Wood"</li>
|
||||||
|
<li>• Propulsion Method: "LSM Launch", "Chain Lift"</li>
|
||||||
|
<li>• Train Type: "Sit-down", "Inverted", "Flying"</li>
|
||||||
|
<li>• Restraint System: "Lap bar", "Over-shoulder"</li>
|
||||||
|
<li>• Number of Trains: "3"</li>
|
||||||
|
<li>• Riders per Train: "28"</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-destructive/10 border border-destructive/20 p-3 rounded-md">
|
||||||
|
<p className="font-semibold text-sm mb-1 text-destructive">⚠️ Important: Metric Units Only</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
All measurements must use metric units (km/h, m, cm, kg). The system will convert
|
||||||
|
them to your preferred units for display. Examples: "km/h" not "mph", "m" not "ft"
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Units and Measurements */}
|
||||||
|
<AccordionItem value="units">
|
||||||
|
<AccordionTrigger>Units and Measurements</AccordionTrigger>
|
||||||
|
<AccordionContent className="space-y-3">
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
ThrillWiki stores all measurements in metric units but displays them in your preferred system.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="bg-muted p-3 rounded-md">
|
||||||
|
<p className="font-semibold text-sm mb-2">How It Works</p>
|
||||||
|
<ol className="text-xs text-muted-foreground space-y-1 list-decimal list-inside">
|
||||||
|
<li>Enter values in YOUR preferred units (metric or imperial)</li>
|
||||||
|
<li>System automatically converts to metric for storage</li>
|
||||||
|
<li>Data displays in each user's preferred unit system</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Speed</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Enter in km/h or mph (auto-converts)</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Example: 120 km/h = 74.6 mph</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Height / Length</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Enter in meters or feet (auto-converts)</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Example: 50m = 164ft</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-primary pl-3">
|
||||||
|
<p className="font-semibold text-sm">Height Requirement</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Enter in cm or inches (auto-converts)</p>
|
||||||
|
<Badge variant="secondary" className="text-xs mt-1">Example: 120cm = 47in</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
|
||||||
|
{/* Submission Process */}
|
||||||
|
<AccordionItem value="submission-process">
|
||||||
|
<AccordionTrigger>Submission Process</AccordionTrigger>
|
||||||
|
<AccordionContent className="space-y-3">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="bg-muted p-3 rounded-md">
|
||||||
|
<p className="font-semibold text-sm mb-2">How Submissions Work</p>
|
||||||
|
<ol className="text-xs text-muted-foreground space-y-2 list-decimal list-inside">
|
||||||
|
<li>Fill out the form with accurate information</li>
|
||||||
|
<li>Your submission goes to a moderation queue</li>
|
||||||
|
<li>Moderators review for accuracy and completeness</li>
|
||||||
|
<li>Approved submissions become visible on the site</li>
|
||||||
|
<li>All changes are versioned - edit history is preserved</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-green-500 pl-3">
|
||||||
|
<p className="font-semibold text-sm text-green-600">✓ Required Fields</p>
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
|
Fields marked with * are required. You cannot submit without completing these.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-blue-500 pl-3">
|
||||||
|
<p className="font-semibold text-sm text-blue-600">Source URL & Notes</p>
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
|
Always provide sources for your information. This helps moderators verify accuracy
|
||||||
|
and gives credit to original sources. Include official websites, press releases, or news articles.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
|
||||||
|
{/* Best Practices */}
|
||||||
|
<AccordionItem value="best-practices">
|
||||||
|
<AccordionTrigger>Best Practices</AccordionTrigger>
|
||||||
|
<AccordionContent className="space-y-3">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="border-l-2 border-green-500 pl-3">
|
||||||
|
<p className="font-semibold text-sm">✓ Do</p>
|
||||||
|
<ul className="text-xs text-muted-foreground space-y-1 mt-1 list-disc list-inside">
|
||||||
|
<li>Use official names from park/manufacturer sources</li>
|
||||||
|
<li>Provide accurate dates with appropriate precision</li>
|
||||||
|
<li>Include source URLs for verification</li>
|
||||||
|
<li>Add detailed descriptions that help users</li>
|
||||||
|
<li>Use proper capitalization and spelling</li>
|
||||||
|
<li>Check if the {type} already exists before creating</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-2 border-red-500 pl-3">
|
||||||
|
<p className="font-semibold text-sm text-destructive">✗ Don't</p>
|
||||||
|
<ul className="text-xs text-muted-foreground space-y-1 mt-1 list-disc list-inside">
|
||||||
|
<li>Use nicknames or unofficial names</li>
|
||||||
|
<li>Guess dates - use appropriate precision instead</li>
|
||||||
|
<li>Submit without sources or verification</li>
|
||||||
|
<li>Leave descriptions empty or vague</li>
|
||||||
|
<li>Use all caps or poor formatting</li>
|
||||||
|
<li>Create duplicates of existing entries</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-blue-50 dark:bg-blue-950 border border-blue-200 dark:border-blue-800 p-3 rounded-md">
|
||||||
|
<p className="font-semibold text-sm mb-1 text-blue-700 dark:text-blue-300">💡 Quality over Speed</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Take your time to ensure accuracy. Well-documented submissions are approved faster
|
||||||
|
and help build a reliable database for everyone.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
</ScrollArea>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user