diff --git a/src/components/admin/DesignerForm.tsx b/src/components/admin/DesignerForm.tsx index 9291f665..bc72b0e9 100644 --- a/src/components/admin/DesignerForm.tsx +++ b/src/components/admin/DesignerForm.tsx @@ -12,9 +12,8 @@ import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { SlugField } from '@/components/ui/slug-field'; import { Ruler, Save, X } from 'lucide-react'; -import { Combobox } from '@/components/ui/combobox'; -import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; import { useUserRole } from '@/hooks/useUserRole'; +import { HeadquartersLocationInput } from './HeadquartersLocationInput'; import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-date-input'; import { submitDesignerCreation, submitDesignerUpdate } from '@/lib/entitySubmissionHelpers'; @@ -58,7 +57,6 @@ interface DesignerFormProps { export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormProps) { const { isModerator } = useUserRole(); - const { headquarters } = useCompanyHeadquarters(); const { user } = useAuth(); const navigate = useNavigate(); @@ -199,14 +197,13 @@ export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormPr
- setValue('headquarters_location', value)} - placeholder="Select or type location" - searchPlaceholder="Search locations..." - emptyText="No locations found" + setValue('headquarters_location', value)} /> +

+ Search OpenStreetMap for accurate location data, or manually enter location name. +

diff --git a/src/components/admin/HeadquartersLocationInput.tsx b/src/components/admin/HeadquartersLocationInput.tsx new file mode 100644 index 00000000..4bf4193c --- /dev/null +++ b/src/components/admin/HeadquartersLocationInput.tsx @@ -0,0 +1,188 @@ +import { useState, useEffect } from 'react'; +import { Input } from '@/components/ui/input'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Search, Edit, MapPin, Loader2, X } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; + +interface LocationResult { + place_id: number; + display_name: string; + address?: { + city?: string; + town?: string; + village?: string; + state?: string; + country?: string; + }; +} + +interface HeadquartersLocationInputProps { + value: string; + onChange: (value: string) => void; + disabled?: boolean; + className?: string; +} + +export function HeadquartersLocationInput({ + value, + onChange, + disabled = false, + className +}: HeadquartersLocationInputProps) { + const [mode, setMode] = useState<'search' | 'manual'>('search'); + const [searchQuery, setSearchQuery] = useState(''); + const [results, setResults] = useState([]); + const [isSearching, setIsSearching] = useState(false); + const [showResults, setShowResults] = useState(false); + + // Debounced search effect + useEffect(() => { + if (!searchQuery || searchQuery.length < 2) { + setResults([]); + setShowResults(false); + return; + } + + const timeoutId = setTimeout(async () => { + setIsSearching(true); + try { + const response = await fetch( + `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent( + searchQuery + )}&limit=5&addressdetails=1`, + { + headers: { + 'User-Agent': 'ThemeParkArchive/1.0' + } + } + ); + + if (response.ok) { + const data = await response.json(); + setResults(data); + setShowResults(true); + } + } catch (error) { + console.error('Error searching locations:', error); + } finally { + setIsSearching(false); + } + }, 500); + + return () => clearTimeout(timeoutId); + }, [searchQuery]); + + const formatLocation = (result: LocationResult): string => { + const { city, town, village, state, country } = result.address || {}; + const cityName = city || town || village; + + if (cityName && state && country) { + return `${cityName}, ${state}, ${country}`; + } else if (cityName && country) { + return `${cityName}, ${country}`; + } else if (country) { + return country; + } + return result.display_name; + }; + + const handleSelectLocation = (result: LocationResult) => { + const formatted = formatLocation(result); + onChange(formatted); + setSearchQuery(''); + setShowResults(false); + setResults([]); + }; + + const handleClear = () => { + onChange(''); + setSearchQuery(''); + setResults([]); + setShowResults(false); + }; + + return ( +
+ setMode(val as 'search' | 'manual')}> + + + + Search Location + + + + Manual Entry + + + + +
+ setSearchQuery(e.target.value)} + placeholder="Search for location (e.g., Munich, Germany)..." + disabled={disabled} + className="pr-10" + /> + {isSearching && ( + + )} +
+ + {showResults && results.length > 0 && ( +
+ {results.map((result) => ( + + ))} +
+ )} + + {showResults && results.length === 0 && !isSearching && ( +

+ No locations found. Try a different search term. +

+ )} + + {value && ( +
+ + {value} + +
+ )} +
+ + + onChange(e.target.value)} + placeholder="Enter location manually..." + disabled={disabled} + /> +

+ Enter any location text. For better data quality, use Search mode. +

