photum/services/mapboxService.ts
João Vitor d087cefb1b feat: Integração completa Mapbox + Upload de avatares
- Integração Mapbox GL JS para seleção interativa de localização
  - Mapa arrastável com pin para localização exata
  - Geocoding e reverse geocoding automático
  - Busca de endereços com autocomplete
  - Campos editáveis que atualizam mapa automaticamente
  - Token configurado via variável de ambiente (.env.local)

- Sistema de upload de fotos de fotógrafos
  - Upload via input de arquivo (substituiu URL)
  - Preview automático com FileReader API
  - Botão para remover foto selecionada
  - Placeholder com ícone de câmera

- Remoção de funcionalidades de uploads/álbuns
  - Removida página Albums.tsx
  - Removido sistema de attachments
  - Removida aba Inspiração para empresas
  - Criada página Inspiração com galeria de exemplo

- Melhorias de responsividade
  - Cards do mapa adaptados para mobile
  - Texto e padding reduzidos em telas pequenas

- Arquivos de configuração
  - .env.example criado
  - vite-env.d.ts para tipagem
  - MAPBOX_SETUP.md com instruções
  - Footer atualizado com serviços universitários
2025-12-02 13:55:56 -03:00

149 lines
4.5 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;
}
export interface MapboxResult {
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);
const url = `https://api.mapbox.com/geocoding/v5/mapbox.places/${encodedQuery}.json?` +
`access_token=${MAPBOX_TOKEN}&` +
`country=${country}&` +
`language=pt&` +
`limit=5`;
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] || '';
return {
description: feature.place_name,
street: feature.text,
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;
}
}