Frontend: - Migração da API de Geocoding do Mapbox para o Google Maps Places API (googleMapsService.ts) no formulário de eventos, garantindo a busca correta pelo nome de locais (estádios, teatros) e com autopreenchimento. - Correção do fluxo de estado do 'horario_fim', propagando e persistindo o 'endTime' pelo DataContext, garantindo a população dos dados na edição do EventForm. - Adição da visualização do horário final na listagem do Dashboard, no EventCard, painéis de EventDetails e atualização das props defaultEndTime no EventScheduler. Backend: - Atualização e migração dos arquivos gerados pelo sqlc (models.go, agenda.sql.go) para suportar operações no novo design do banco. - Atualização síncrona dos artefatos Swagger de documentação de API (docs.go, swagger.json, swagger.yaml).
171 lines
4.9 KiB
TypeScript
171 lines
4.9 KiB
TypeScript
// Mapbox Geocoding Service
|
|
// Docs: https://docs.mapbox.com/api/search/geocoding/
|
|
|
|
export interface MapboxFeature {
|
|
id: string;
|
|
place_name: string;
|
|
center: [number, number]; // [longitude, latitude]
|
|
geometry: {
|
|
coordinates: [number, number];
|
|
};
|
|
context?: Array<{
|
|
id: string;
|
|
text: string;
|
|
short_code?: string;
|
|
}>;
|
|
place_type: string[];
|
|
text: string;
|
|
address?: string;
|
|
properties?: any;
|
|
}
|
|
|
|
export interface MapboxResult {
|
|
placeName?: string;
|
|
description: string;
|
|
street: string;
|
|
number: string;
|
|
city: string;
|
|
state: string;
|
|
zip: string;
|
|
lat: number;
|
|
lng: number;
|
|
mapLink: string;
|
|
}
|
|
|
|
// Token do Mapbox configurado no arquivo .env.local
|
|
const MAPBOX_TOKEN = import.meta.env.VITE_MAPBOX_TOKEN || "";
|
|
|
|
/**
|
|
* Busca endereços usando a API de Geocoding do Mapbox
|
|
* @param query - Texto de busca (endereço, local, etc)
|
|
* @param country - Código do país (ex: 'br' para Brasil)
|
|
*/
|
|
export async function searchMapboxLocation(
|
|
query: string,
|
|
country: string = "br"
|
|
): Promise<MapboxResult[]> {
|
|
if (!MAPBOX_TOKEN || MAPBOX_TOKEN.startsWith("YOUR_")) {
|
|
console.warn(
|
|
"⚠️ Mapbox Token não configurado. Configure em services/mapboxService.ts"
|
|
);
|
|
return [];
|
|
}
|
|
|
|
try {
|
|
const encodedQuery = encodeURIComponent(query);
|
|
let url =
|
|
`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodedQuery}.json?` +
|
|
`access_token=${MAPBOX_TOKEN}&` +
|
|
`country=${country}&` +
|
|
`language=pt&` +
|
|
`types=poi,address&` +
|
|
`limit=10`;
|
|
|
|
// Removed proximity bias to prevent Mapbox from hiding national POIs (like Estádio Pacaembu)
|
|
// when the user's region is set far away from the POI.
|
|
|
|
console.log("🔍 Buscando endereço:", query);
|
|
const response = await fetch(url);
|
|
|
|
if (!response.ok) {
|
|
console.error("❌ Erro na API Mapbox:", response.statusText);
|
|
throw new Error(`Mapbox API error: ${response.statusText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log("✅ Resultados encontrados:", data.features?.length || 0);
|
|
|
|
if (!data.features || data.features.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
return data.features.map((feature: MapboxFeature) => {
|
|
// Extrair informações do contexto
|
|
const context = feature.context || [];
|
|
const place = context.find((c) => c.id.startsWith("place"));
|
|
const region = context.find((c) => c.id.startsWith("region"));
|
|
const postcode = context.find((c) => c.id.startsWith("postcode"));
|
|
|
|
// Extrair número do endereço
|
|
const addressMatch =
|
|
feature.address || feature.text.match(/\d+/)?.[0] || "";
|
|
|
|
let placeName = undefined;
|
|
let street = feature.text;
|
|
|
|
if (feature.place_type.includes("poi")) {
|
|
placeName = feature.text;
|
|
street = feature.properties?.address || feature.place_name.split(",")[0];
|
|
}
|
|
|
|
return {
|
|
placeName,
|
|
description: feature.place_name,
|
|
street,
|
|
number: addressMatch,
|
|
city: place?.text || "",
|
|
state: region?.short_code?.replace("BR-", "") || region?.text || "",
|
|
zip: postcode?.text || "",
|
|
lat: feature.center[1],
|
|
lng: feature.center[0],
|
|
mapLink: `https://www.google.com/maps/search/?api=1&query=${feature.center[1]},${feature.center[0]}`,
|
|
};
|
|
});
|
|
} catch (error) {
|
|
console.error("Erro ao buscar localização no Mapbox:", error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Busca reversa: converte coordenadas em endereço
|
|
* Retorna o endereço completo baseado nas coordenadas do pin
|
|
*/
|
|
export async function reverseGeocode(
|
|
lat: number,
|
|
lng: number
|
|
): Promise<MapboxResult | null> {
|
|
if (!MAPBOX_TOKEN || MAPBOX_TOKEN.startsWith("YOUR_")) {
|
|
console.warn("⚠️ Mapbox Token não configurado");
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const url =
|
|
`https://api.mapbox.com/geocoding/v5/mapbox.places/${lng},${lat}.json?` +
|
|
`access_token=${MAPBOX_TOKEN}&` +
|
|
`language=pt&` +
|
|
`types=address,place`;
|
|
|
|
const response = await fetch(url);
|
|
const data = await response.json();
|
|
|
|
if (data.features && data.features.length > 0) {
|
|
const feature = data.features[0];
|
|
const context = feature.context || [];
|
|
const place = context.find((c: any) => c.id.startsWith("place"));
|
|
const region = context.find((c: any) => c.id.startsWith("region"));
|
|
const postcode = context.find((c: any) => c.id.startsWith("postcode"));
|
|
|
|
// Extrair número se houver
|
|
const addressMatch = feature.address || "";
|
|
|
|
return {
|
|
description: feature.place_name,
|
|
street: feature.text,
|
|
number: addressMatch.toString(),
|
|
city: place?.text || "",
|
|
state: region?.short_code?.replace("BR-", "") || region?.text || "",
|
|
zip: postcode?.text || "",
|
|
lat,
|
|
lng,
|
|
mapLink: `https://www.google.com/maps/search/?api=1&query=${lat},${lng}`,
|
|
};
|
|
}
|
|
|
|
return null;
|
|
} catch (error) {
|
|
console.error("Erro no reverse geocode:", error);
|
|
return null;
|
|
}
|
|
}
|