/** * Enhanced Validation Messages * Provides contextual, helpful error messages with examples */ export const validationMessages = { slug: { format: 'Slug must contain only lowercase letters, numbers, and hyphens. Example: "steel-vengeance" or "millennium-force"', required: 'Slug is required. It will be used in the URL. Example: "fury-325"', duplicate: 'This slug is already in use. Try adding a location or number: "thunder-run-kentucky"', }, url: { format: 'Must be a valid URL starting with http:// or https://. Example: "https://www.cedarpoint.com"', protocol: 'URL must start with http:// or https://. Add the protocol to your URL.', }, email: { format: 'Must be a valid email address. Example: "contact@park.com"', }, phone: { format: 'Enter phone number in any format. Examples: "+1-419-555-0123" or "(419) 555-0123"', maxLength: (max: number) => `Phone number must be less than ${max} characters`, }, dates: { future: 'Opening date cannot be in the future. Use today or an earlier date.', closingBeforeOpening: 'Closing date must be after opening date. Check both dates for accuracy.', invalidFormat: 'Invalid date format. Use the date picker or enter in YYYY-MM-DD format.', precision: 'Select how precise this date is (exact, month, year, etc.)', }, numbers: { heightRequirement: 'Height must be in centimeters, between 0-300. Example: "122" for 122cm (48 inches)', speed: 'Speed must be in km/h, between 0-500. Example: "193" for 193 km/h (120 mph)', length: 'Length must be in meters. Example: "1981" for 1,981 meters (6,500 feet)', height: 'Height must be in meters. Example: "94" for 94 meters (310 feet)', gForce: 'G-force must be between -10 and 10. Example: "4.5" for 4.5 positive Gs', inversions: 'Number of inversions (upside-down elements). Example: "7"', capacity: 'Capacity per hour must be between 1-99,999. Example: "1200" for 1,200 riders/hour', duration: 'Duration in seconds. Example: "180" for 3 minutes', positive: 'Value must be a positive number', range: (min: number, max: number) => `Value must be between ${min} and ${max}`, }, text: { required: 'This field is required', maxLength: (max: number, current?: number) => current ? `${current}/${max} characters. Please shorten by ${current - max} characters.` : `Maximum ${max} characters`, minLength: (min: number) => `Must be at least ${min} characters`, noHtml: 'HTML tags are not allowed. Use plain text only.', trimmed: 'Extra spaces at the beginning or end will be removed', }, park: { nameRequired: 'Park name is required. Example: "Cedar Point" or "Six Flags Magic Mountain"', typeRequired: 'Select a park type (theme park, amusement park, water park, etc.)', statusRequired: 'Select the current status (operating, closed, under construction, etc.)', locationRequired: 'Location is required. Use the search to find or add a location.', operatorHelp: 'The company that operates the park (e.g., Cedar Fair, Six Flags)', ownerHelp: 'The company that owns the property (often same as operator)', }, ride: { nameRequired: 'Ride name is required. Example: "Steel Vengeance" or "Maverick"', categoryRequired: 'Select a ride category (roller coaster, flat ride, water ride, etc.)', parkRequired: 'Park is required. Select or create the park where this ride is located.', manufacturerHelp: 'Company that manufactured the ride (e.g., RMC, Intamin, B&M)', designerHelp: 'Company that designed the ride (if different from manufacturer)', trackMaterial: 'Materials used for the track. Common: Steel, Wood, Hybrid (RMC IBox)', supportMaterial: 'Materials used for support structure. Common: Steel, Wood', propulsionMethod: 'How the ride is propelled. Common: LSM Launch, Chain Lift, Hydraulic Launch', }, company: { nameRequired: 'Company name is required. Example: "Rocky Mountain Construction"', typeRequired: 'Select company type (manufacturer, designer, operator, property owner)', countryHelp: 'Country where the company is headquartered', }, units: { metricOnly: 'All measurements must be in metric units (m, km, cm, kg, km/h, etc.)', metricExamples: 'Use metric: m (meters), km/h (speed), cm (centimeters), kg (weight)', imperialNote: 'The system will automatically convert to imperial for users who prefer it', temperature: 'Temperature must be in Celsius. Example: "25" for 25°C (77°F)', }, submission: { sourceUrl: 'Where did you find this information? Helps moderators verify accuracy. Example: manufacturer website, news article, park map', notes: 'Add context for moderators. Example: "Confirmed via park press release" or "Specifications approximate"', notesMaxLength: 'Submission notes must be less than 1000 characters', }, }; /** * Common validation helpers */ export const validationHelpers = { /** * Check if a URL has proper protocol */ hasProtocol: (url: string): boolean => { return url.startsWith('http://') || url.startsWith('https://'); }, /** * Suggest adding protocol to URL */ suggestProtocol: (url: string): string => { if (!url) return ''; if (validationHelpers.hasProtocol(url)) return url; return `https://${url}`; }, /** * Format a slug from a name */ formatSlug: (name: string): string => { return name .toLowerCase() .trim() .replace(/[^a-z0-9\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-') .replace(/^-|-$/g, ''); }, /** * Check if date is in the future */ isFutureDate: (date: string | Date): boolean => { const d = new Date(date); return d > new Date(); }, /** * Format character count display */ formatCharCount: (current: number, max: number): string => { const remaining = max - current; if (remaining < 0) { return `${current}/${max} (${Math.abs(remaining)} over limit)`; } if (remaining < 50) { return `${current}/${max} (${remaining} remaining)`; } return `${current}/${max}`; }, }; /** * Field-specific validation hints for FormDescription */ export const fieldHints = { slug: 'URL-friendly identifier using lowercase letters, numbers, and hyphens only', websiteUrl: 'Official website URL (must start with https:// or http://)', email: 'Contact email for the park or ride operator', phone: 'Contact phone number in any format', heightRequirement: 'Minimum height in centimeters (metric). Will be converted for display.', ageRequirement: 'Minimum age requirement in years', capacity: 'Theoretical maximum riders per hour under optimal conditions', duration: 'Typical ride duration in seconds from dispatch to return', speed: 'Maximum speed in km/h (metric). Will be converted for display.', height: 'Maximum height in meters (metric). Will be converted for display.', length: 'Track/route length in meters (metric). Will be converted for display.', inversions: 'Total number of elements where riders go upside down (≥90 degrees)', gForce: 'Maximum positive or negative G-forces experienced', sourceUrl: 'Reference link to verify this information (Wikipedia, official site, news article, etc.)', submissionNotes: 'Help moderators understand your submission (how you verified the info, any uncertainties, etc.)', };