Spaces:
Running
Running
Guilherme Silberfarb Costa commited on
Commit ·
fce4fe8
1
Parent(s): c485ae0
correcao de erro apos adicao de avaliandos
Browse files- backend/app/api/pesquisa.py +17 -16
- backend/app/core/map_layers.py +21 -0
- backend/app/services/pesquisa_service.py +110 -44
- frontend/src/api.js +13 -1
- frontend/src/components/PesquisaTab.jsx +64 -155
- frontend/src/styles.css +23 -11
backend/app/api/pesquisa.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
import json
|
|
|
|
| 4 |
|
| 5 |
from fastapi import APIRouter, HTTPException, Query
|
| 6 |
from pydantic import BaseModel
|
|
@@ -37,27 +38,27 @@ def _parse_avaliandos_geo_json(raw: str | None) -> list[dict]:
|
|
| 37 |
|
| 38 |
|
| 39 |
class AvaliandoGeoPayload(BaseModel):
|
| 40 |
-
id:
|
| 41 |
-
label:
|
| 42 |
-
lat:
|
| 43 |
-
lon:
|
| 44 |
-
logradouro:
|
| 45 |
-
numero_usado:
|
| 46 |
-
cdlog:
|
| 47 |
-
origem:
|
| 48 |
|
| 49 |
|
| 50 |
class MapaModelosPayload(BaseModel):
|
| 51 |
modelos_ids: list[str]
|
| 52 |
-
avaliando_lat:
|
| 53 |
-
avaliando_lon:
|
| 54 |
avaliandos: list[AvaliandoGeoPayload] | None = None
|
| 55 |
-
modo_exibicao:
|
| 56 |
-
criterio_espacial:
|
| 57 |
-
trabalhos_tecnicos_modelos_modo:
|
| 58 |
-
trabalhos_tecnicos_proximidade_modo:
|
| 59 |
-
trabalhos_tecnicos_modo:
|
| 60 |
-
trabalhos_tecnicos_raio_m:
|
| 61 |
|
| 62 |
|
| 63 |
class LocalizacaoAvaliandoPayload(BaseModel):
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
import json
|
| 4 |
+
from typing import Any
|
| 5 |
|
| 6 |
from fastapi import APIRouter, HTTPException, Query
|
| 7 |
from pydantic import BaseModel
|
|
|
|
| 38 |
|
| 39 |
|
| 40 |
class AvaliandoGeoPayload(BaseModel):
|
| 41 |
+
id: Any = None
|
| 42 |
+
label: Any = None
|
| 43 |
+
lat: Any = None
|
| 44 |
+
lon: Any = None
|
| 45 |
+
logradouro: Any = None
|
| 46 |
+
numero_usado: Any = None
|
| 47 |
+
cdlog: Any = None
|
| 48 |
+
origem: Any = None
|
| 49 |
|
| 50 |
|
| 51 |
class MapaModelosPayload(BaseModel):
|
| 52 |
modelos_ids: list[str]
|
| 53 |
+
avaliando_lat: Any = None
|
| 54 |
+
avaliando_lon: Any = None
|
| 55 |
avaliandos: list[AvaliandoGeoPayload] | None = None
|
| 56 |
+
modo_exibicao: Any = "pontos"
|
| 57 |
+
criterio_espacial: Any = None
|
| 58 |
+
trabalhos_tecnicos_modelos_modo: Any = None
|
| 59 |
+
trabalhos_tecnicos_proximidade_modo: Any = None
|
| 60 |
+
trabalhos_tecnicos_modo: Any = None
|
| 61 |
+
trabalhos_tecnicos_raio_m: Any = 1000
|
| 62 |
|
| 63 |
|
| 64 |
class LocalizacaoAvaliandoPayload(BaseModel):
|
backend/app/core/map_layers.py
CHANGED
|
@@ -174,6 +174,17 @@ def add_trabalhos_tecnicos_markers(
|
|
| 174 |
endereco_texto = ", ".join(endereco_parts) or label or "Endereco nao informado"
|
| 175 |
modelos_texto = ", ".join(modelos) or "Modelo nao informado"
|
| 176 |
modelos_label = "Modelo" if len(modelos) == 1 else "Modelos"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
|
| 178 |
if trabalho_id:
|
| 179 |
payload_json = json.dumps(
|
|
@@ -199,6 +210,16 @@ def add_trabalhos_tecnicos_markers(
|
|
| 199 |
f"<div style='margin-bottom:6px;'>{trabalho_nome_html}</div>"
|
| 200 |
+ (f"<div><span style='color:#666;'>Tipo:</span> {escape(tipo_label)}</div>" if tipo_label else "")
|
| 201 |
+ f"<div><span style='color:#666;'>Endereco:</span> {escape(endereco_texto)}</div>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
+ f"<div><span style='color:#666;'>{escape(modelos_label)}:</span> {escape(modelos_texto)}</div>"
|
| 203 |
+ "</div>"
|
| 204 |
)
|
|
|
|
| 174 |
endereco_texto = ", ".join(endereco_parts) or label or "Endereco nao informado"
|
| 175 |
modelos_texto = ", ".join(modelos) or "Modelo nao informado"
|
| 176 |
modelos_label = "Modelo" if len(modelos) == 1 else "Modelos"
|
| 177 |
+
avaliandos_proximos = item.get("avaliandos_proximos") if isinstance(item.get("avaliandos_proximos"), list) else []
|
| 178 |
+
avaliandos_proximos_texto = ", ".join(
|
| 179 |
+
(
|
| 180 |
+
f"{str(entry.get('label') or '').strip()} ({str(entry.get('distancia_label') or '').strip()})"
|
| 181 |
+
if str(entry.get("distancia_label") or "").strip()
|
| 182 |
+
else str(entry.get("label") or "").strip()
|
| 183 |
+
)
|
| 184 |
+
for entry in avaliandos_proximos
|
| 185 |
+
if str(entry.get("label") or "").strip()
|
| 186 |
+
)
|
| 187 |
+
distancia_min_label = str(item.get("distancia_label_min") or "").strip()
|
| 188 |
|
| 189 |
if trabalho_id:
|
| 190 |
payload_json = json.dumps(
|
|
|
|
| 210 |
f"<div style='margin-bottom:6px;'>{trabalho_nome_html}</div>"
|
| 211 |
+ (f"<div><span style='color:#666;'>Tipo:</span> {escape(tipo_label)}</div>" if tipo_label else "")
|
| 212 |
+ f"<div><span style='color:#666;'>Endereco:</span> {escape(endereco_texto)}</div>"
|
| 213 |
+
+ (
|
| 214 |
+
f"<div><span style='color:#666;'>Menor distancia:</span> {escape(distancia_min_label)}</div>"
|
| 215 |
+
if distancia_min_label
|
| 216 |
+
else ""
|
| 217 |
+
)
|
| 218 |
+
+ (
|
| 219 |
+
f"<div><span style='color:#666;'>Proximo de:</span> {escape(avaliandos_proximos_texto)}</div>"
|
| 220 |
+
if avaliandos_proximos_texto
|
| 221 |
+
else ""
|
| 222 |
+
)
|
| 223 |
+ f"<div><span style='color:#666;'>{escape(modelos_label)}:</span> {escape(modelos_texto)}</div>"
|
| 224 |
+ "</div>"
|
| 225 |
)
|
backend/app/services/pesquisa_service.py
CHANGED
|
@@ -707,6 +707,76 @@ def _chave_unica_trabalho_tecnico(item: dict[str, Any]) -> tuple[str, str, str,
|
|
| 707 |
)
|
| 708 |
|
| 709 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 710 |
def gerar_mapa_modelos(
|
| 711 |
modelos_ids: list[str],
|
| 712 |
limite_pontos_por_modelo: int = 0,
|
|
@@ -833,16 +903,13 @@ def gerar_mapa_modelos(
|
|
| 833 |
|
| 834 |
avaliandos_tecnicos_proximos: list[dict[str, Any]] | None = None
|
| 835 |
if (
|
| 836 |
-
|
| 837 |
-
and aval_lat is not None
|
| 838 |
-
and aval_lon is not None
|
| 839 |
and trabalhos_tecnicos_proximidade_modo_norm == TRABALHOS_TECNICOS_PROXIMIDADE_ATIVADA
|
| 840 |
):
|
| 841 |
-
avaliandos_tecnicos_proximos =
|
| 842 |
-
|
| 843 |
-
aval_lon,
|
| 844 |
trabalhos_tecnicos_raio_m_norm,
|
| 845 |
-
|
| 846 |
)
|
| 847 |
for item in avaliandos_tecnicos_proximos or []:
|
| 848 |
prox_lat, prox_lon = _normalizar_coordenadas_avaliando(item.get("coord_lat"), item.get("coord_lon"))
|
|
@@ -859,24 +926,14 @@ def gerar_mapa_modelos(
|
|
| 859 |
total_pontos = 0
|
| 860 |
for modelo in modelos_plotados:
|
| 861 |
total_pontos += int(modelo["total_pontos"])
|
| 862 |
-
|
| 863 |
-
|
| 864 |
-
|
| 865 |
-
|
| 866 |
-
|
| 867 |
-
|
| 868 |
-
|
| 869 |
-
|
| 870 |
-
),
|
| 871 |
-
"cobertura": _renderizar_mapa_modelos(
|
| 872 |
-
modelos_plotados,
|
| 873 |
-
bounds,
|
| 874 |
-
avaliandos_geo,
|
| 875 |
-
"cobertura",
|
| 876 |
-
avaliandos_tecnicos_proximos=avaliandos_tecnicos_proximos,
|
| 877 |
-
trabalhos_tecnicos_raio_m=trabalhos_tecnicos_raio_m_norm,
|
| 878 |
-
),
|
| 879 |
-
}
|
| 880 |
|
| 881 |
total_trabalhos_modelos = len(
|
| 882 |
{
|
|
@@ -890,10 +947,9 @@ def gerar_mapa_modelos(
|
|
| 890 |
detalhe_trabalhos = (
|
| 891 |
f" Trabalhos técnicos {descricao_modelos_status}: {total_trabalhos_modelos}."
|
| 892 |
+ (
|
| 893 |
-
f" Próximos ao avaliando (até {trabalhos_tecnicos_raio_m_norm} m): {total_trabalhos_proximos}."
|
| 894 |
if (
|
| 895 |
-
|
| 896 |
-
and aval_lon is not None
|
| 897 |
and trabalhos_tecnicos_proximidade_modo_norm == TRABALHOS_TECNICOS_PROXIMIDADE_ATIVADA
|
| 898 |
)
|
| 899 |
else ""
|
|
@@ -902,9 +958,9 @@ def gerar_mapa_modelos(
|
|
| 902 |
|
| 903 |
return sanitize_value(
|
| 904 |
{
|
| 905 |
-
"mapa_html":
|
| 906 |
-
"mapa_html_pontos":
|
| 907 |
-
"mapa_html_cobertura":
|
| 908 |
"total_modelos_plotados": len(modelos_plotados),
|
| 909 |
"total_pontos": total_pontos,
|
| 910 |
"modelos_plotados": [
|
|
@@ -1068,19 +1124,29 @@ def _renderizar_mapa_modelos(
|
|
| 1068 |
camada_modelo.add_to(mapa)
|
| 1069 |
|
| 1070 |
if camada_trabalhos_proximos is not None:
|
| 1071 |
-
if
|
| 1072 |
-
|
| 1073 |
-
|
| 1074 |
-
|
| 1075 |
-
|
| 1076 |
-
|
| 1077 |
-
|
| 1078 |
-
|
| 1079 |
-
|
| 1080 |
-
|
| 1081 |
-
|
| 1082 |
-
|
| 1083 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1084 |
add_trabalhos_tecnicos_markers(camada_trabalhos_proximos, avaliandos_tecnicos_proximos or [])
|
| 1085 |
camada_trabalhos_proximos.add_to(mapa)
|
| 1086 |
|
|
|
|
| 707 |
)
|
| 708 |
|
| 709 |
|
| 710 |
+
def _listar_avaliandos_tecnicos_proximos_por_avaliandos(
|
| 711 |
+
avaliandos_geo: list[dict[str, Any]] | None,
|
| 712 |
+
raio_m: int,
|
| 713 |
+
chaves_modelos_selecionados: list[str] | None = None,
|
| 714 |
+
) -> list[dict[str, Any]]:
|
| 715 |
+
agregados: dict[tuple[str, str, str, str, str, str], dict[str, Any]] = {}
|
| 716 |
+
|
| 717 |
+
for idx, avaliando in enumerate(avaliandos_geo or []):
|
| 718 |
+
lat, lon = _normalizar_coordenadas_avaliando(avaliando.get("lat"), avaliando.get("lon"))
|
| 719 |
+
if lat is None or lon is None:
|
| 720 |
+
continue
|
| 721 |
+
|
| 722 |
+
label = str(avaliando.get("label") or f"A{idx + 1}").strip() or f"A{idx + 1}"
|
| 723 |
+
avaliando_id = str(avaliando.get("id") or label or f"avaliando-{idx + 1}").strip() or f"avaliando-{idx + 1}"
|
| 724 |
+
proximos = trabalhos_tecnicos_service.listar_avaliandos_proximos(
|
| 725 |
+
lat,
|
| 726 |
+
lon,
|
| 727 |
+
raio_m,
|
| 728 |
+
chaves_modelo_excluir=chaves_modelos_selecionados,
|
| 729 |
+
)
|
| 730 |
+
|
| 731 |
+
for item in proximos or []:
|
| 732 |
+
chave = _chave_unica_trabalho_tecnico(item)
|
| 733 |
+
distancia_m = _to_float_or_none(item.get("distancia_m"))
|
| 734 |
+
distancia_label = str(item.get("distancia_label") or "").strip() or None
|
| 735 |
+
proximidade_avaliando = {
|
| 736 |
+
"id": avaliando_id,
|
| 737 |
+
"label": label,
|
| 738 |
+
"distancia_m": distancia_m,
|
| 739 |
+
"distancia_label": distancia_label,
|
| 740 |
+
}
|
| 741 |
+
atual = agregados.get(chave)
|
| 742 |
+
if atual is None:
|
| 743 |
+
novo_item = dict(item)
|
| 744 |
+
novo_item["avaliandos_proximos"] = [proximidade_avaliando]
|
| 745 |
+
novo_item["distancia_m_min"] = distancia_m
|
| 746 |
+
novo_item["distancia_label_min"] = distancia_label
|
| 747 |
+
agregados[chave] = novo_item
|
| 748 |
+
continue
|
| 749 |
+
|
| 750 |
+
proximidades_existentes = atual.setdefault("avaliandos_proximos", [])
|
| 751 |
+
if not any(str(entry.get("id") or "").strip() == avaliando_id for entry in proximidades_existentes):
|
| 752 |
+
proximidades_existentes.append(proximidade_avaliando)
|
| 753 |
+
|
| 754 |
+
distancia_atual = _to_float_or_none(atual.get("distancia_m_min"))
|
| 755 |
+
if distancia_m is not None and (distancia_atual is None or distancia_m < distancia_atual):
|
| 756 |
+
atual["distancia_m_min"] = distancia_m
|
| 757 |
+
atual["distancia_label_min"] = distancia_label
|
| 758 |
+
|
| 759 |
+
consolidado = list(agregados.values())
|
| 760 |
+
for item in consolidado:
|
| 761 |
+
proximidades = item.get("avaliandos_proximos") if isinstance(item.get("avaliandos_proximos"), list) else []
|
| 762 |
+
proximidades.sort(
|
| 763 |
+
key=lambda entry: (
|
| 764 |
+
str(entry.get("label") or "").casefold(),
|
| 765 |
+
float(entry.get("distancia_m") or 0.0),
|
| 766 |
+
)
|
| 767 |
+
)
|
| 768 |
+
item["avaliandos_proximos"] = proximidades
|
| 769 |
+
|
| 770 |
+
consolidado.sort(
|
| 771 |
+
key=lambda item: (
|
| 772 |
+
float(item.get("distancia_m_min") or 0.0),
|
| 773 |
+
str(item.get("trabalho_nome") or "").casefold(),
|
| 774 |
+
str(item.get("label") or item.get("endereco") or "").casefold(),
|
| 775 |
+
)
|
| 776 |
+
)
|
| 777 |
+
return sanitize_value(consolidado)
|
| 778 |
+
|
| 779 |
+
|
| 780 |
def gerar_mapa_modelos(
|
| 781 |
modelos_ids: list[str],
|
| 782 |
limite_pontos_por_modelo: int = 0,
|
|
|
|
| 903 |
|
| 904 |
avaliandos_tecnicos_proximos: list[dict[str, Any]] | None = None
|
| 905 |
if (
|
| 906 |
+
avaliandos_geo
|
|
|
|
|
|
|
| 907 |
and trabalhos_tecnicos_proximidade_modo_norm == TRABALHOS_TECNICOS_PROXIMIDADE_ATIVADA
|
| 908 |
):
|
| 909 |
+
avaliandos_tecnicos_proximos = _listar_avaliandos_tecnicos_proximos_por_avaliandos(
|
| 910 |
+
avaliandos_geo,
|
|
|
|
| 911 |
trabalhos_tecnicos_raio_m_norm,
|
| 912 |
+
chaves_modelos_selecionados,
|
| 913 |
)
|
| 914 |
for item in avaliandos_tecnicos_proximos or []:
|
| 915 |
prox_lat, prox_lon = _normalizar_coordenadas_avaliando(item.get("coord_lat"), item.get("coord_lon"))
|
|
|
|
| 926 |
total_pontos = 0
|
| 927 |
for modelo in modelos_plotados:
|
| 928 |
total_pontos += int(modelo["total_pontos"])
|
| 929 |
+
mapa_html = _renderizar_mapa_modelos(
|
| 930 |
+
modelos_plotados,
|
| 931 |
+
bounds,
|
| 932 |
+
avaliandos_geo,
|
| 933 |
+
modo_exibicao_norm,
|
| 934 |
+
avaliandos_tecnicos_proximos=avaliandos_tecnicos_proximos,
|
| 935 |
+
trabalhos_tecnicos_raio_m=trabalhos_tecnicos_raio_m_norm,
|
| 936 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 937 |
|
| 938 |
total_trabalhos_modelos = len(
|
| 939 |
{
|
|
|
|
| 947 |
detalhe_trabalhos = (
|
| 948 |
f" Trabalhos técnicos {descricao_modelos_status}: {total_trabalhos_modelos}."
|
| 949 |
+ (
|
| 950 |
+
f" {'Próximos aos avaliandos' if len(avaliandos_geo) > 1 else 'Próximos ao avaliando'} (até {trabalhos_tecnicos_raio_m_norm} m): {total_trabalhos_proximos}."
|
| 951 |
if (
|
| 952 |
+
avaliandos_geo
|
|
|
|
| 953 |
and trabalhos_tecnicos_proximidade_modo_norm == TRABALHOS_TECNICOS_PROXIMIDADE_ATIVADA
|
| 954 |
)
|
| 955 |
else ""
|
|
|
|
| 958 |
|
| 959 |
return sanitize_value(
|
| 960 |
{
|
| 961 |
+
"mapa_html": mapa_html,
|
| 962 |
+
"mapa_html_pontos": mapa_html if modo_exibicao_norm == "pontos" else "",
|
| 963 |
+
"mapa_html_cobertura": mapa_html if modo_exibicao_norm == "cobertura" else "",
|
| 964 |
"total_modelos_plotados": len(modelos_plotados),
|
| 965 |
"total_pontos": total_pontos,
|
| 966 |
"modelos_plotados": [
|
|
|
|
| 1124 |
camada_modelo.add_to(mapa)
|
| 1125 |
|
| 1126 |
if camada_trabalhos_proximos is not None:
|
| 1127 |
+
if int(trabalhos_tecnicos_raio_m or 0) > 0:
|
| 1128 |
+
for idx, avaliando in enumerate(avaliandos_geo):
|
| 1129 |
+
lat_item, lon_item = _normalizar_coordenadas_avaliando(avaliando.get("lat"), avaliando.get("lon"))
|
| 1130 |
+
if lat_item is None or lon_item is None:
|
| 1131 |
+
continue
|
| 1132 |
+
label = str(avaliando.get("label") or f"A{idx + 1}").strip() or f"A{idx + 1}"
|
| 1133 |
+
tooltip_raio = (
|
| 1134 |
+
f"Raio de busca: ate {int(trabalhos_tecnicos_raio_m)} m"
|
| 1135 |
+
if len(avaliandos_geo) == 1
|
| 1136 |
+
else f"Raio de busca • {label}: ate {int(trabalhos_tecnicos_raio_m)} m"
|
| 1137 |
+
)
|
| 1138 |
+
folium.Circle(
|
| 1139 |
+
location=[lat_item, lon_item],
|
| 1140 |
+
radius=float(trabalhos_tecnicos_raio_m),
|
| 1141 |
+
color="#c62828",
|
| 1142 |
+
weight=2,
|
| 1143 |
+
opacity=0.75,
|
| 1144 |
+
fill=True,
|
| 1145 |
+
fill_color="#c62828",
|
| 1146 |
+
fill_opacity=0.06,
|
| 1147 |
+
dash_array="6,6",
|
| 1148 |
+
tooltip=tooltip_raio,
|
| 1149 |
+
).add_to(camada_trabalhos_proximos)
|
| 1150 |
add_trabalhos_tecnicos_markers(camada_trabalhos_proximos, avaliandos_tecnicos_proximos or [])
|
| 1151 |
camada_trabalhos_proximos.add_to(mapa)
|
| 1152 |
|
frontend/src/api.js
CHANGED
|
@@ -95,7 +95,19 @@ async function handleResponse(response, path = '') {
|
|
| 95 |
}
|
| 96 |
const detailMessage = typeof detail === 'string'
|
| 97 |
? detail
|
| 98 |
-
:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
if (response.status === 401 && path !== '/api/auth/login') {
|
| 101 |
setAuthToken('')
|
|
|
|
| 95 |
}
|
| 96 |
const detailMessage = typeof detail === 'string'
|
| 97 |
? detail
|
| 98 |
+
: Array.isArray(detail)
|
| 99 |
+
? detail
|
| 100 |
+
.map((item) => {
|
| 101 |
+
const loc = Array.isArray(item?.loc)
|
| 102 |
+
? item.loc.filter((part) => part !== 'body').join(' > ')
|
| 103 |
+
: ''
|
| 104 |
+
const msg = String(item?.msg || '').trim()
|
| 105 |
+
if (loc && msg) return `${loc}: ${msg}`
|
| 106 |
+
return msg || loc || ''
|
| 107 |
+
})
|
| 108 |
+
.filter(Boolean)
|
| 109 |
+
.join(' | ')
|
| 110 |
+
: String(detail?.message || detail?.detail || response.statusText || 'Erro inesperado')
|
| 111 |
|
| 112 |
if (response.status === 401 && path !== '/api/auth/login') {
|
| 113 |
setAuthToken('')
|
frontend/src/components/PesquisaTab.jsx
CHANGED
|
@@ -287,51 +287,36 @@ function buildVariablesContent(text) {
|
|
| 287 |
|
| 288 |
function LocalizacaoResumoCard({
|
| 289 |
title,
|
| 290 |
-
subtitle = '',
|
| 291 |
localizacao = null,
|
| 292 |
actions = null,
|
| 293 |
className = '',
|
| 294 |
}) {
|
| 295 |
if (!localizacao) return null
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
const classes = ['pesquisa-localizacao-registered', className].filter(Boolean).join(' ')
|
| 297 |
return (
|
| 298 |
<div className={classes}>
|
| 299 |
<div className="pesquisa-localizacao-registered-head">
|
| 300 |
<div className="pesquisa-localizacao-registered-copy">
|
| 301 |
<strong>{title}</strong>
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
{localizacao?.logradouro ? (
|
| 309 |
-
<div className="pesquisa-localizacao-summary-row">
|
| 310 |
-
<span className="pesquisa-localizacao-summary-label">Endereço</span>
|
| 311 |
-
<span className="pesquisa-localizacao-summary-value">
|
| 312 |
-
{localizacao.logradouro}
|
| 313 |
-
{localizacao?.numero_usado ? `, ${localizacao.numero_usado}` : ''}
|
| 314 |
-
</span>
|
| 315 |
-
</div>
|
| 316 |
-
) : null}
|
| 317 |
-
{localizacao?.cdlog ? (
|
| 318 |
-
<div className="pesquisa-localizacao-summary-row">
|
| 319 |
-
<span className="pesquisa-localizacao-summary-label">CDLOG</span>
|
| 320 |
-
<span className="pesquisa-localizacao-summary-value">{localizacao.cdlog}</span>
|
| 321 |
</div>
|
| 322 |
-
) : null}
|
| 323 |
-
<div className="pesquisa-localizacao-summary-row">
|
| 324 |
-
<span className="pesquisa-localizacao-summary-label">Latitude</span>
|
| 325 |
-
<span className="pesquisa-localizacao-summary-value">{Number(localizacao.lat).toFixed(6)}</span>
|
| 326 |
-
</div>
|
| 327 |
-
<div className="pesquisa-localizacao-summary-row">
|
| 328 |
-
<span className="pesquisa-localizacao-summary-label">Longitude</span>
|
| 329 |
-
<span className="pesquisa-localizacao-summary-value">{Number(localizacao.lon).toFixed(6)}</span>
|
| 330 |
-
</div>
|
| 331 |
-
<div className="pesquisa-localizacao-summary-row">
|
| 332 |
-
<span className="pesquisa-localizacao-summary-label">Origem</span>
|
| 333 |
-
<span className="pesquisa-localizacao-summary-value">{formatLocalizacaoOrigemLabel(localizacao)}</span>
|
| 334 |
</div>
|
|
|
|
| 335 |
</div>
|
| 336 |
</div>
|
| 337 |
)
|
|
@@ -972,7 +957,6 @@ export default function PesquisaTab({
|
|
| 972 |
const [localizacaoModo, setLocalizacaoModo] = useState('endereco')
|
| 973 |
const [localizacaoInputs, setLocalizacaoInputs] = useState(EMPTY_LOCATION_INPUTS)
|
| 974 |
const [avaliandosGeolocalizados, setAvaliandosGeolocalizados] = useState([])
|
| 975 |
-
const [localizacaoEditorAberto, setLocalizacaoEditorAberto] = useState(false)
|
| 976 |
const [localizacaoLoading, setLocalizacaoLoading] = useState(false)
|
| 977 |
const [localizacaoError, setLocalizacaoError] = useState('')
|
| 978 |
const [localizacaoStatus, setLocalizacaoStatus] = useState('')
|
|
@@ -1032,7 +1016,6 @@ export default function PesquisaTab({
|
|
| 1032 |
const localizacaoAtiva = avaliandosGeoPayload.length > 0
|
| 1033 |
const localizacaoMultipla = avaliandosGeoPayload.length > 1
|
| 1034 |
const avaliandoUnicoAtivo = avaliandosGeoPayload.length === 1
|
| 1035 |
-
const avaliandoPrincipal = avaliandosGeolocalizados.length === 1 ? avaliandosGeolocalizados[0] : null
|
| 1036 |
const modelosOrdenados = useMemo(
|
| 1037 |
() => (localizacaoMultipla ? sortModelosBySpatialCriterion(result.modelos || [], criterioEspacial) : (result.modelos || [])),
|
| 1038 |
[criterioEspacial, localizacaoMultipla, result.modelos],
|
|
@@ -1040,10 +1023,6 @@ export default function PesquisaTab({
|
|
| 1040 |
const resultIds = useMemo(() => modelosOrdenados.map((modelo) => modelo.id), [modelosOrdenados])
|
| 1041 |
const todosSelecionados = resultIds.length > 0 && resultIds.every((id) => selectedIds.includes(id))
|
| 1042 |
const algunsSelecionados = resultIds.some((id) => selectedIds.includes(id))
|
| 1043 |
-
const localizacaoRegistradaMensagem = localizacaoMultipla
|
| 1044 |
-
? `${formatCount(avaliandosGeoPayload.length)} avaliandos foram geolocalizados e serão usados na distância espacial dos modelos.`
|
| 1045 |
-
: (String(localizacaoStatus || '').trim()
|
| 1046 |
-
|| 'A geolocalização do avaliando foi registrada e será usada na distância espacial dos modelos.')
|
| 1047 |
const mapaHtmlAtual = mapaHtmls[mapaModoExibicao] || ''
|
| 1048 |
const mapaFoiGerado = Boolean(mapaHtmls.pontos || mapaHtmls.cobertura)
|
| 1049 |
|
|
@@ -1086,7 +1065,7 @@ export default function PesquisaTab({
|
|
| 1086 |
? 'selecionados_e_outras_versoes'
|
| 1087 |
: String(modelosModoBruto || 'selecionados')
|
| 1088 |
const proximidadeModoBruto = overrides.trabalhosTecnicosProximidadeModo ?? mapaTrabalhosTecnicosProximidadeModo
|
| 1089 |
-
const proximidadeModo =
|
| 1090 |
const raioNumero = Number(overrides.trabalhosTecnicosRaio ?? mapaTrabalhosTecnicosRaio)
|
| 1091 |
const raio = Number.isFinite(raioNumero)
|
| 1092 |
? Math.max(0, Math.min(5000, Math.round(raioNumero)))
|
|
@@ -1122,16 +1101,21 @@ export default function PesquisaTab({
|
|
| 1122 |
try {
|
| 1123 |
const response = await api.pesquisarMapaModelos(
|
| 1124 |
idsValidos,
|
| 1125 |
-
localizacaoMultipla ? avaliandosGeoPayload :
|
| 1126 |
modoExibicaoSolicitado,
|
| 1127 |
criterioEspacial,
|
| 1128 |
trabalhosTecnicosConfig.modelosModo,
|
| 1129 |
trabalhosTecnicosConfig.proximidadeModo,
|
| 1130 |
trabalhosTecnicosConfig.raio,
|
| 1131 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1132 |
setMapaHtmls({
|
| 1133 |
-
pontos:
|
| 1134 |
-
cobertura:
|
| 1135 |
})
|
| 1136 |
setMapaStatus(response.status || '')
|
| 1137 |
mapaTrabalhosTecnicosConfigRef.current = buildMapaTrabalhosTecnicosConfigKey(trabalhosTecnicosConfig)
|
|
@@ -1403,6 +1387,13 @@ export default function PesquisaTab({
|
|
| 1403 |
await carregarMapaPesquisa(selectedIds)
|
| 1404 |
}
|
| 1405 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1406 |
async function onAdminConfigSalva() {
|
| 1407 |
if (pesquisaInicializada) {
|
| 1408 |
await buscarModelos(filters, avaliandosGeolocalizados)
|
|
@@ -1411,21 +1402,6 @@ export default function PesquisaTab({
|
|
| 1411 |
await carregarContextoInicial()
|
| 1412 |
}
|
| 1413 |
|
| 1414 |
-
function onAdicionarOutroAvaliando() {
|
| 1415 |
-
setLocalizacaoInputs(EMPTY_LOCATION_INPUTS)
|
| 1416 |
-
setLocalizacaoModo('endereco')
|
| 1417 |
-
setLocalizacaoError('')
|
| 1418 |
-
setLocalizacaoStatus('')
|
| 1419 |
-
setLocalizacaoEditorAberto(true)
|
| 1420 |
-
}
|
| 1421 |
-
|
| 1422 |
-
function onCancelarInclusaoAvaliando() {
|
| 1423 |
-
setLocalizacaoInputs(EMPTY_LOCATION_INPUTS)
|
| 1424 |
-
setLocalizacaoModo('endereco')
|
| 1425 |
-
setLocalizacaoError('')
|
| 1426 |
-
setLocalizacaoEditorAberto(false)
|
| 1427 |
-
}
|
| 1428 |
-
|
| 1429 |
function onLimparFormularioLocalizacao() {
|
| 1430 |
setLocalizacaoInputs(EMPTY_LOCATION_INPUTS)
|
| 1431 |
setLocalizacaoModo('endereco')
|
|
@@ -1486,7 +1462,6 @@ export default function PesquisaTab({
|
|
| 1486 |
const nextEntries = [...avaliandosGeolocalizados, nextEntry]
|
| 1487 |
setAvaliandosGeolocalizados(nextEntries)
|
| 1488 |
setLocalizacaoInputs(EMPTY_LOCATION_INPUTS)
|
| 1489 |
-
setLocalizacaoEditorAberto(false)
|
| 1490 |
setLocalizacaoStatus(
|
| 1491 |
nextEntries.length > 1
|
| 1492 |
? `${nextEntries.length} avaliandos geolocalizados com sucesso.`
|
|
@@ -1499,24 +1474,11 @@ export default function PesquisaTab({
|
|
| 1499 |
setLocalizacaoLoading(false)
|
| 1500 |
}
|
| 1501 |
}
|
| 1502 |
-
|
| 1503 |
-
async function onLimparLocalizacao() {
|
| 1504 |
-
setLocalizacaoInputs(EMPTY_LOCATION_INPUTS)
|
| 1505 |
-
setAvaliandosGeolocalizados([])
|
| 1506 |
-
setLocalizacaoEditorAberto(false)
|
| 1507 |
-
setLocalizacaoError('')
|
| 1508 |
-
setLocalizacaoStatus('')
|
| 1509 |
-
await atualizarPesquisaAposGeolocalizacao([])
|
| 1510 |
-
}
|
| 1511 |
-
|
| 1512 |
async function onRemoverAvaliandoLocalizacao(id) {
|
| 1513 |
const nextEntries = avaliandosGeolocalizados.filter((item) => item.id !== id)
|
| 1514 |
setAvaliandosGeolocalizados(nextEntries)
|
| 1515 |
setLocalizacaoError('')
|
| 1516 |
setLocalizacaoStatus('')
|
| 1517 |
-
if (!nextEntries.length) {
|
| 1518 |
-
setLocalizacaoEditorAberto(false)
|
| 1519 |
-
}
|
| 1520 |
await atualizarPesquisaAposGeolocalizacao(nextEntries)
|
| 1521 |
}
|
| 1522 |
|
|
@@ -1648,85 +1610,30 @@ export default function PesquisaTab({
|
|
| 1648 |
subtitle="Registre um endereço ou coordenadas do avaliando. O preenchimento desta seção não é obrigatório. Se você não informar localização, a pesquisa continua normal, a distância espacial dos modelos não será calculada e o avaliando não aparecerá no mapa gerado."
|
| 1649 |
>
|
| 1650 |
<div className="pesquisa-localizacao-section">
|
| 1651 |
-
{
|
| 1652 |
-
<
|
| 1653 |
-
|
| 1654 |
-
|
| 1655 |
-
|
| 1656 |
-
|
| 1657 |
-
|
| 1658 |
-
|
| 1659 |
-
|
| 1660 |
-
|
| 1661 |
-
|
| 1662 |
-
|
| 1663 |
-
|
| 1664 |
-
|
| 1665 |
-
|
| 1666 |
-
|
| 1667 |
-
|
| 1668 |
-
|
| 1669 |
-
|
| 1670 |
-
|
| 1671 |
-
>
|
| 1672 |
-
Reiniciar geolocalização
|
| 1673 |
-
</button>
|
| 1674 |
-
</div>
|
| 1675 |
-
)}
|
| 1676 |
-
/>
|
| 1677 |
-
) : null}
|
| 1678 |
-
|
| 1679 |
-
{localizacaoMultipla ? (
|
| 1680 |
-
<div className="pesquisa-localizacao-multiple-block">
|
| 1681 |
-
<div className="pesquisa-localizacao-registered-head pesquisa-localizacao-multi-head">
|
| 1682 |
-
<div className="pesquisa-localizacao-registered-copy">
|
| 1683 |
-
<strong>Geolocalizações registradas</strong>
|
| 1684 |
-
<span>{localizacaoRegistradaMensagem}</span>
|
| 1685 |
-
</div>
|
| 1686 |
-
<div className="pesquisa-localizacao-registered-actions">
|
| 1687 |
-
<button
|
| 1688 |
-
type="button"
|
| 1689 |
-
className="pesquisa-localizacao-action pesquisa-localizacao-action-ok"
|
| 1690 |
-
onClick={localizacaoEditorAberto ? onCancelarInclusaoAvaliando : onAdicionarOutroAvaliando}
|
| 1691 |
-
disabled={localizacaoLoading || loading}
|
| 1692 |
-
>
|
| 1693 |
-
{localizacaoEditorAberto ? 'Cancelar inclusão' : 'Adicionar outro avaliando'}
|
| 1694 |
-
</button>
|
| 1695 |
-
<button
|
| 1696 |
-
type="button"
|
| 1697 |
-
className="pesquisa-localizacao-action pesquisa-localizacao-action-reset"
|
| 1698 |
-
onClick={() => void onLimparLocalizacao()}
|
| 1699 |
-
disabled={localizacaoLoading || loading}
|
| 1700 |
-
>
|
| 1701 |
-
Reiniciar geolocalizações
|
| 1702 |
-
</button>
|
| 1703 |
-
</div>
|
| 1704 |
-
</div>
|
| 1705 |
-
|
| 1706 |
-
<div className="pesquisa-localizacao-multi-grid">
|
| 1707 |
-
{avaliandosGeolocalizados.map((item, index) => (
|
| 1708 |
-
<LocalizacaoResumoCard
|
| 1709 |
-
key={item.id}
|
| 1710 |
-
title={`Avaliando ${formatAvaliandoGeoLabel(index)}`}
|
| 1711 |
-
localizacao={item}
|
| 1712 |
-
className="pesquisa-localizacao-multi-card"
|
| 1713 |
-
actions={(
|
| 1714 |
-
<button
|
| 1715 |
-
type="button"
|
| 1716 |
-
className="pesquisa-localizacao-action pesquisa-localizacao-action-reset"
|
| 1717 |
-
onClick={() => void onRemoverAvaliandoLocalizacao(item.id)}
|
| 1718 |
-
disabled={localizacaoLoading || loading}
|
| 1719 |
-
>
|
| 1720 |
-
Remover
|
| 1721 |
-
</button>
|
| 1722 |
-
)}
|
| 1723 |
-
/>
|
| 1724 |
-
))}
|
| 1725 |
-
</div>
|
| 1726 |
</div>
|
| 1727 |
) : null}
|
| 1728 |
|
| 1729 |
-
{
|
| 1730 |
<div className="pesquisa-localizacao-grid pesquisa-localizacao-grid-coords">
|
| 1731 |
<label className="pesquisa-field">
|
| 1732 |
Forma de localização
|
|
@@ -1755,7 +1662,7 @@ export default function PesquisaTab({
|
|
| 1755 |
onClick={() => void onResolverLocalizacao()}
|
| 1756 |
disabled={localizacaoLoading}
|
| 1757 |
>
|
| 1758 |
-
{localizacaoLoading ? '
|
| 1759 |
</button>
|
| 1760 |
<button
|
| 1761 |
type="button"
|
|
@@ -1810,7 +1717,7 @@ export default function PesquisaTab({
|
|
| 1810 |
onClick={() => void onResolverLocalizacao()}
|
| 1811 |
disabled={localizacaoLoading}
|
| 1812 |
>
|
| 1813 |
-
{localizacaoLoading ? '
|
| 1814 |
</button>
|
| 1815 |
<button
|
| 1816 |
type="button"
|
|
@@ -1822,7 +1729,7 @@ export default function PesquisaTab({
|
|
| 1822 |
</button>
|
| 1823 |
</div>
|
| 1824 |
</div>
|
| 1825 |
-
)
|
| 1826 |
|
| 1827 |
{localizacaoStatus && !localizacaoAtiva ? <div className="status-line">{localizacaoStatus}</div> : null}
|
| 1828 |
{localizacaoError ? <div className="error-line inline-error">{localizacaoError}</div> : null}
|
|
@@ -2154,7 +2061,7 @@ export default function PesquisaTab({
|
|
| 2154 |
<select
|
| 2155 |
{...buildSelectAutofillProps('mapaModoExibicao')}
|
| 2156 |
value={mapaModoExibicao}
|
| 2157 |
-
onChange={
|
| 2158 |
>
|
| 2159 |
<option value="pontos">Pontos representando dados de mercado</option>
|
| 2160 |
<option value="cobertura">Cobertura dos modelos</option>
|
|
@@ -2176,7 +2083,7 @@ export default function PesquisaTab({
|
|
| 2176 |
</select>
|
| 2177 |
</label>
|
| 2178 |
|
| 2179 |
-
{
|
| 2180 |
<label className="pesquisa-field pesquisa-mapa-proximidade-field">
|
| 2181 |
Trabalhos técnicos adicionais por raio
|
| 2182 |
<select
|
|
@@ -2186,14 +2093,16 @@ export default function PesquisaTab({
|
|
| 2186 |
disabled={mapaLoading || !selectedIds.length}
|
| 2187 |
>
|
| 2188 |
<option value="sem_proximidade">Não mostrar</option>
|
| 2189 |
-
<option value="proximos_ao_avaliando">
|
|
|
|
|
|
|
| 2190 |
</select>
|
| 2191 |
</label>
|
| 2192 |
) : null}
|
| 2193 |
|
| 2194 |
-
{
|
| 2195 |
<label className="pesquisa-field pesquisa-mapa-raio-field">
|
| 2196 |
-
Raio do avaliando (m)
|
| 2197 |
<div className="pesquisa-mapa-raio-control">
|
| 2198 |
<input
|
| 2199 |
type="range"
|
|
|
|
| 287 |
|
| 288 |
function LocalizacaoResumoCard({
|
| 289 |
title,
|
|
|
|
| 290 |
localizacao = null,
|
| 291 |
actions = null,
|
| 292 |
className = '',
|
| 293 |
}) {
|
| 294 |
if (!localizacao) return null
|
| 295 |
+
const endereco = String(localizacao?.logradouro || '').trim()
|
| 296 |
+
const numeroUsado = String(localizacao?.numero_usado || '').trim()
|
| 297 |
+
const enderecoTexto = endereco ? `${endereco}${numeroUsado ? `, ${numeroUsado}` : ''}` : ''
|
| 298 |
+
const badges = [
|
| 299 |
+
enderecoTexto ? { label: 'Logradouro', value: enderecoTexto } : null,
|
| 300 |
+
localizacao?.cdlog ? { label: 'CDLOG', value: String(localizacao.cdlog) } : null,
|
| 301 |
+
{ label: 'Lat', value: Number(localizacao.lat).toFixed(6) },
|
| 302 |
+
{ label: 'Lon', value: Number(localizacao.lon).toFixed(6) },
|
| 303 |
+
{ label: 'Origem', value: formatLocalizacaoOrigemLabel(localizacao) },
|
| 304 |
+
].filter(Boolean)
|
| 305 |
const classes = ['pesquisa-localizacao-registered', className].filter(Boolean).join(' ')
|
| 306 |
return (
|
| 307 |
<div className={classes}>
|
| 308 |
<div className="pesquisa-localizacao-registered-head">
|
| 309 |
<div className="pesquisa-localizacao-registered-copy">
|
| 310 |
<strong>{title}</strong>
|
| 311 |
+
<div className="pesquisa-localizacao-registered-inline-meta">
|
| 312 |
+
{badges.map((item) => (
|
| 313 |
+
<span key={`${title}-${item.label}`} className="pesquisa-localizacao-registered-inline-item">
|
| 314 |
+
<strong>{item.label}:</strong> {item.value}
|
| 315 |
+
</span>
|
| 316 |
+
))}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
</div>
|
| 319 |
+
{actions}
|
| 320 |
</div>
|
| 321 |
</div>
|
| 322 |
)
|
|
|
|
| 957 |
const [localizacaoModo, setLocalizacaoModo] = useState('endereco')
|
| 958 |
const [localizacaoInputs, setLocalizacaoInputs] = useState(EMPTY_LOCATION_INPUTS)
|
| 959 |
const [avaliandosGeolocalizados, setAvaliandosGeolocalizados] = useState([])
|
|
|
|
| 960 |
const [localizacaoLoading, setLocalizacaoLoading] = useState(false)
|
| 961 |
const [localizacaoError, setLocalizacaoError] = useState('')
|
| 962 |
const [localizacaoStatus, setLocalizacaoStatus] = useState('')
|
|
|
|
| 1016 |
const localizacaoAtiva = avaliandosGeoPayload.length > 0
|
| 1017 |
const localizacaoMultipla = avaliandosGeoPayload.length > 1
|
| 1018 |
const avaliandoUnicoAtivo = avaliandosGeoPayload.length === 1
|
|
|
|
| 1019 |
const modelosOrdenados = useMemo(
|
| 1020 |
() => (localizacaoMultipla ? sortModelosBySpatialCriterion(result.modelos || [], criterioEspacial) : (result.modelos || [])),
|
| 1021 |
[criterioEspacial, localizacaoMultipla, result.modelos],
|
|
|
|
| 1023 |
const resultIds = useMemo(() => modelosOrdenados.map((modelo) => modelo.id), [modelosOrdenados])
|
| 1024 |
const todosSelecionados = resultIds.length > 0 && resultIds.every((id) => selectedIds.includes(id))
|
| 1025 |
const algunsSelecionados = resultIds.some((id) => selectedIds.includes(id))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1026 |
const mapaHtmlAtual = mapaHtmls[mapaModoExibicao] || ''
|
| 1027 |
const mapaFoiGerado = Boolean(mapaHtmls.pontos || mapaHtmls.cobertura)
|
| 1028 |
|
|
|
|
| 1065 |
? 'selecionados_e_outras_versoes'
|
| 1066 |
: String(modelosModoBruto || 'selecionados')
|
| 1067 |
const proximidadeModoBruto = overrides.trabalhosTecnicosProximidadeModo ?? mapaTrabalhosTecnicosProximidadeModo
|
| 1068 |
+
const proximidadeModo = localizacaoAtiva ? String(proximidadeModoBruto || 'sem_proximidade') : 'sem_proximidade'
|
| 1069 |
const raioNumero = Number(overrides.trabalhosTecnicosRaio ?? mapaTrabalhosTecnicosRaio)
|
| 1070 |
const raio = Number.isFinite(raioNumero)
|
| 1071 |
? Math.max(0, Math.min(5000, Math.round(raioNumero)))
|
|
|
|
| 1101 |
try {
|
| 1102 |
const response = await api.pesquisarMapaModelos(
|
| 1103 |
idsValidos,
|
| 1104 |
+
localizacaoMultipla ? avaliandosGeoPayload : (avaliandosGeoPayload[0] || null),
|
| 1105 |
modoExibicaoSolicitado,
|
| 1106 |
criterioEspacial,
|
| 1107 |
trabalhosTecnicosConfig.modelosModo,
|
| 1108 |
trabalhosTecnicosConfig.proximidadeModo,
|
| 1109 |
trabalhosTecnicosConfig.raio,
|
| 1110 |
)
|
| 1111 |
+
const mapaHtmlSolicitado = String(
|
| 1112 |
+
response.mapa_html
|
| 1113 |
+
|| (modoExibicaoSolicitado === 'cobertura' ? response.mapa_html_cobertura : response.mapa_html_pontos)
|
| 1114 |
+
|| '',
|
| 1115 |
+
)
|
| 1116 |
setMapaHtmls({
|
| 1117 |
+
pontos: modoExibicaoSolicitado === 'pontos' ? mapaHtmlSolicitado : '',
|
| 1118 |
+
cobertura: modoExibicaoSolicitado === 'cobertura' ? mapaHtmlSolicitado : '',
|
| 1119 |
})
|
| 1120 |
setMapaStatus(response.status || '')
|
| 1121 |
mapaTrabalhosTecnicosConfigRef.current = buildMapaTrabalhosTecnicosConfigKey(trabalhosTecnicosConfig)
|
|
|
|
| 1387 |
await carregarMapaPesquisa(selectedIds)
|
| 1388 |
}
|
| 1389 |
|
| 1390 |
+
function onMapaModoExibicaoChange(event) {
|
| 1391 |
+
const nextModo = String(event?.target?.value || 'pontos')
|
| 1392 |
+
setMapaModoExibicao(nextModo)
|
| 1393 |
+
if (!mapaFoiGerado || mapaLoading || !selectedIds.length || mapaHtmls[nextModo]) return
|
| 1394 |
+
void carregarMapaPesquisa(selectedIds, { modoExibicao: nextModo })
|
| 1395 |
+
}
|
| 1396 |
+
|
| 1397 |
async function onAdminConfigSalva() {
|
| 1398 |
if (pesquisaInicializada) {
|
| 1399 |
await buscarModelos(filters, avaliandosGeolocalizados)
|
|
|
|
| 1402 |
await carregarContextoInicial()
|
| 1403 |
}
|
| 1404 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1405 |
function onLimparFormularioLocalizacao() {
|
| 1406 |
setLocalizacaoInputs(EMPTY_LOCATION_INPUTS)
|
| 1407 |
setLocalizacaoModo('endereco')
|
|
|
|
| 1462 |
const nextEntries = [...avaliandosGeolocalizados, nextEntry]
|
| 1463 |
setAvaliandosGeolocalizados(nextEntries)
|
| 1464 |
setLocalizacaoInputs(EMPTY_LOCATION_INPUTS)
|
|
|
|
| 1465 |
setLocalizacaoStatus(
|
| 1466 |
nextEntries.length > 1
|
| 1467 |
? `${nextEntries.length} avaliandos geolocalizados com sucesso.`
|
|
|
|
| 1474 |
setLocalizacaoLoading(false)
|
| 1475 |
}
|
| 1476 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1477 |
async function onRemoverAvaliandoLocalizacao(id) {
|
| 1478 |
const nextEntries = avaliandosGeolocalizados.filter((item) => item.id !== id)
|
| 1479 |
setAvaliandosGeolocalizados(nextEntries)
|
| 1480 |
setLocalizacaoError('')
|
| 1481 |
setLocalizacaoStatus('')
|
|
|
|
|
|
|
|
|
|
| 1482 |
await atualizarPesquisaAposGeolocalizacao(nextEntries)
|
| 1483 |
}
|
| 1484 |
|
|
|
|
| 1610 |
subtitle="Registre um endereço ou coordenadas do avaliando. O preenchimento desta seção não é obrigatório. Se você não informar localização, a pesquisa continua normal, a distância espacial dos modelos não será calculada e o avaliando não aparecerá no mapa gerado."
|
| 1611 |
>
|
| 1612 |
<div className="pesquisa-localizacao-section">
|
| 1613 |
+
{avaliandosGeolocalizados.length ? (
|
| 1614 |
+
<div className="pesquisa-localizacao-multi-grid">
|
| 1615 |
+
{avaliandosGeolocalizados.map((item, index) => (
|
| 1616 |
+
<LocalizacaoResumoCard
|
| 1617 |
+
key={item.id}
|
| 1618 |
+
title={`Avaliando ${index + 1}: Geolocalização registrada`}
|
| 1619 |
+
localizacao={item}
|
| 1620 |
+
className="pesquisa-localizacao-multi-card"
|
| 1621 |
+
actions={(
|
| 1622 |
+
<button
|
| 1623 |
+
type="button"
|
| 1624 |
+
className="pesquisa-localizacao-action pesquisa-localizacao-action-reset"
|
| 1625 |
+
onClick={() => void onRemoverAvaliandoLocalizacao(item.id)}
|
| 1626 |
+
disabled={localizacaoLoading || loading}
|
| 1627 |
+
>
|
| 1628 |
+
Excluir avaliando
|
| 1629 |
+
</button>
|
| 1630 |
+
)}
|
| 1631 |
+
/>
|
| 1632 |
+
))}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1633 |
</div>
|
| 1634 |
) : null}
|
| 1635 |
|
| 1636 |
+
{localizacaoModo === 'coords' ? (
|
| 1637 |
<div className="pesquisa-localizacao-grid pesquisa-localizacao-grid-coords">
|
| 1638 |
<label className="pesquisa-field">
|
| 1639 |
Forma de localização
|
|
|
|
| 1662 |
onClick={() => void onResolverLocalizacao()}
|
| 1663 |
disabled={localizacaoLoading}
|
| 1664 |
>
|
| 1665 |
+
{localizacaoLoading ? 'Adicionando...' : 'Adicionar'}
|
| 1666 |
</button>
|
| 1667 |
<button
|
| 1668 |
type="button"
|
|
|
|
| 1717 |
onClick={() => void onResolverLocalizacao()}
|
| 1718 |
disabled={localizacaoLoading}
|
| 1719 |
>
|
| 1720 |
+
{localizacaoLoading ? 'Adicionando...' : 'Adicionar'}
|
| 1721 |
</button>
|
| 1722 |
<button
|
| 1723 |
type="button"
|
|
|
|
| 1729 |
</button>
|
| 1730 |
</div>
|
| 1731 |
</div>
|
| 1732 |
+
)}
|
| 1733 |
|
| 1734 |
{localizacaoStatus && !localizacaoAtiva ? <div className="status-line">{localizacaoStatus}</div> : null}
|
| 1735 |
{localizacaoError ? <div className="error-line inline-error">{localizacaoError}</div> : null}
|
|
|
|
| 2061 |
<select
|
| 2062 |
{...buildSelectAutofillProps('mapaModoExibicao')}
|
| 2063 |
value={mapaModoExibicao}
|
| 2064 |
+
onChange={onMapaModoExibicaoChange}
|
| 2065 |
>
|
| 2066 |
<option value="pontos">Pontos representando dados de mercado</option>
|
| 2067 |
<option value="cobertura">Cobertura dos modelos</option>
|
|
|
|
| 2083 |
</select>
|
| 2084 |
</label>
|
| 2085 |
|
| 2086 |
+
{localizacaoAtiva ? (
|
| 2087 |
<label className="pesquisa-field pesquisa-mapa-proximidade-field">
|
| 2088 |
Trabalhos técnicos adicionais por raio
|
| 2089 |
<select
|
|
|
|
| 2093 |
disabled={mapaLoading || !selectedIds.length}
|
| 2094 |
>
|
| 2095 |
<option value="sem_proximidade">Não mostrar</option>
|
| 2096 |
+
<option value="proximos_ao_avaliando">
|
| 2097 |
+
{localizacaoMultipla ? 'Mostrar próximos de cada avaliando' : 'Mostrar próximos do avaliando'}
|
| 2098 |
+
</option>
|
| 2099 |
</select>
|
| 2100 |
</label>
|
| 2101 |
) : null}
|
| 2102 |
|
| 2103 |
+
{localizacaoAtiva && mapaTrabalhosTecnicosProximidadeModo === 'proximos_ao_avaliando' ? (
|
| 2104 |
<label className="pesquisa-field pesquisa-mapa-raio-field">
|
| 2105 |
+
{localizacaoMultipla ? 'Raio de cada avaliando (m)' : 'Raio do avaliando (m)'}
|
| 2106 |
<div className="pesquisa-mapa-raio-control">
|
| 2107 |
<input
|
| 2108 |
type="range"
|
frontend/src/styles.css
CHANGED
|
@@ -2613,12 +2613,6 @@ button.pesquisa-otica-btn.active:hover {
|
|
| 2613 |
color: #215a37;
|
| 2614 |
}
|
| 2615 |
|
| 2616 |
-
.pesquisa-localizacao-registered-copy span {
|
| 2617 |
-
font-size: 0.82rem;
|
| 2618 |
-
color: #496a56;
|
| 2619 |
-
line-height: 1.45;
|
| 2620 |
-
}
|
| 2621 |
-
|
| 2622 |
.pesquisa-localizacao-registered-actions {
|
| 2623 |
display: inline-flex;
|
| 2624 |
align-items: center;
|
|
@@ -2627,11 +2621,6 @@ button.pesquisa-otica-btn.active:hover {
|
|
| 2627 |
flex-wrap: wrap;
|
| 2628 |
}
|
| 2629 |
|
| 2630 |
-
.pesquisa-localizacao-multiple-block {
|
| 2631 |
-
display: grid;
|
| 2632 |
-
gap: 14px;
|
| 2633 |
-
}
|
| 2634 |
-
|
| 2635 |
.pesquisa-localizacao-multi-grid {
|
| 2636 |
display: grid;
|
| 2637 |
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
|
|
@@ -2691,6 +2680,29 @@ button.pesquisa-otica-btn.active:hover {
|
|
| 2691 |
min-width: 220px;
|
| 2692 |
}
|
| 2693 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2694 |
.pesquisa-localizacao-summary {
|
| 2695 |
grid-column: 1 / -1;
|
| 2696 |
display: grid;
|
|
|
|
| 2613 |
color: #215a37;
|
| 2614 |
}
|
| 2615 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2616 |
.pesquisa-localizacao-registered-actions {
|
| 2617 |
display: inline-flex;
|
| 2618 |
align-items: center;
|
|
|
|
| 2621 |
flex-wrap: wrap;
|
| 2622 |
}
|
| 2623 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2624 |
.pesquisa-localizacao-multi-grid {
|
| 2625 |
display: grid;
|
| 2626 |
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
|
|
|
|
| 2680 |
min-width: 220px;
|
| 2681 |
}
|
| 2682 |
|
| 2683 |
+
.pesquisa-localizacao-registered-inline-meta {
|
| 2684 |
+
display: flex;
|
| 2685 |
+
flex-wrap: wrap;
|
| 2686 |
+
gap: 4px 12px;
|
| 2687 |
+
padding-top: 2px;
|
| 2688 |
+
color: #496a56;
|
| 2689 |
+
font-size: 0.8rem;
|
| 2690 |
+
line-height: 1.4;
|
| 2691 |
+
}
|
| 2692 |
+
|
| 2693 |
+
.pesquisa-localizacao-registered-inline-item {
|
| 2694 |
+
display: inline-flex;
|
| 2695 |
+
align-items: baseline;
|
| 2696 |
+
gap: 4px;
|
| 2697 |
+
min-width: 0;
|
| 2698 |
+
overflow-wrap: anywhere;
|
| 2699 |
+
}
|
| 2700 |
+
|
| 2701 |
+
.pesquisa-localizacao-registered-inline-item strong {
|
| 2702 |
+
color: #2d5c45;
|
| 2703 |
+
font-size: 0.79rem;
|
| 2704 |
+
}
|
| 2705 |
+
|
| 2706 |
.pesquisa-localizacao-summary {
|
| 2707 |
grid-column: 1 / -1;
|
| 2708 |
display: grid;
|