// Local do check-in — lado da producao. Orcamento 298, Fase 1. // // A validacao de raio da migration 038 so entra em acao se o evento tiver // coordenada. Sem esta tela ela fica inerte e o check-in aceita qualquer lugar. // // COMO A PRODUCAO INFORMA O PONTO // 1) cola o link do Google Maps do local (jeito mais comum no dia a dia), ou // 2) cola "lat, lng" direto, ou // 3) toca em "Usar minha localizacao" quando ja esta no local montando. // Nao usamos API paga de geocodificacao: para uma acao por vez, colar o link do // Maps resolve e nao cria custo recorrente nem chave para gerenciar. const RAIO_PADRAO_M = 300; const RAIO_MIN_M = 50; const RAIO_MAX_M = 5000; // Aceita os formatos que o Google Maps produz no compartilhamento: // https://maps.google.com/...@-3.7420,-38.4820,17z // https://maps.google.com/?q=-3.7420,-38.4820 // ...!3d-3.7420!4d-38.4820 // "-3.7420, -38.4820" (colado na mao) function extrairCoordenadas(texto) { const t = String(texto || '').trim(); if (!t) return null; const padroes = [ /@(-?\d+\.\d+),\s*(-?\d+\.\d+)/, /[?&]q=(-?\d+\.\d+),\s*(-?\d+\.\d+)/, /!3d(-?\d+\.\d+)!4d(-?\d+\.\d+)/, /^(-?\d+\.?\d*),\s*(-?\d+\.?\d*)$/, ]; for (const re of padroes) { const m = t.match(re); if (!m) continue; const lat = parseFloat(m[1]); const lng = parseFloat(m[2]); // Fora destes limites nao e coordenada valida — normalmente e um link // encurtado (maps.app.goo.gl), que nao carrega o ponto. if (Math.abs(lat) <= 90 && Math.abs(lng) <= 180) return { lat, lng }; } return null; } function LocalCheckInCard({ event, showToast }) { const [entrada, setEntrada] = React.useState(''); const [raio, setRaio] = React.useState(String((event && event.raio_checkin_metros) || RAIO_PADRAO_M)); const [coord, setCoord] = React.useState( (event && event.local_lat != null && event.local_lng != null) ? { lat: Number(event.local_lat), lng: Number(event.local_lng) } : null, ); const [salvando, setSalvando] = React.useState(false); const [buscandoGps, setBuscandoGps] = React.useState(false); const aplicarEntrada = (valor) => { setEntrada(valor); const c = extrairCoordenadas(valor); if (c) setCoord(c); }; const usarMinhaLocalizacao = async () => { setBuscandoGps(true); try { const p = await obterPosicao(); setCoord({ lat: p.lat, lng: p.lng }); setEntrada(p.lat.toFixed(7) + ', ' + p.lng.toFixed(7)); showToast('Localização capturada. Confira e salve.'); } catch (err) { showToast(err.message, 'danger'); } setBuscandoGps(false); }; const salvar = async () => { const raioNum = parseInt(raio, 10); if (!coord) { showToast('Informe o ponto do local antes de salvar.', 'danger'); return; } if (!Number.isFinite(raioNum) || raioNum < RAIO_MIN_M || raioNum > RAIO_MAX_M) { showToast('O raio precisa ficar entre ' + RAIO_MIN_M + ' e ' + RAIO_MAX_M + ' metros.', 'danger'); return; } setSalvando(true); const { error } = await sb.from('events').update({ local_lat: coord.lat, local_lng: coord.lng, raio_checkin_metros: raioNum, }).eq('id', event.id); setSalvando(false); if (error) { console.error('[LocalCheckInCard.salvar] Supabase error:', error); showToast('Erro ao salvar o local do check-in.', 'danger'); return; } // Mantem o objeto em memoria coerente com o banco — mesmo padrao do // handleSalvarCoordenador, em Escalas.jsx. event.local_lat = coord.lat; event.local_lng = coord.lng; event.raio_checkin_metros = raioNum; showToast('Local do check-in salvo!'); }; const mapaUrl = coord ? 'https://www.google.com/maps?q=' + coord.lat + ',' + coord.lng : null; return (