Guilherme Silberfarb Costa commited on
Commit
1c93e8e
·
1 Parent(s): b6d4e48

correcoes esteticas

Browse files
backend/app/core/visualizacao/app.py CHANGED
@@ -1223,11 +1223,20 @@ def exportar_avaliacoes_excel_viz(estado_avaliacoes):
1223
  return gr.update(value=None, visible=False)
1224
 
1225
 
1226
- def _formatar_badge_completo(pacote):
1227
- """Retorna HTML do badge combinado: elaborador (esquerda) + variáveis (direita) com flex space-between."""
1228
  if not pacote:
1229
  return ""
1230
 
 
 
 
 
 
 
 
 
 
1231
  def _data_br(value):
1232
  texto = str(value or "").strip()
1233
  match = re.match(r"^(\d{4})-(\d{2})-(\d{2})$", texto)
@@ -1235,103 +1244,127 @@ def _formatar_badge_completo(pacote):
1235
  return f"{match.group(3)}/{match.group(2)}/{match.group(1)}"
1236
  return texto
1237
 
 
 
1238
  periodo = pacote.get("periodo_dados_mercado") or {}
1239
  data_inicial = _data_br(periodo.get("data_inicial"))
1240
  data_final = _data_br(periodo.get("data_final"))
1241
- periodo_txt = f"{data_inicial} a {data_final}" if data_inicial and data_final else ""
1242
-
1243
- # --- Lado direito: lista de variáveis ---
1244
- variaveis_html = ""
1245
- info = pacote.get("transformacoes", {}).get("info")
1246
- if info:
1247
- y_str = info[0]
1248
- y_parts = y_str.split(": ", 1)
1249
  y_nome = y_parts[0].strip()
1250
  y_transf = y_parts[1].strip() if len(y_parts) > 1 else ""
1251
- y_transf_display = "" if y_transf in ("(y)", "(x)", "y", "x", "") else y_transf
1252
- y_badge = (
1253
- "<span style='background:#cce5ff;color:#004085;border-radius:4px;"
1254
- f"padding:3px 10px;font-size:1em;font-weight:600;'>{y_nome}"
1255
- + (f" <span style='font-weight:400;font-size:0.85em;'>{y_transf_display}</span>"
1256
- if y_transf_display else "")
1257
- + "</span>"
1258
- )
1259
- x_badges = []
1260
- for item in info[1:]:
1261
- parts = item.split(": ", 1)
1262
  nome = parts[0].strip()
1263
  transf = parts[1].strip() if len(parts) > 1 else ""
1264
- transf_display = "" if transf in ("(x)", "(y)", "x", "y", "") else transf
1265
- badge = (
1266
- "<span style='background:#ced4da;color:#343a40;border-radius:4px;"
1267
- f"padding:3px 8px;font-size:1em;display:inline-block;margin:2px 3px 2px 0;'>{nome}"
1268
- + (f" <span style='color:#6c757d;font-size:1em;'>{transf_display}</span>"
1269
- if transf_display else "")
1270
- + "</span>"
1271
- )
1272
- x_badges.append(badge)
1273
- variaveis_html = (
1274
- "<div style='font-size:0.9em;line-height:2;'>"
1275
- "<div><span style='font-weight:600;color:#495057;margin-right:8px;font-size:1.1em;display:inline-block;min-width:190px;'>Variável Dependente:</span>"
1276
- + y_badge + "</div>"
1277
- + "<div style='margin-top:4px;'>"
1278
- "<span style='font-weight:600;color:#495057;margin-right:8px;font-size:1.1em;display:inline-block;min-width:190px;'>Variáveis Independentes:</span>"
1279
- + "".join(x_badges) + "</div>"
1280
  + (
1281
- "<div style='margin-top:6px;'>"
1282
- "<span style='font-weight:600;color:#495057;margin-right:8px;font-size:1.1em;display:inline-block;min-width:190px;'>Período dos dados de mercado:</span>"
1283
- f"<span style='color:#2f4458;'>{periodo_txt}</span>"
1284
- "</div>"
1285
- if periodo_txt else ""
1286
  )
1287
- + "</div>"
1288
- )
1289
- elif periodo_txt:
1290
- variaveis_html = (
1291
- "<div style='font-size:0.95em;line-height:1.7;'>"
1292
- "<span style='font-weight:600;color:#495057;margin-right:8px;display:inline-block;min-width:190px;'>Período dos dados de mercado:</span>"
1293
- f"<span style='color:#2f4458;'>{periodo_txt}</span>"
1294
- "</div>"
1295
  )
 