+
+
+
+ ); +} diff --git a/src/components/admin/ManufacturerForm.tsx b/src/components/admin/ManufacturerForm.tsx index 48e8a8c5..37dd07b7 100644 --- a/src/components/admin/ManufacturerForm.tsx +++ b/src/components/admin/ManufacturerForm.tsx @@ -12,9 +12,8 @@ import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { SlugField } from '@/components/ui/slug-field'; import { Building2, Save, X } from 'lucide-react'; -import { Combobox } from '@/components/ui/combobox'; -import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; import { useUserRole } from '@/hooks/useUserRole'; +import { HeadquartersLocationInput } from './HeadquartersLocationInput'; import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-date-input'; import { submitManufacturerCreation, submitManufacturerUpdate } from '@/lib/entitySubmissionHelpers'; @@ -59,7 +58,6 @@ interface ManufacturerFormProps { export function ManufacturerForm({ onSubmit, onCancel, initialData }: ManufacturerFormProps) { const { isModerator } = useUserRole(); - const { headquarters } = useCompanyHeadquarters(); const { user } = useAuth(); const navigate = useNavigate(); @@ -200,14 +198,13 @@ export function ManufacturerForm({ onSubmit, onCancel, initialData }: Manufactur
- setValue('headquarters_location', value)} - placeholder="Select or type location" - searchPlaceholder="Search locations..." - emptyText="No locations found" + setValue('headquarters_location', value)} /> +

+ Search OpenStreetMap for accurate location data, or manually enter location name. +

diff --git a/src/components/admin/OperatorForm.tsx b/src/components/admin/OperatorForm.tsx index d90a20f0..1a0dd24f 100644 --- a/src/components/admin/OperatorForm.tsx +++ b/src/components/admin/OperatorForm.tsx @@ -12,9 +12,8 @@ import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { SlugField } from '@/components/ui/slug-field'; import { FerrisWheel, Save, X } from 'lucide-react'; -import { Combobox } from '@/components/ui/combobox'; -import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; import { useUserRole } from '@/hooks/useUserRole'; +import { HeadquartersLocationInput } from './HeadquartersLocationInput'; import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-date-input'; import { submitOperatorCreation, submitOperatorUpdate } from '@/lib/entitySubmissionHelpers'; @@ -58,7 +57,6 @@ interface OperatorFormProps { export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormProps) { const { isModerator } = useUserRole(); - const { headquarters } = useCompanyHeadquarters(); const { user } = useAuth(); const navigate = useNavigate(); @@ -199,14 +197,13 @@ export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormPr
- setValue('headquarters_location', value)} - placeholder="Select or type location" - searchPlaceholder="Search locations..." - emptyText="No locations found" + setValue('headquarters_location', value)} /> +

+ Search OpenStreetMap for accurate location data, or manually enter location name. +

diff --git a/src/components/admin/PropertyOwnerForm.tsx b/src/components/admin/PropertyOwnerForm.tsx index afd2207c..b2edebca 100644 --- a/src/components/admin/PropertyOwnerForm.tsx +++ b/src/components/admin/PropertyOwnerForm.tsx @@ -12,9 +12,8 @@ import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { SlugField } from '@/components/ui/slug-field'; import { Building2, Save, X } from 'lucide-react'; -import { Combobox } from '@/components/ui/combobox'; -import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; import { useUserRole } from '@/hooks/useUserRole'; +import { HeadquartersLocationInput } from './HeadquartersLocationInput'; import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-date-input'; import { submitPropertyOwnerCreation, submitPropertyOwnerUpdate } from '@/lib/entitySubmissionHelpers'; @@ -58,7 +57,6 @@ interface PropertyOwnerFormProps { export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyOwnerFormProps) { const { isModerator } = useUserRole(); - const { headquarters } = useCompanyHeadquarters(); const { user } = useAuth(); const navigate = useNavigate(); @@ -199,14 +197,13 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO
- setValue('headquarters_location', value)} - placeholder="Select or type location" - searchPlaceholder="Search locations..." - emptyText="No locations found" + setValue('headquarters_location', value)} /> +

+ Search OpenStreetMap for accurate location data, or manually enter location name. +

diff --git a/src/components/admin/index.ts b/src/components/admin/index.ts index 429fae97..c9642531 100644 --- a/src/components/admin/index.ts +++ b/src/components/admin/index.ts @@ -1,6 +1,7 @@ // Admin components barrel exports export { AdminPageLayout } from './AdminPageLayout'; export { DesignerForm } from './DesignerForm'; +export { HeadquartersLocationInput } from './HeadquartersLocationInput'; export { LocationSearch } from './LocationSearch'; export { ManufacturerForm } from './ManufacturerForm'; export { MarkdownEditor } from './MarkdownEditor';