import React, { useEffect, useState } from 'react' import { api, downloadBlob } from '../api' import DataTable from './DataTable' import EquationFormatsPanel from './EquationFormatsPanel' import LoadingOverlay from './LoadingOverlay' import MapFrame from './MapFrame' import PlotFigure from './PlotFigure' const REPO_INNER_TABS = [ { key: 'mapa', label: 'Mapa' }, { key: 'dados_mercado', label: 'Dados de Mercado' }, { key: 'metricas', label: 'Métricas' }, { key: 'transformacoes', label: 'Transformações' }, { key: 'resumo', label: 'Resumo' }, { key: 'coeficientes', label: 'Coeficientes' }, { key: 'obs_calc', label: 'Obs x Calc' }, { key: 'graficos', label: 'Gráficos' }, ] function formatarFonte(fonte) { if (!fonte || typeof fonte !== 'object') return 'Fonte não informada' const provider = String(fonte.provider || '').toLowerCase() if (provider === 'hf_dataset') { const repo = String(fonte.repo_id || '').trim() const revision = String(fonte.revision || '').trim() const suffix = fonte.degraded ? ' (modo contingência)' : '' return `HF Dataset${repo ? ` (${repo})` : ''}${revision ? ` | revisão ${revision.slice(0, 8)}` : ''}${suffix}` } return 'Pasta local' } export default function RepositorioTab({ authUser, sessionId }) { const [modelos, setModelos] = useState([]) const [fonte, setFonte] = useState(null) const [loading, setLoading] = useState(false) const [uploading, setUploading] = useState(false) const [deleting, setDeleting] = useState(false) const [error, setError] = useState('') const [status, setStatus] = useState('') const [arquivoUpload, setArquivoUpload] = useState(null) const [confirmDeleteId, setConfirmDeleteId] = useState('') const [confirmarSubstituicao, setConfirmarSubstituicao] = useState({ open: false, substituidos: [] }) const [confirmarExclusaoModal, setConfirmarExclusaoModal] = useState({ open: false, modeloId: '', nomeModelo: '', digitado: '', }) const [modeloAbertoMeta, setModeloAbertoMeta] = useState(null) const [modeloAbertoLoading, setModeloAbertoLoading] = useState(false) const [modeloAbertoError, setModeloAbertoError] = useState('') const [modeloAbertoActiveTab, setModeloAbertoActiveTab] = useState('mapa') const [modeloAbertoDados, setModeloAbertoDados] = useState(null) const [modeloAbertoEstatisticas, setModeloAbertoEstatisticas] = useState(null) const [modeloAbertoEscalasHtml, setModeloAbertoEscalasHtml] = useState('') const [modeloAbertoDadosTransformados, setModeloAbertoDadosTransformados] = useState(null) const [modeloAbertoResumoHtml, setModeloAbertoResumoHtml] = useState('') const [modeloAbertoEquacoes, setModeloAbertoEquacoes] = useState(null) const [modeloAbertoCoeficientes, setModeloAbertoCoeficientes] = useState(null) const [modeloAbertoObsCalc, setModeloAbertoObsCalc] = useState(null) const [modeloAbertoMapaHtml, setModeloAbertoMapaHtml] = useState('') const [modeloAbertoMapaChoices, setModeloAbertoMapaChoices] = useState(['Visualização Padrão']) const [modeloAbertoMapaVar, setModeloAbertoMapaVar] = useState('Visualização Padrão') const [modeloAbertoPlotObsCalc, setModeloAbertoPlotObsCalc] = useState(null) const [modeloAbertoPlotResiduos, setModeloAbertoPlotResiduos] = useState(null) const [modeloAbertoPlotHistograma, setModeloAbertoPlotHistograma] = useState(null) const [modeloAbertoPlotCook, setModeloAbertoPlotCook] = useState(null) const [modeloAbertoPlotCorr, setModeloAbertoPlotCorr] = useState(null) const isAdmin = String(authUser?.perfil || '').toLowerCase() === 'admin' const totalModelos = modelos.length const modoModeloAberto = Boolean(modeloAbertoMeta) const nomesSubstituidos = Array.isArray(confirmarSubstituicao.substituidos) ? confirmarSubstituicao.substituidos.filter(Boolean) : [] const exclusaoDigitadaCorreta = confirmarExclusaoModal.digitado === confirmarExclusaoModal.nomeModelo useEffect(() => { void carregarModelos() }, []) async function carregarModelos() { setLoading(true) setError('') try { const resp = await api.repositorioListar() setModelos(Array.isArray(resp?.modelos) ? resp.modelos : []) setFonte(resp?.fonte || null) setStatus('') } catch (err) { setError(err.message || 'Falha ao carregar repositório.') setModelos([]) setFonte(null) } finally { setLoading(false) } } function parseSubstituidosErro(err) { if (!err || typeof err !== 'object') return [] const detail = err.detail if (!detail || typeof detail !== 'object') return [] const lista = detail.substituidos if (!Array.isArray(lista)) return [] return lista.map((item) => String(item || '').trim()).filter(Boolean) } async function onUploadArquivo(confirmar = false) { if (!isAdmin || !arquivoUpload) return setUploading(true) setError('') setStatus('') try { const resp = await api.repositorioUpload([arquivoUpload], { confirmarSubstituicao: confirmar }) setModelos(Array.isArray(resp?.modelos) ? resp.modelos : []) setFonte(resp?.fonte || null) setStatus(resp?.status || 'Modelo incluído no repositório.') setArquivoUpload(null) setConfirmarSubstituicao({ open: false, substituidos: [] }) } catch (err) { const substituidos = parseSubstituidosErro(err) const erroDuplicado = Number(err?.status) === 409 && substituidos.length > 0 if (!confirmar && erroDuplicado) { setConfirmarSubstituicao({ open: true, substituidos }) setError('') return } setError(err.message || 'Falha ao incluir modelo no repositório.') } finally { setUploading(false) } } async function onExcluirModelo(modeloId) { if (!isAdmin || !modeloId) return setDeleting(true) setError('') setStatus('') try { const resp = await api.repositorioDelete([String(modeloId)]) setModelos(Array.isArray(resp?.modelos) ? resp.modelos : []) setFonte(resp?.fonte || null) setStatus(resp?.status || 'Modelo removido do repositório.') setConfirmDeleteId('') setConfirmarExclusaoModal({ open: false, modeloId: '', nomeModelo: '', digitado: '' }) } catch (err) { setError(err.message || 'Falha na exclusão do modelo.') } finally { setDeleting(false) } } function onAbrirModalExclusao(item) { const id = String(item?.id || '').trim() if (!id) return const nomeModelo = String(item?.nome_modelo || item?.arquivo || id).trim() setConfirmDeleteId('') setConfirmarExclusaoModal({ open: true, modeloId: id, nomeModelo, digitado: '', }) } function onCancelarModalExclusao() { if (deleting) return setConfirmarExclusaoModal({ open: false, modeloId: '', nomeModelo: '', digitado: '' }) } async function onConfirmarExclusaoModal() { const modeloId = String(confirmarExclusaoModal.modeloId || '').trim() if (!modeloId) return if (confirmarExclusaoModal.digitado !== confirmarExclusaoModal.nomeModelo) return await onExcluirModelo(modeloId) } function preencherModeloAberto(resp) { setModeloAbertoDados(resp?.dados || null) setModeloAbertoEstatisticas(resp?.estatisticas || null) setModeloAbertoEscalasHtml(resp?.escalas_html || '') setModeloAbertoDadosTransformados(resp?.dados_transformados || null) setModeloAbertoResumoHtml(resp?.resumo_html || '') setModeloAbertoEquacoes(resp?.equacoes || null) setModeloAbertoCoeficientes(resp?.coeficientes || null) setModeloAbertoObsCalc(resp?.obs_calc || null) setModeloAbertoMapaHtml(resp?.mapa_html || '') setModeloAbertoMapaChoices(resp?.mapa_choices || ['Visualização Padrão']) setModeloAbertoMapaVar('Visualização Padrão') setModeloAbertoPlotObsCalc(resp?.grafico_obs_calc || null) setModeloAbertoPlotResiduos(resp?.grafico_residuos || null) setModeloAbertoPlotHistograma(resp?.grafico_histograma || null) setModeloAbertoPlotCook(resp?.grafico_cook || null) setModeloAbertoPlotCorr(resp?.grafico_correlacao || null) } async function onAbrirModelo(item) { if (!sessionId) { setError('Sessão indisponível no momento. Aguarde e tente novamente.') return } setModeloAbertoLoading(true) setModeloAbertoError('') try { await api.visualizacaoRepositorioCarregar(sessionId, String(item?.id || '')) const resp = await api.exibirVisualizacao(sessionId) preencherModeloAberto(resp) setModeloAbertoActiveTab('mapa') setModeloAbertoMeta({ id: String(item?.id || ''), nome: item?.nome_modelo || item?.arquivo || String(item?.id || ''), }) } catch (err) { setModeloAbertoError(err.message || 'Falha ao abrir modelo.') } finally { setModeloAbertoLoading(false) } } function onVoltarRepositorio() { setModeloAbertoMeta(null) setModeloAbertoError('') setModeloAbertoActiveTab('mapa') } async function onModeloAbertoMapChange(nextVar) { setModeloAbertoMapaVar(nextVar) if (!sessionId) return try { const resp = await api.updateVisualizacaoMap(sessionId, nextVar) setModeloAbertoMapaHtml(resp?.mapa_html || '') } catch (err) { setModeloAbertoError(err.message || 'Falha ao atualizar mapa do modelo.') } } async function onDownloadEquacaoModeloAberto(mode) { if (!sessionId || !mode) return setModeloAbertoLoading(true) try { const blob = await api.exportEquationViz(sessionId, mode) const sufixo = String(mode) === 'excel_sab' ? 'estilo_sab' : 'excel' downloadBlob(blob, `equacao_modelo_${sufixo}.xlsx`) } catch (err) { setModeloAbertoError(err.message || 'Falha ao exportar equação.') } finally { setModeloAbertoLoading(false) } } if (modoModeloAberto) { return (

{modeloAbertoMeta?.nome || 'Modelo'}

Visualização do modelo do repositório

{REPO_INNER_TABS.map((tab) => ( ))}
{modeloAbertoActiveTab === 'mapa' ? ( <>
) : null} {modeloAbertoActiveTab === 'dados_mercado' ? : null} {modeloAbertoActiveTab === 'metricas' ? : null} {modeloAbertoActiveTab === 'transformacoes' ? ( <>

Dados com variáveis transformadas

) : null} {modeloAbertoActiveTab === 'resumo' ? ( <>

Equações do Modelo

void onDownloadEquacaoModeloAberto(mode)} disabled={modeloAbertoLoading} />
) : null} {modeloAbertoActiveTab === 'coeficientes' ? : null} {modeloAbertoActiveTab === 'obs_calc' ? : null} {modeloAbertoActiveTab === 'graficos' ? ( <>
) : null}
{modeloAbertoError ?
{modeloAbertoError}
: null}
) } return (
Total: {totalModelos}
Fonte: {formatarFonte(fonte)}
Perfil: {isAdmin ? 'Administrador' : 'Leitura'}
{isAdmin ? (
setArquivoUpload(event.target.files?.[0] || null)} disabled={uploading || deleting} />
{arquivoUpload ?
Arquivo selecionado: {arquivoUpload.name}
: null}
) : (
Perfil de leitura: inclusão e exclusão disponíveis apenas para administradores.
)} {status ?
{status}
: null} {error ?
{error}
: null}
{isAdmin ? : null} {modelos.map((item) => { const key = String(item.id) const emConfirmacao = confirmDeleteId === key return ( {isAdmin ? ( ) : null} ) })} {!modelos.length ? ( ) : null}
Modelo Tipo Finalidade Autor Período Dados APP Status AbrirExcluir
{item.nome_modelo || item.arquivo || key} {item.tipo_imovel || '-'} {item.finalidade || '-'} {item.autor || '-'} {item.periodo_dados?.label || '-'} {item.total_dados ?? '-'} {item.tem_app ? 'Sim' : 'Não'} {item.status || '-'} {!emConfirmacao ? ( ) : (
)}
{loading ? 'Carregando modelos...' : 'Nenhum modelo encontrado no repositório.'}
{confirmarSubstituicao.open ? (

Confirmar substituição de modelo

Já existe modelo com o mesmo nome no repositório.

Se você continuar, o modelo existente será substituído.
    {(nomesSubstituidos.length ? nomesSubstituidos : [String(arquivoUpload?.name || '')]).map((nome) => (
  • {nome}
  • ))}
) : null} {confirmarExclusaoModal.open ? (

{confirmarExclusaoModal.nomeModelo || 'Confirmar exclusão'}

Digite o nome completo do modelo para confirmar a exclusão.

{ event.preventDefault() void onConfirmarExclusaoModal() }} > {confirmarExclusaoModal.digitado && !exclusaoDigitadaCorreta ? (
O texto digitado não corresponde ao nome do modelo.
) : (
A exclusão será concluída somente quando o nome for digitado exatamente.
)}
) : null}
) }