1296
 
1297
- # --- Lado esquerdo: elaborador ---
1298
- elaborador = pacote.get("elaborador")
1299
- nome = elaborador.get("nome_completo", "") if elaborador else ""
1300
- if not nome:
1301
- # Sem elaborador: só exibe variáveis (sem flex desnecessário)
1302
- if not variaveis_html:
1303
- return ""
1304
- return (
1305
- '<div style="background:#e9ecef; border-left:5px solid #6c757d; border-radius:6px; '
1306
- 'padding:14px 18px; margin-top:8px;">'
1307
- + variaveis_html
1308
- + '</div>'
 
 
 
 
 
 
 
 
1309
  )
 
 
1310
 
1311
- cargo = elaborador.get("cargo", "")
1312
- conselho = elaborador.get("conselho", "")
1313
- num = elaborador.get("numero_conselho", "")
1314
- estado = elaborador.get("estado_conselho", "")
1315
- matricula = elaborador.get("matricula_sem_digito", "")
1316
- lotacao = elaborador.get("lotacao", "")
1317
- linha2 = f"{cargo} · {conselho}/{estado} {num}".strip(" ·") if cargo else ""
1318
- linha3 = f"Matrícula: {matricula} · {lotacao}".strip(" ·") if matricula else ""
1319
-
1320
- elab_html = (
1321
- '<div style="line-height:1.8;">'
1322
- f'<span style="display:block; font-size:1.05em; font-weight:600; color:#212529; margin-bottom:4px;">{nome}</span>'
1323
- + (f'<span style="display:block; font-size:0.95em;">{linha2}</span>' if linha2 else '')
1324
- + (f'<span style="display:block; font-size:0.9em; color:#6c757d;">{linha3}</span>' if linha3 else '')
1325
- + '</div>'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1326
  )
1327
 
1328
  return (
1329
- '<div style="background:#e9ecef; border-left:5px solid #6c757d; border-radius:6px; '
1330
- 'padding:14px 18px; color:#495057; margin-top:8px; display:flex; '
1331
- 'justify-content:space-between; align-items:flex-start; gap:24px;">'
1332
- + elab_html
1333
- + (f'<div>{variaveis_html}</div>' if variaveis_html else '')
1334
- + '</div>'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1335
  )
1336
 
1337
 
 
1223
  return gr.update(value=None, visible=False)
1224
 
1225
 
1226
+ def _formatar_badge_completo(pacote, nome_modelo=""):
1227
+ """Retorna HTML do badge no mesmo padrão visual da aba Elaboração."""
1228
  if not pacote:
1229
  return ""
1230
 
1231
+ def _esc(value):
1232
+ return (
1233
+ str(value or "")
1234
+ .replace("&", "&amp;")
1235
+ .replace("<", "&lt;")
1236
+ .replace(">", "&gt;")
1237
+ .replace('"', "&quot;")
1238
+ )
1239
+
1240
  def _data_br(value):
1241
  texto = str(value or "").strip()
1242
  match = re.match(r"^(\d{4})-(\d{2})-(\d{2})$", texto)
 
1244
  return f"{match.group(3)}/{match.group(2)}/{match.group(1)}"
1245
  return texto
1246
 
1247
+ model_name = str(nome_modelo or "").strip() or "-"
1248
+
1249
  periodo = pacote.get("periodo_dados_mercado") or {}
1250
  data_inicial = _data_br(periodo.get("data_inicial"))
1251
  data_final = _data_br(periodo.get("data_final"))
1252
+ periodo_txt = f"{data_inicial} a {data_final}" if data_inicial and data_final else "-"
1253
+
1254
+ info_variaveis = pacote.get("transformacoes", {}).get("info") or []
1255
+ y_nome = ""
1256
+ y_transf = ""
1257
+ x_itens: list[tuple[str, str]] = []
1258
+ if info_variaveis:
1259
+ y_parts = str(info_variaveis[0]).split(": ", 1)
1260
  y_nome = y_parts[0].strip()
1261
  y_transf = y_parts[1].strip() if len(y_parts) > 1 else ""
