Add comprehensive help docs modal

Add a new Submission Help Dialog component accessible from park/ride forms, covering date precision, park/ride concepts, manufacturer vs designer, technical specs, units, submission process, and best practices with examples. Include trigger button integration from forms and plan for future documentation.
This commit is contained in:
gpt-engineer-app[bot]
2025-11-11 23:05:59 +00:00
parent 99c8c94e47
commit 67ce8b5a88
3 changed files with 401 additions and 8 deletions

View File

@@ -30,6 +30,7 @@ import { LocationSearch } from './LocationSearch';
import { OperatorForm } from './OperatorForm';
import { PropertyOwnerForm } from './PropertyOwnerForm';
import { Checkbox } from '@/components/ui/checkbox';
import { SubmissionHelpDialog } from '@/components/help/SubmissionHelpDialog';
const parkSchema = z.object({
name: z.string().min(1, 'Park name is required'),
@@ -314,10 +315,13 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
return (
<Card className="w-full max-w-4xl mx-auto">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<MapPin className="w-5 h-5" />
{isEditing ? 'Edit Park' : 'Create New Park'}
</CardTitle>
<div className="flex items-center justify-between">
<CardTitle className="flex items-center gap-2">
<MapPin className="w-5 h-5" />
{isEditing ? 'Edit Park' : 'Create New Park'}
</CardTitle>
<SubmissionHelpDialog type="park" variant="icon" />
</div>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit(handleFormSubmit)} className="space-y-6">

View File

@@ -35,6 +35,7 @@ import { ParkForm } from './ParkForm';
import { TechnicalSpecsEditor, validateTechnicalSpecs } from './editors/TechnicalSpecsEditor';
import { CoasterStatsEditor, validateCoasterStats } from './editors/CoasterStatsEditor';
import { FormerNamesEditor } from './editors/FormerNamesEditor';
import { SubmissionHelpDialog } from '@/components/help/SubmissionHelpDialog';
import {
convertValueToMetric,
convertValueFromMetric,
@@ -385,10 +386,13 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
<TooltipProvider>
<Card className="w-full max-w-4xl mx-auto">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Zap className="w-5 h-5" />
{isEditing ? 'Edit Ride' : 'Create New Ride'}
</CardTitle>
<div className="flex items-center justify-between">
<CardTitle className="flex items-center gap-2">
<Zap className="w-5 h-5" />
{isEditing ? 'Edit Ride' : 'Create New Ride'}
</CardTitle>
<SubmissionHelpDialog type="ride" variant="icon" />
</div>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit(handleFormSubmit)} className="space-y-6">

View 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>
);
}