mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 06:31:12 -05:00
Fix: Implement location data storage fix
This commit is contained in:
@@ -23,7 +23,6 @@ interface LocationResult {
|
||||
}
|
||||
|
||||
interface SelectedLocation {
|
||||
id?: string;
|
||||
name: string;
|
||||
city?: string;
|
||||
state_province?: string;
|
||||
@@ -32,6 +31,7 @@ interface SelectedLocation {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
timezone?: string;
|
||||
display_name: string; // Full OSM display name for reference
|
||||
}
|
||||
|
||||
interface LocationSearchProps {
|
||||
@@ -61,11 +61,10 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
||||
.from('locations')
|
||||
.select('*')
|
||||
.eq('id', locationId)
|
||||
.single();
|
||||
.maybeSingle();
|
||||
|
||||
if (data && !error) {
|
||||
setSelectedLocation({
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
city: data.city || undefined,
|
||||
state_province: data.state_province || undefined,
|
||||
@@ -74,6 +73,7 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
||||
latitude: parseFloat(data.latitude?.toString() || '0'),
|
||||
longitude: parseFloat(data.longitude?.toString() || '0'),
|
||||
timezone: data.timezone || undefined,
|
||||
display_name: data.name, // Use name as display for existing locations
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -94,12 +94,30 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Check if response is OK and content-type is JSON
|
||||
if (!response.ok) {
|
||||
console.error('OpenStreetMap API error:', response.status);
|
||||
setResults([]);
|
||||
setShowResults(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (!contentType || !contentType.includes('application/json')) {
|
||||
console.error('Invalid response format from OpenStreetMap');
|
||||
setResults([]);
|
||||
setShowResults(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setResults(data);
|
||||
setShowResults(true);
|
||||
} catch (error) {
|
||||
console.error('Error searching locations:', error);
|
||||
setResults([]);
|
||||
setShowResults(false);
|
||||
} finally {
|
||||
setIsSearching(false);
|
||||
}
|
||||
@@ -123,61 +141,18 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
||||
? `${city}, ${result.address.state || ''} ${result.address.country}`.trim()
|
||||
: result.display_name;
|
||||
|
||||
// Check if location exists in database
|
||||
const { data: existingLocation } = await supabase
|
||||
.from('locations')
|
||||
.select('*')
|
||||
.eq('latitude', latitude)
|
||||
.eq('longitude', longitude)
|
||||
.maybeSingle();
|
||||
|
||||
let locationData: SelectedLocation;
|
||||
|
||||
if (existingLocation) {
|
||||
locationData = {
|
||||
id: existingLocation.id,
|
||||
name: existingLocation.name,
|
||||
city: existingLocation.city || undefined,
|
||||
state_province: existingLocation.state_province || undefined,
|
||||
country: existingLocation.country,
|
||||
postal_code: existingLocation.postal_code || undefined,
|
||||
latitude: parseFloat(existingLocation.latitude?.toString() || '0'),
|
||||
longitude: parseFloat(existingLocation.longitude?.toString() || '0'),
|
||||
timezone: existingLocation.timezone || undefined,
|
||||
};
|
||||
} else {
|
||||
// Create new location
|
||||
const { data: newLocation, error } = await supabase
|
||||
.from('locations')
|
||||
.insert({
|
||||
name: locationName,
|
||||
city: city || null,
|
||||
state_province: result.address.state || null,
|
||||
country: result.address.country || '',
|
||||
postal_code: result.address.postcode || null,
|
||||
latitude,
|
||||
longitude,
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error || !newLocation) {
|
||||
console.error('Error creating location:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
locationData = {
|
||||
id: newLocation.id,
|
||||
name: newLocation.name,
|
||||
city: newLocation.city || undefined,
|
||||
state_province: newLocation.state_province || undefined,
|
||||
country: newLocation.country,
|
||||
postal_code: newLocation.postal_code || undefined,
|
||||
latitude: parseFloat(newLocation.latitude?.toString() || '0'),
|
||||
longitude: parseFloat(newLocation.longitude?.toString() || '0'),
|
||||
timezone: newLocation.timezone || undefined,
|
||||
};
|
||||
}
|
||||
// Build location data object (no database operations)
|
||||
const locationData: SelectedLocation = {
|
||||
name: locationName,
|
||||
city: city || undefined,
|
||||
state_province: result.address.state || undefined,
|
||||
country: result.address.country || '',
|
||||
postal_code: result.address.postcode || undefined,
|
||||
latitude,
|
||||
longitude,
|
||||
timezone: undefined, // Will be set by server during approval if needed
|
||||
display_name: result.display_name,
|
||||
};
|
||||
|
||||
setSelectedLocation(locationData);
|
||||
setSearchQuery('');
|
||||
|
||||
@@ -28,6 +28,17 @@ const parkSchema = z.object({
|
||||
status: z.string().min(1, 'Status is required'),
|
||||
opening_date: z.string().optional(),
|
||||
closing_date: z.string().optional(),
|
||||
location: z.object({
|
||||
name: z.string(),
|
||||
city: z.string().optional(),
|
||||
state_province: z.string().optional(),
|
||||
country: z.string(),
|
||||
postal_code: z.string().optional(),
|
||||
latitude: z.number(),
|
||||
longitude: z.number(),
|
||||
timezone: z.string().optional(),
|
||||
display_name: z.string(),
|
||||
}).optional(),
|
||||
location_id: z.string().uuid().optional(),
|
||||
website_url: z.string().url().optional().or(z.literal('')),
|
||||
phone: z.string().optional(),
|
||||
@@ -289,12 +300,12 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
<Label>Location</Label>
|
||||
<LocationSearch
|
||||
onLocationSelect={(location) => {
|
||||
setValue('location_id', location.id);
|
||||
setValue('location', location);
|
||||
}}
|
||||
initialLocationId={watch('location_id')}
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Search for the park's location using OpenStreetMap
|
||||
Search for the park's location using OpenStreetMap. Location will be created when submission is approved.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,21 @@ export interface ParkFormData {
|
||||
email?: string;
|
||||
operator_id?: string;
|
||||
property_owner_id?: string;
|
||||
|
||||
// Location can be stored as object for new submissions or ID for editing
|
||||
location?: {
|
||||
name: string;
|
||||
city?: string;
|
||||
state_province?: string;
|
||||
country: string;
|
||||
postal_code?: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
timezone?: string;
|
||||
display_name: string;
|
||||
};
|
||||
location_id?: string;
|
||||
|
||||
images?: ImageAssignments;
|
||||
banner_image_url?: string;
|
||||
banner_image_id?: string;
|
||||
|
||||
@@ -294,6 +294,12 @@ async function createPark(data: any, dependencyMap: Map<string, string>): Promis
|
||||
// Handle park edit
|
||||
const resolvedData = resolveDependencies(data, dependencyMap);
|
||||
|
||||
// Resolve location_id if location data is provided
|
||||
let locationId = resolvedData.location_id;
|
||||
if (resolvedData.location && !locationId) {
|
||||
locationId = await resolveLocationId(resolvedData.location);
|
||||
}
|
||||
|
||||
// Extract image assignments from ImageAssignments structure
|
||||
const imageData = extractImageAssignments(resolvedData.images);
|
||||
|
||||
@@ -311,7 +317,7 @@ async function createPark(data: any, dependencyMap: Map<string, string>): Promis
|
||||
email: resolvedData.email || null,
|
||||
operator_id: resolvedData.operator_id || null,
|
||||
property_owner_id: resolvedData.property_owner_id || null,
|
||||
location_id: resolvedData.location_id || null,
|
||||
location_id: locationId || null,
|
||||
...imageData,
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
@@ -333,6 +339,12 @@ async function createPark(data: any, dependencyMap: Map<string, string>): Promis
|
||||
validateSubmissionData(data, 'Park');
|
||||
const resolvedData = resolveDependencies(data, dependencyMap);
|
||||
|
||||
// Resolve location_id if location data is provided
|
||||
let locationId = resolvedData.location_id;
|
||||
if (resolvedData.location && !locationId) {
|
||||
locationId = await resolveLocationId(resolvedData.location);
|
||||
}
|
||||
|
||||
// Ensure unique slug
|
||||
const uniqueSlug = await ensureUniqueSlug(resolvedData.slug, 'parks');
|
||||
resolvedData.slug = uniqueSlug;
|
||||
@@ -341,7 +353,11 @@ async function createPark(data: any, dependencyMap: Map<string, string>): Promis
|
||||
const imageData = extractImageAssignments(resolvedData.images);
|
||||
|
||||
// Transform to database format
|
||||
const parkData = { ...transformParkData(resolvedData), ...imageData };
|
||||
const parkData = {
|
||||
...transformParkData(resolvedData),
|
||||
...imageData,
|
||||
location_id: locationId || null,
|
||||
};
|
||||
|
||||
// Insert into database
|
||||
const { data: park, error } = await supabase
|
||||
@@ -358,6 +374,51 @@ async function createPark(data: any, dependencyMap: Map<string, string>): Promis
|
||||
return park.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve location data to a location_id
|
||||
* Checks for existing locations by coordinates, creates new ones if needed
|
||||
*/
|
||||
async function resolveLocationId(locationData: any): Promise<string | null> {
|
||||
if (!locationData || !locationData.latitude || !locationData.longitude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if location already exists by coordinates
|
||||
const { data: existingLocation } = await supabase
|
||||
.from('locations')
|
||||
.select('id')
|
||||
.eq('latitude', locationData.latitude)
|
||||
.eq('longitude', locationData.longitude)
|
||||
.maybeSingle();
|
||||
|
||||
if (existingLocation) {
|
||||
return existingLocation.id;
|
||||
}
|
||||
|
||||
// Create new location (moderator has permission via RLS)
|
||||
const { data: newLocation, error } = await supabase
|
||||
.from('locations')
|
||||
.insert({
|
||||
name: locationData.name,
|
||||
city: locationData.city || null,
|
||||
state_province: locationData.state_province || null,
|
||||
country: locationData.country,
|
||||
postal_code: locationData.postal_code || null,
|
||||
latitude: locationData.latitude,
|
||||
longitude: locationData.longitude,
|
||||
timezone: locationData.timezone || null,
|
||||
})
|
||||
.select('id')
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
console.error('Error creating location:', error);
|
||||
throw new Error(`Failed to create location: ${error.message}`);
|
||||
}
|
||||
|
||||
return newLocation.id;
|
||||
}
|
||||
|
||||
async function createRide(data: any, dependencyMap: Map<string, string>): Promise<string> {
|
||||
const { transformRideData, validateSubmissionData } = await import('./entityTransformers');
|
||||
const { ensureUniqueSlug } = await import('./slugUtils');
|
||||
|
||||
Reference in New Issue
Block a user