1262
+ for item in info_variaveis[1:]:
1263
+ parts = str(item).split(": ", 1)
 
 
 
 
 
 
 
 
 
1264
  nome = parts[0].strip()
1265
  transf = parts[1].strip() if len(parts) > 1 else ""
1266
+ x_itens.append((nome, transf))
1267
+
1268
+ y_transf_display = "" if y_transf in ("(y)", "(x)", "y", "x", "") else y_transf
1269
+ y_html = (
1270
+ "<div class='section1-empty-hint'>Variável dependente não encontrada no modelo carregado.</div>"
1271
+ if not y_nome
1272
+ else (
1273
+ "<div class='variavel-badge-line'>"
1274
+ "<span class='variavel-badge-label'>Dependente:</span>"
1275
+ "<span class='variavel-chip variavel-chip-y variavel-chip-inline'>"
1276
+ + _esc(y_nome)
 
 
 
 
 
1277
  + (
1278
+ f"<span class='variavel-chip-transform'> {_esc(y_transf_display)}</span>"
1279
+ if y_transf_display
1280
+ else ""
 
 
1281
  )
1282
+ + "</span></div>"
 
 
 
 
 
 
 
1283
  )
1284
+ )
1285
 
1286
+ if x_itens:
1287
+ x_badges = []
1288
+ for nome, transf in x_itens:
1289
+ transf_display = "" if transf in ("(x)", "(y)", "x", "y", "") else transf
1290
+ x_badges.append(
1291
+ "<span class='variavel-chip'>"
1292
+ + _esc(nome)
1293
+ + (
1294
+ f"<span class='variavel-chip-transform'> {_esc(transf_display)}</span>"
1295
+ if transf_display
1296
+ else ""
1297
+ )
1298
+ + "</span>"
1299
+ )
1300
+ x_html = (
1301
+ "<div class='variavel-badge-line'>"
1302
+ "<span class='variavel-badge-label'>Independentes:</span>"
1303
+ "<div class='variavel-chip-wrap'>"
1304
+ + "".join(x_badges)
1305
+ + "</div></div>"
1306
  )
1307
+ else:
1308
+ x_html = "<div class='section1-empty-hint'>Sem variáveis independentes no modelo carregado.</div>"
1309
 
1310
+ periodo_html = (
1311
+ "<div class='variavel-badge-line'>"
1312
+ "<span class='variavel-badge-label'>Período dados:</span>"
1313
+ f"<span class='variavel-badge-value'>{_esc(periodo_txt)}</span>"
1314
+ "</div>"
1315
+ )
1316
+
1317
+ elaborador = pacote.get("elaborador") or {}
1318
+ nome_elaborador = str(elaborador.get("nome_completo") or "").strip()
1319
+ cargo = str(elaborador.get("cargo") or "").strip()
1320
+ conselho = str(elaborador.get("conselho") or "").strip()
1321
+ numero = str(elaborador.get("numero_conselho") or "").strip()
1322
+ estado = str(elaborador.get("estado_conselho") or "").strip()
1323
+ matricula = str(elaborador.get("matricula_sem_digito") or "").strip()
1324
+ lotacao = str(elaborador.get("lotacao") or "").strip()
1325
+
1326
+ conselho_reg = ""
1327
+ if conselho and estado and numero:
1328
+ conselho_reg = f"{conselho}/{estado} {numero}"
1329
+ elif conselho or numero or estado:
1330
+ conselho_reg = " ".join(part for part in [conselho, numero, estado] if part)
1331
+
1332
+ meta = [cargo, conselho_reg, f"Matrícula {matricula}" if matricula else "", lotacao]
1333
+ meta_txt = " | ".join(part for part in meta if part)
1334
+
1335
+ elaborador_html = (
1336
+ "<div class='section1-empty-hint'>Elaborador não informado no arquivo.</div>"
1337
+ if not nome_elaborador
1338
+ else (
1339
+ f"<div class='elaborador-badge-name'>{_esc(nome_elaborador)}</div>"
1340
+ + (f"<div class='elaborador-badge-meta'>{_esc(meta_txt)}</div>" if meta_txt else "")
1341
+ )
1342
  )
1343
 
