mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 12:31:14 -05:00
feat: Add street address to locations
Adds a street_address column to the locations table and updates the LocationSearch component to capture, store, and display full street addresses. This includes database migration, interface updates, and formatter logic.
This commit is contained in:
@@ -14,17 +14,27 @@ interface LocationResult {
|
||||
lat: string;
|
||||
lon: string;
|
||||
address: {
|
||||
house_number?: string;
|
||||
road?: string;
|
||||
city?: string;
|
||||
town?: string;
|
||||
village?: string;
|
||||
municipality?: string;
|
||||
state?: string;
|
||||
province?: string;
|
||||
state_district?: string;
|
||||
county?: string;
|
||||
region?: string;
|
||||
territory?: string;
|
||||
country?: string;
|
||||
country_code?: string;
|
||||
postcode?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface SelectedLocation {
|
||||
name: string;
|
||||
street_address?: string;
|
||||
city?: string;
|
||||
state_province?: string;
|
||||
country: string;
|
||||
@@ -61,13 +71,14 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
||||
const loadInitialLocation = async (locationId: string): Promise<void> => {
|
||||
const { data, error } = await supabase
|
||||
.from('locations')
|
||||
.select('id, name, city, state_province, country, postal_code, latitude, longitude, timezone')
|
||||
.select('id, name, street_address, city, state_province, country, postal_code, latitude, longitude, timezone')
|
||||
.eq('id', locationId)
|
||||
.maybeSingle();
|
||||
|
||||
if (data && !error) {
|
||||
setSelectedLocation({
|
||||
name: data.name,
|
||||
street_address: data.street_address || undefined,
|
||||
city: data.city || undefined,
|
||||
state_province: data.state_province || undefined,
|
||||
country: data.country,
|
||||
@@ -150,21 +161,38 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
||||
|
||||
// Safely access address properties with fallback
|
||||
const address = result.address || {};
|
||||
const city = address.city || address.town || address.village;
|
||||
const state = address.state || '';
|
||||
const country = address.country || 'Unknown';
|
||||
|
||||
const locationName = city
|
||||
? `${city}, ${state} ${country}`.trim()
|
||||
: result.display_name;
|
||||
// Extract street address components
|
||||
const houseNumber = address.house_number || '';
|
||||
const road = address.road || '';
|
||||
const streetAddress = [houseNumber, road].filter(Boolean).join(' ').trim() || undefined;
|
||||
|
||||
// Extract city
|
||||
const city = address.city || address.town || address.village || address.municipality;
|
||||
|
||||
// Extract state/province (try multiple fields for international support)
|
||||
const state = address.state ||
|
||||
address.province ||
|
||||
address.state_district ||
|
||||
address.county ||
|
||||
address.region ||
|
||||
address.territory;
|
||||
|
||||
const country = address.country || 'Unknown';
|
||||
const postalCode = address.postcode;
|
||||
|
||||
// Build location name
|
||||
const locationParts = [streetAddress, city, state, country].filter(Boolean);
|
||||
const locationName = locationParts.join(', ');
|
||||
|
||||
// Build location data object (no database operations)
|
||||
const locationData: SelectedLocation = {
|
||||
name: locationName,
|
||||
street_address: streetAddress,
|
||||
city: city || undefined,
|
||||
state_province: state || undefined,
|
||||
country: country,
|
||||
postal_code: address.postcode || undefined,
|
||||
postal_code: postalCode || undefined,
|
||||
latitude,
|
||||
longitude,
|
||||
timezone: undefined, // Will be set by server during approval if needed
|
||||
@@ -249,6 +277,7 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-medium">{selectedLocation.name}</p>
|
||||
<div className="text-sm text-muted-foreground space-y-1 mt-1">
|
||||
{selectedLocation.street_address && <p>Street: {selectedLocation.street_address}</p>}
|
||||
{selectedLocation.city && <p>City: {selectedLocation.city}</p>}
|
||||
{selectedLocation.state_province && <p>State/Province: {selectedLocation.state_province}</p>}
|
||||
<p>Country: {selectedLocation.country}</p>
|
||||
|
||||
@@ -1615,6 +1615,7 @@ export type Database = {
|
||||
name: string
|
||||
postal_code: string | null
|
||||
state_province: string | null
|
||||
street_address: string | null
|
||||
timezone: string | null
|
||||
}
|
||||
Insert: {
|
||||
@@ -1627,6 +1628,7 @@ export type Database = {
|
||||
name: string
|
||||
postal_code?: string | null
|
||||
state_province?: string | null
|
||||
street_address?: string | null
|
||||
timezone?: string | null
|
||||
}
|
||||
Update: {
|
||||
@@ -1639,6 +1641,7 @@ export type Database = {
|
||||
name?: string
|
||||
postal_code?: string | null
|
||||
state_province?: string | null
|
||||
street_address?: string | null
|
||||
timezone?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
|
||||
64
src/lib/locationFormatter.ts
Normal file
64
src/lib/locationFormatter.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Location Formatting Utilities
|
||||
*
|
||||
* Centralized utilities for formatting location data consistently across the app.
|
||||
*/
|
||||
|
||||
export interface LocationData {
|
||||
street_address?: string | null;
|
||||
city?: string | null;
|
||||
state_province?: string | null;
|
||||
country?: string | null;
|
||||
postal_code?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format location for display
|
||||
* @param location - Location data object
|
||||
* @param includeStreet - Whether to include street address in the output
|
||||
* @returns Formatted location string or null if no location data
|
||||
*/
|
||||
export function formatLocationDisplay(
|
||||
location: LocationData | null | undefined,
|
||||
includeStreet: boolean = false
|
||||
): string | null {
|
||||
if (!location) return null;
|
||||
|
||||
const parts: string[] = [];
|
||||
|
||||
if (includeStreet && location.street_address) {
|
||||
parts.push(location.street_address);
|
||||
}
|
||||
|
||||
if (location.city) {
|
||||
parts.push(location.city);
|
||||
}
|
||||
|
||||
if (location.state_province) {
|
||||
parts.push(location.state_province);
|
||||
}
|
||||
|
||||
if (location.country) {
|
||||
parts.push(location.country);
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts.join(', ') : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format full address including street
|
||||
* @param location - Location data object
|
||||
* @returns Formatted full address or null if no location data
|
||||
*/
|
||||
export function formatFullAddress(location: LocationData | null | undefined): string | null {
|
||||
return formatLocationDisplay(location, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format location without street address (city, state, country only)
|
||||
* @param location - Location data object
|
||||
* @returns Formatted location without street or null if no location data
|
||||
*/
|
||||
export function formatLocationShort(location: LocationData | null | undefined): string | null {
|
||||
return formatLocationDisplay(location, false);
|
||||
}
|
||||
@@ -57,11 +57,13 @@ export interface LocationInfoSettings {
|
||||
* Location data structure
|
||||
*/
|
||||
export interface LocationData {
|
||||
street_address?: string;
|
||||
country?: string;
|
||||
state_province?: string;
|
||||
city?: string;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
postal_code?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,10 +73,12 @@ export function isLocationData(data: unknown): data is LocationData {
|
||||
if (typeof data !== 'object' || data === null) return false;
|
||||
const loc = data as Record<string, unknown>;
|
||||
return (
|
||||
(loc.street_address === undefined || typeof loc.street_address === 'string') &&
|
||||
(loc.country === undefined || typeof loc.country === 'string') &&
|
||||
(loc.state_province === undefined || typeof loc.state_province === 'string') &&
|
||||
(loc.city === undefined || typeof loc.city === 'string') &&
|
||||
(loc.latitude === undefined || typeof loc.latitude === 'number') &&
|
||||
(loc.longitude === undefined || typeof loc.longitude === 'number')
|
||||
(loc.longitude === undefined || typeof loc.longitude === 'number') &&
|
||||
(loc.postal_code === undefined || typeof loc.postal_code === 'string')
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user