@v1ct0rbr/waze-web
v0.3.2
Published
Módulo independente de visualização de alertas do Waze (wazedatafeedintegrator). Consumível como pacote npm em qualquer app React ou standalone.
Readme
@v1ct0rbr/waze-web
Módulo independente de visualização de alertas do wazedatafeedintegrator (Waze Partner Hub).
- Standalone — roda como app de exemplo próprio (README de mapeamento de dados).
- Biblioteca — consumível em qualquer app React (Siga+/Seguir e outros) via
npm, com isolamento arquitetural, versionamento próprio e sem acoplar no host.
Arquitetura
wazedatafeedintegrator (Java - pipeline + API REST)
▲ Bearer <token estático> (server-side)
│
│ No Seguir: proxy autenticado → GET /waze/alerts/filter (Keycloak JWT)
│ Standalone: acesso direto → http://localhost:8081/alerts/filter
│
@v1ct0rbr/waze-web (este pacote: React 19 + Vite + Leaflet + TanStack Query)O token do Waze nunca vai para o browser no fluxo Seguir — o proxy
WazeProxyController
guarda app.waze.token no servidor.
Escopo de CSS
Todo o CSS da biblioteca (style.css) é escopado sob .waze-module
(gerado com postcss-prefix-selector apenas no build da lib). Isso garante que
o Bootstrap embutido no pacote não vaze para o host: seletores globais como
.btn, .card e :root viram .waze-module .btn, .waze-module .card etc.
O layout raiz do módulo usa estilos inline (independe de CSS global/escopado),
então o mesmo bundle funciona igual em app embutida e standalone.
O host não precisa importar
leaflet/dist/leaflet.css— as folhas do Leaflet já entram nowaze-web.cssescopadas. Se importar a global também, não há conflito, mas é redundante.
UI
Layout mapa-first com abas Alertas / Métricas / Sobre:
- Mapa OpenStreetMap/CARTO à esquerda, com alternância Mapa / Satélite, modos Pontos / Clusters / Calor e controles de zoom recentralização.
- Sidebar direita retrátil com abas Filtros / Lista / Camadas; a listagem
usa
@tanstack/react-virtual(linhas virtualizadas) com paginação "Ver mais". - Clicar em um marcador/linha seleciona a ocorrência e busca o detalhe completo
(
GET /alerts/{uuid}) sob demanda. - Tema light/dark alternável na interface (atributos
data-waze-themeedata-bs-themeno root do módulo). - Camadas GeoJSON opcionais (URL ou upload), com visibilidade/cor por camada.
Mapeamento de dados (importante)
| Campo da API | Tipo | Observação |
|---|---|---|
| uuid | string | chave primária (dedup do feed) |
| type | JAM / HAZARD / ACCIDENT / … | tipo do alerta |
| subtype | string | subtipo |
| pubMillis | number | epoch em ms → new Date(pubMillis) |
| location.x | number | longitude |
| location.y | number | latitude |
| confidence, reliability, reportRating | int | 0–100 (rating 0–5) — apenas no DTO completo (detalhe) |
| street, city, country | string | localização textual — apenas no DTO completo (detalhe) |
A listagem (/alerts/filter) retorna o DTO mínimo WazeAlertSummaryDTO (WazeAlertSummary no TS) com apenas uuid, type, subtype, pubMillis e location; o WazeAlertDTO completo (WazeAlert) é usado apenas na ingestão e no detalhe (/alerts/{uuid}).
O Leaflet espera [latitude, longitude]; use o helper exportado toLatLng().
Endpoints mapeados no WazeApi (espelham o controller Java):
- Alertas:
GET /alerts/filter?type=HAZARD&minReliability=5&…(máx. 1000, paginação porlimit/offset),GET /alerts/{uuid}(DTO completo) - Métricas:
GET /alerts/metrics,/alerts/metrics/top-cities,/alerts/metrics/by-type,/alerts/metrics/by-subtype - Catálogo (exige admin-token):
/types,/subtypes,/types/{id}/subtypes - Auxiliares:
/cities?q=…,/countries,/road-types
Rodar standalone
npm install
cp .env.example .env # ajuste VITE_WAZE_API_URL e token
npm run dev # http://localhost:5174Variáveis de ambiente (.env):
| Variável | Padrão | Descrição |
|---|---|---|
| VITE_WAZE_API_URL | http://localhost:8081 | URL base da API (ou /waze via proxy do Seguir) |
| VITE_WAZE_API_TOKEN | — | token estático (acesso direto) |
| VITE_WAZE_INITIAL_DEFAULT_LATITUDE/LONGITUDE/ZOOM | — | centro/zoom inicial do mapa |
| VITE_WAZE_COMPANY_ACRONYM / VITE_WAZE_COMPANY_NAME | — | identidade na barra |
| VITE_WAZE_THEME | light | tema inicial (light | dark) |
Usar como pacote
npm run build:lib # gera dist/ (ESM + .d.ts + waze-web.css)No host React (ex.: sigamais-admin):
import '@v1ct0rbr/waze-web/style.css'
import { WazeAlertsModule } from '@v1ct0rbr/waze-web'
// Em uma rota do host, ex.: <Route path="/waze" />
<WazeAlertsModule
baseUrl="/waze" // proxy do backend do Seguir
getToken={() => localStorage.getItem('access_token')} // JWT Keycloak do host
/>No modo acesso direto à API do Waze (outros sistemas):
<WazeAlertsModule baseUrl="https://waze-api.derpb.com.br" token={process.env.WAZE_TOKEN} />Acesso a dados sem UI (@v1ct0rbr/waze-web/data)
Para integrar as ocorrências externamente sem re-mapear DTOs nem puxar a
camada de UI (React/Leaflet/TanStack), consuma o subpath data:
import { WazeApi, WazeApiError } from '@v1ct0rbr/waze-web/data'
import type { WazeAlertSummary, AlertFilter } from '@v1ct0rbr/waze-web/data'
const api = new WazeApi({ baseUrl: '/waze', getToken: () => localStorage.getItem('access_token') })
// ou acesso direto: new WazeApi({ baseUrl: 'https://waze-api.derpb.com.br', token: '...' })
const filter: AlertFilter = { startDate, endDate, type: 'HAZARD', limit: 100 }
const alerts: WazeAlertSummary[] = await api.filterAlerts(filter)
const detail = await api.getByUuid(uuid)O bundle waze-data é framework-agnóstico e expõe o WazeApi (client tipado)
e todos os DTOs (WazeAlert, WazeAlertSummary, AlertFilter, métricas,
catálogos). Filtros e detalhes usam exatamente os mesmos DTOs da UI, eliminando
mapeamentos duplicados e acoplamento de integração nas aplicações consumidoras.
Props de WazeAlertsModule
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| baseUrl | string | — | URL base da API (proxy /waze ou API direta) |
| token | string? | — | token estático (acesso direto) |
| getToken | () => string \| null \| Promise<...>? | — | resolvedor dinâmico de token (Keycloak do host) |
| fetch | typeof fetch? | global | transporte injetável |
| timeoutMs | number? | 15000 | timeout das requisições |
| center | [lat, lng]? | [-7.1, -35.9] | centro inicial do mapa |
| zoom | number? | 7 | zoom inicial |
| refetchIntervalMs | number? | 60000 | auto-refresh dos alertas |
| layers | WazeLayer[]? | — | camadas GeoJSON opcionais exibidas no mapa. Pode ser por url (fetch autenticado com getToken quando fornecido) ou por data já carregado pelo host |
| theme | 'light' \| 'dark' | 'light' | tema visual inicial |
| syncThemeToBody | boolean | false | aplica o tema também ao <body> (só em uso standalone) |
Scripts
npm run dev # app standalone (exemplo)
npm run build # build standalone
npm run build:lib # build da biblioteca (dist/)
npm run typecheck # tsc --noEmit
npm run test # vitest
npm run lint # eslint