1344
  return (
1345
+ "<div class='subpanel section1-group'>"
1346
+ "<h4>Informações do modelo</h4>"
1347
+ "<div class='modelo-info-card'>"
1348
+ "<div class='modelo-info-split'>"
1349
+ "<div class='modelo-info-col'>"
1350
+ "<div class='modelo-info-stack-block'>"
1351
+ "<div class='elaborador-badge-title'>NOME DO MODELO:</div>"
1352
+ f"<div class='elaborador-badge-name'>{_esc(model_name)}</div>"
1353
+ "</div>"
1354
+ "<div class='modelo-info-stack-block'>"
1355
+ "<div class='elaborador-badge-title'>ELABORADO POR:</div>"
1356
+ + elaborador_html +
1357
+ "</div>"
1358
+ "</div>"
1359
+ "<div class='modelo-info-col modelo-info-col-vars'>"
1360
+ "<div class='elaborador-badge-title'>Variáveis selecionadas:</div>"
1361
+ + y_html +
1362
+ x_html +
1363
+ periodo_html +
1364
+ "</div>"
1365
+ "</div>"
1366
+ "</div>"
1367
+ "</div>"
1368
  )
1369
 
1370
 
backend/app/services/elaboracao_service.py CHANGED
@@ -545,6 +545,7 @@ def load_dai_for_elaboracao(session: SessionState, caminho_arquivo: str) -> dict
545
  return {
546
  "status": msg,
547
  "tipo": "dai",
 
548
  **base,
549
  "selection": selection_payload,
550
  "fit": fit_payload,
 
545
  return {
546
  "status": msg,
547
  "tipo": "dai",
548
+ "nome_modelo": Path(caminho_arquivo).stem,
549
  **base,
550
  "selection": selection_payload,
551
  "fit": fit_payload,
backend/app/services/visualizacao_service.py CHANGED
@@ -1,6 +1,6 @@
1
  from __future__ import annotations
2
 
3
- import os
4
  from typing import Any
5
 
6
  import numpy as np
@@ -51,12 +51,13 @@ def carregar_modelo(session: SessionState, caminho_arquivo: str) -> dict[str, An
51
  session.dados_visualizacao = None
52
  session.avaliacoes_visualizacao = []
53
 
54
- status = f"Modelo carregado: {os.path.basename(caminho_arquivo)}"
55
- badge_html = viz_app._formatar_badge_completo(pacote)
56
 
57
  return {
58
- "status": status,
59
  "badge_html": badge_html,
 
60
  }
61
 
62
 
 
1
  from __future__ import annotations
2
 
3
+ from pathlib import Path
4
  from typing import Any
5
 
6
  import numpy as np
 
51
  session.dados_visualizacao = None
52
  session.avaliacoes_visualizacao = []
53
 
54
+ nome_modelo = Path(caminho_arquivo).stem
55
+ badge_html = viz_app._formatar_badge_completo(pacote, nome_modelo=nome_modelo)
56
 
57
  return {
58
+ "status": "",
59
  "badge_html": badge_html,
60
+ "nome_modelo": nome_modelo,
61
  }
62
 
63
 
frontend/src/components/ElaboracaoTab.jsx CHANGED
@@ -480,6 +480,7 @@ function buildLoadedModelInfo(resp) {
480
 
481
  const contexto = resp?.contexto || {}
482
  const fit = resp?.fit || {}
 
483
  const colunaY = String(contexto.coluna_y || '').trim()
484
  const colunasX = Array.isArray(contexto.colunas_x) ? contexto.colunas_x.map((item) => String(item)) : []
485
  const transformacoesX = fit && typeof fit.transformacoes_x === 'object' && fit.transformacoes_x !== null
@@ -487,6 +488,7 @@ function buildLoadedModelInfo(resp) {
487
  : {}
488
 
489
  return {
 
490
  coluna_y: colunaY,
491
  colunas_x: colunasX,
492
  transformacao_y: fit.transformacao_y || contexto.transformacao_y || '(x)',
@@ -2146,55 +2148,62 @@ export default function ElaboracaoTab({ sessionId }) {
2146
  <div className="subpanel section1-group">
2147
  <h4>Informações do modelo</h4>
2148
  {modeloCarregadoInfo ? (
2149
- <div className="modelo-info-card">
2150
- <div className="modelo-info-split">
2151
- <div className="modelo-info-col">
2152
- <div className="elaborador-badge-title">Modelo elaborado por:</div>
2153
- {elaborador?.nome_completo ? (
2154
- <div className="elaborador-badge-name">{elaborador.nome_completo}</div>
2155
- ) : (
2156
- <div className="section1-empty-hint">Elaborador não informado no arquivo.</div>
2157
- )}
2158
- {elaboradorMeta.length > 0 && elaborador?.nome_completo ? (
2159
- <div className="elaborador-badge-meta">{elaboradorMeta.join(' | ')}</div>
2160
- ) : null}
2161
- </div>
2162
 
2163
- <div className="modelo-info-col modelo-info-col-vars">
2164
- <div className="elaborador-badge-title">Variáveis do modelo carregado</div>
2165
- {modeloCarregadoInfo.coluna_y ? (
2166
- <div className="variavel-badge-line">
2167
- <span className="variavel-badge-label">Dependente:</span>
2168
- <span className="variavel-chip variavel-chip-y variavel-chip-inline">
2169
- {modeloCarregadoInfo.coluna_y}
2170
- <span className="variavel-chip-transform">{` ${transformacaoYModeloBadge}`}</span>
2171
- </span>
 
2172
  </div>
2173
- ) : (
2174
- <div className="section1-empty-hint">Variável dependente não encontrada no modelo carregado.</div>
2175
- )}
2176
- {variaveisIndependentesModeloBadge.length > 0 ? (
2177
- <div className="variavel-badge-line">
2178
- <span className="variavel-badge-label">Independentes:</span>
2179
- <div className="variavel-chip-wrap">
2180
- {variaveisIndependentesModeloBadge.map((item) => (
2181
- <span key={`ind-${item.coluna}`} className="variavel-chip">
2182
- {item.coluna}
2183
- <span className="variavel-chip-transform">{` ${item.transformacao}`}</span>
2184
- </span>
2185
- ))}
 
 
 
 
 
 
 
 
 
 
 
 
 
2186
  </div>
 
 
 
 
 
 
2187
  </div>
2188
- ) : (
2189
- <div className="section1-empty-hint">Sem variáveis independentes no modelo carregado.</div>
2190
- )}
2191
- <div className="variavel-badge-line">
2192
- <span className="variavel-badge-label">Período dados:</span>
2193
- <span className="variavel-badge-value">{periodoModeloCarregadoTexto}</span>
2194
  </div>
2195
  </div>
2196
  </div>
2197
- </div>
2198
  ) : (
2199
  <div className="section1-empty-hint">Carregue um modelo .dai para visualizar os badges do elaborador.</div>
2200
  )}
 
480
 
481
  const contexto = resp?.contexto || {}
482
  const fit = resp?.fit || {}
483
+ const nomeModelo = String(resp?.nome_modelo || '').trim()
484
  const colunaY = String(contexto.coluna_y || '').trim()
485
  const colunasX = Array.isArray(contexto.colunas_x) ? contexto.colunas_x.map((item) => String(item)) : []
486
  const transformacoesX = fit && typeof fit.transformacoes_x === 'object' && fit.transformacoes_x !== null
 
488
  : {}
489
 
490
  return {
491
+ nome_modelo: nomeModelo,
492
  coluna_y: colunaY,
493
  colunas_x: colunasX,
494
  transformacao_y: fit.transformacao_y || contexto.transformacao_y || '(x)',
 
2148
  <div className="subpanel section1-group">
2149
  <h4>Informações do modelo</h4>
2150
  {modeloCarregadoInfo ? (
2151
+ <div className="modelo-info-card">
2152
+ <div className="modelo-info-split">
2153
+ <div className="modelo-info-col">
2154
+ <div className="modelo-info-stack-block">
2155
+ <div className="elaborador-badge-title">NOME DO MODELO:</div>
2156
+ <div className="elaborador-badge-name">{modeloCarregadoInfo.nome_modelo || '-'}</div>
2157
+ </div>
 
 
 
 
 
 
2158
 
2159
+ <div className="modelo-info-stack-block">
2160
+ <div className="elaborador-badge-title">ELABORADO POR:</div>
2161
+ {elaborador?.nome_completo ? (
2162
+ <div className="elaborador-badge-name">{elaborador.nome_completo}</div>
2163
+ ) : (
2164
+ <div className="section1-empty-hint">Elaborador não informado no arquivo.</div>
2165
+ )}
2166
+ {elaboradorMeta.length > 0 && elaborador?.nome_completo ? (
2167
+ <div className="elaborador-badge-meta">{elaboradorMeta.join(' | ')}</div>
2168
+ ) : null}
2169
  </div>
2170
+ </div>
2171
+
2172
+ <div className="modelo-info-col modelo-info-col-vars">
2173
+ <div className="elaborador-badge-title">Variáveis selecionadas:</div>
2174
+ {modeloCarregadoInfo.coluna_y ? (
2175
+ <div className="variavel-badge-line">
2176
+ <span className="variavel-badge-label">Dependente:</span>
2177
+ <span className="variavel-chip variavel-chip-y variavel-chip-inline">
2178
+ {modeloCarregadoInfo.coluna_y}
2179
+ <span className="variavel-chip-transform">{` ${transformacaoYModeloBadge}`}</span>
2180
+ </span>
2181
+ </div>
2182
+ ) : (
2183
+ <div className="section1-empty-hint">Variável dependente não encontrada no modelo carregado.</div>
2184
+ )}
2185
+ {variaveisIndependentesModeloBadge.length > 0 ? (
2186
+ <div className="variavel-badge-line">
2187
+ <span className="variavel-badge-label">Independentes:</span>
2188
+ <div className="variavel-chip-wrap">
2189
+ {variaveisIndependentesModeloBadge.map((item) => (
2190
+ <span key={`ind-${item.coluna}`} className="variavel-chip">
2191
+ {item.coluna}
2192
+ <span className="variavel-chip-transform">{` ${item.transformacao}`}</span>
2193
+ </span>
2194
+ ))}
2195
+ </div>
2196
  </div>
2197
+ ) : (
2198
+ <div className="section1-empty-hint">Sem variáveis independentes no modelo carregado.</div>
2199
+ )}
2200
+ <div className="variavel-badge-line">
2201
+ <span className="variavel-badge-label">Período dados:</span>
2202
+ <span className="variavel-badge-value">{periodoModeloCarregadoTexto}</span>
2203
  </div>
 
 
 
 
 
 
2204
  </div>
2205
  </div>
2206
  </div>
 
2207
  ) : (
2208
  <div className="section1-empty-hint">Carregue um modelo .dai para visualizar os badges do elaborador.</div>
2209
  )}
frontend/src/components/PesquisaTab.jsx CHANGED
@@ -364,7 +364,7 @@ export default function PesquisaTab() {
364
  <PesquisaAdminConfigPanel onSaved={() => void onAdminConfigSalva()} />
365
  ) : null}
366
 
367
- <div className="pesquisa-fields-grid">
368
  <label className="pesquisa-field">
369
  Contem variavel APP (% APP)
370
  <select
@@ -394,9 +394,6 @@ export default function PesquisaTab() {
394
  ))}
395
  </select>
396
  </label>
397
- </div>
398
-
399
- <div className="pesquisa-fields-grid pesquisa-avaliando-grid">
400
  <label className="pesquisa-field">
401
  Finalidade do imovel
402
  <TextFieldInput
@@ -418,19 +415,22 @@ export default function PesquisaTab() {
418
  placeholder="Ex: Centro"
419
  />
420
  </label>
 
421
 
422
- <div className="pesquisa-avaliando-inline pesquisa-avaliando-inline-periodo">
423
- <div className="pesquisa-field-pair pesquisa-field-pair-inline">
424
- <span className="pesquisa-field-pair-title">Periodo de data do imovel</span>
425
- <label className="pesquisa-field">
426
- Data inicial
427
- <DateFieldInput field="dataMin" value={filters.dataMin} onChange={onFieldChange} />
428
- </label>
429
- <label className="pesquisa-field">
430
- Data final
431
- <DateFieldInput field="dataMax" value={filters.dataMax} onChange={onFieldChange} />
432
- </label>
433
- </div>
 
 
434
  <label className="pesquisa-field">
435
  Area do imovel
436
  <NumberFieldInput field="avalArea" value={filters.avalArea} onChange={onFieldChange} placeholder="0" />
 
364
  <PesquisaAdminConfigPanel onSaved={() => void onAdminConfigSalva()} />
365
  ) : null}
366
 
367
+ <div className="pesquisa-fields-grid pesquisa-top-four-grid">
368
  <label className="pesquisa-field">
369
  Contem variavel APP (% APP)
370
  <select
 
394
  ))}
395
  </select>
396
  </label>
 
 
 
397
  <label className="pesquisa-field">
398
  Finalidade do imovel
399
  <TextFieldInput
 
415
  placeholder="Ex: Centro"
416
  />
417
  </label>
418
+ </div>
419
 
420
+ <div className="pesquisa-avaliando-bottom-grid">
421
+ <div className="pesquisa-field-pair pesquisa-field-pair-inline pesquisa-avaliando-periodo-pair">
422
+ <span className="pesquisa-field-pair-title">Período de referência do imóvel (deve estar contido no período de dados de mercado do modelo)</span>
423
+ <label className="pesquisa-field">
424
+ Data inicial
425
+ <DateFieldInput field="dataMin" value={filters.dataMin} onChange={onFieldChange} />
426
+ </label>
427
+ <label className="pesquisa-field">
428
+ Data final
429
+ <DateFieldInput field="dataMax" value={filters.dataMax} onChange={onFieldChange} />
430
+ </label>
431
+ </div>
432
+
433
+ <div className="pesquisa-avaliando-stack pesquisa-avaliando-bottom-stack">
434
  <label className="pesquisa-field">
435
  Area do imovel
436
  <NumberFieldInput field="avalArea" value={filters.avalArea} onChange={onFieldChange} placeholder="0" />
frontend/src/components/VisualizacaoTab.jsx CHANGED
@@ -431,7 +431,7 @@ export default function VisualizacaoTab({ sessionId }) {
431
  </div>
432
  </div>
433
  {status ? <div className="status-line">{status}</div> : null}
434
- {badgeHtml ? <div dangerouslySetInnerHTML={{ __html: badgeHtml }} /> : null}
435
  </SectionBlock>
436
 
437
  {dados ? (
@@ -452,7 +452,7 @@ export default function VisualizacaoTab({ sessionId }) {
452
  <div className="inner-tab-panel">
453
  {activeInnerTab === 'mapa' ? (
454
  <>
455
- <div className="row compact">
456
  <label>Variável no mapa</label>
457
  <select value={mapaVar} onChange={(e) => onMapChange(e.target.value)}>
458
  {mapaChoices.map((choice) => (
@@ -518,6 +518,12 @@ export default function VisualizacaoTab({ sessionId }) {
518
 
519
  {activeInnerTab === 'avaliacao' ? (
520
  <>
 
 
 
 
 
 
521
  <div className="avaliacao-grid" key={`avaliacao-grid-viz-${avaliacaoFormVersion}`}>
522
  {camposAvaliacao.map((campo) => (
523
  <div key={`campo-${campo.coluna}`} className="avaliacao-card">
 
431
  </div>
432
  </div>
433
  {status ? <div className="status-line">{status}</div> : null}
434
+ {badgeHtml ? <div className="upload-badge-block" dangerouslySetInnerHTML={{ __html: badgeHtml }} /> : null}
435
  </SectionBlock>
436
 
437
  {dados ? (
 
452
  <div className="inner-tab-panel">
453
  {activeInnerTab === 'mapa' ? (
454
  <>
455
+ <div className="row compact visualizacao-mapa-controls">
456
  <label>Variável no mapa</label>
457
  <select value={mapaVar} onChange={(e) => onMapChange(e.target.value)}>
458
  {mapaChoices.map((choice) => (
 
518
 
519
  {activeInnerTab === 'avaliacao' ? (
520
  <>
521
+ <div className="equation-formats-section avaliacao-equacao-section">
522
+ <h5>Equação (estilo SAB)</h5>
523
+ <div className="equation-box equation-box-plain">
524
+ {equacoes?.excel_sab || 'Equação indisponível.'}
525
+ </div>
526
+ </div>
527
  <div className="avaliacao-grid" key={`avaliacao-grid-viz-${avaliacaoFormVersion}`}>
528
  {camposAvaliacao.map((campo) => (
529
  <div key={`campo-${campo.coluna}`} className="avaliacao-card">
frontend/src/styles.css CHANGED
@@ -727,6 +727,23 @@ textarea {
727
  margin-top: 6px;
728
  }
729
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
730
  .placeholder-section .empty-box {
731
  border-style: solid;
732
  }
@@ -909,23 +926,35 @@ button.pesquisa-otica-btn.active:hover {
909
  max-width: none;
910
  }
911
 
912
- .pesquisa-avaliando-grid {
 
913
  align-items: end;
914
  }
915
 
916
- .pesquisa-avaliando-inline {
917
- grid-column: 1 / -1;
918
  display: grid;
919
- grid-template-columns: repeat(3, minmax(0, 1fr));
920
  gap: 12px 14px;
 
 
921
  }
922
 
923
- .pesquisa-avaliando-inline-periodo {
924
- grid-template-columns: minmax(280px, 1.45fr) repeat(2, minmax(0, 1fr));
925
- align-items: end;
 
 
926
  }
927
 
928
- .pesquisa-avaliando-inline-periodo .pesquisa-field-pair {
 
 
 
 
 
 
 
 
929
  grid-column: auto;
930
  margin: 0;
931
  }
@@ -1657,6 +1686,10 @@ button.btn-upload-select {
1657
  word-break: break-word;
1658
  }
1659
 
 
 
 
 
1660
  .coords-section-groups {
1661
  display: grid;
1662
  gap: 14px;
@@ -1756,6 +1789,10 @@ button.btn-upload-select {
1756
  padding: 12px 14px;
1757
  }
1758
 
 
 
 
 
1759
  .modelo-info-col + .modelo-info-col {
1760
  border-left: 1px solid #dde7f1;
1761
  }
@@ -3356,7 +3393,7 @@ button.btn-download-subtle {
3356
  grid-template-columns: 1fr;
3357
  }
3358
 
3359
- .pesquisa-avaliando-inline {
3360
  grid-template-columns: 1fr;
3361
  }
3362
 
 
727
  margin-top: 6px;
728
  }
729
 
730
+ .visualizacao-mapa-controls {
731
+ margin-bottom: 28px;
732
+ }
733
+
734
+ .visualizacao-mapa-controls + .map-frame,
735
+ .visualizacao-mapa-controls + .empty-box {
736
+ margin-top: 10px;
737
+ }
738
+
739
+ .avaliacao-equacao-section {
740
+ margin-bottom: 12px;
741
+ }
742
+
743
+ .avaliacao-equacao-section h5 {
744
+ margin: 0 0 8px;
745
+ }
746
+
747
  .placeholder-section .empty-box {
748
  border-style: solid;
749
  }
 
926
  max-width: none;
927
  }
928
 
929
+ .pesquisa-top-four-grid {
930
+ grid-template-columns: repeat(4, minmax(0, 1fr));
931
  align-items: end;
932
  }
933
 
934
+ .pesquisa-avaliando-bottom-grid {
 
935
  display: grid;
936
+ grid-template-columns: repeat(2, minmax(0, 1fr));
937
  gap: 12px 14px;
938
+ margin-bottom: 14px;
939
+ align-items: start;
940
  }
941
 
942
+ .pesquisa-avaliando-stack {
943
+ display: grid;
944
+ grid-template-columns: 1fr;
945
+ gap: 12px;
946
+ align-items: start;
947
  }
948
 
949
+ .pesquisa-avaliando-periodo-pair {
950
+ margin: 0;
951
+ }
952
+
953
+ .pesquisa-avaliando-bottom-stack {
954
+ gap: 12px;
955
+ }
956
+
957
+ .pesquisa-avaliando-bottom-grid .pesquisa-field-pair {
958
  grid-column: auto;
959
  margin: 0;
960
  }
 
1686
  word-break: break-word;
1687
  }
1688
 
1689
+ .upload-badge-block {
1690
+ margin-top: 14px;
1691
+ }
1692
+
1693
  .coords-section-groups {
1694
  display: grid;
1695
  gap: 14px;
 
1789
  padding: 12px 14px;
1790
  }
1791
 
1792
+ .modelo-info-stack-block + .modelo-info-stack-block {
1793
+ margin-top: 10px;
1794
+ }
1795
+
1796
  .modelo-info-col + .modelo-info-col {
1797
  border-left: 1px solid #dde7f1;
1798
  }
 
3393
  grid-template-columns: 1fr;
3394
  }
3395
 
3396
+ .pesquisa-avaliando-bottom-grid {
3397
  grid-template-columns: 1fr;
3398
  }
3399