document.documentElement.dataset.panelReady = "true"; const loginForm = document.querySelector('[data-admin-login-form="true"]'); const reviewBoard = document.querySelector('[data-admin-tool-review-board="true"]'); const toolIntakePage = document.querySelector('[data-admin-tool-intake="true"]'); const collaboratorBoard = document.querySelector('[data-admin-collaborator-board="true"]'); const systemConfigurationPage = document.querySelector('[data-admin-system-configuration="true"]'); const salesRevenueReportsPage = document.querySelector('[data-admin-sales-revenue-reports="true"]'); const rentalReportsPage = document.querySelector('[data-admin-rental-reports="true"]'); const botMonitoringPage = document.querySelector('[data-admin-bot-monitoring="true"]'); if (loginForm) { mountLoginForm(loginForm); } if (reviewBoard) { mountToolReviewBoard(reviewBoard); } if (toolIntakePage) { mountToolIntakePage(toolIntakePage); } if (collaboratorBoard) { mountCollaboratorBoard(collaboratorBoard); } if (systemConfigurationPage) { mountSystemConfigurationPage(systemConfigurationPage); } if (salesRevenueReportsPage) { mountSalesRevenueReportsPage(salesRevenueReportsPage); } if (rentalReportsPage) { mountRentalReportsPage(rentalReportsPage); } if (botMonitoringPage) { mountBotMonitoringPage(botMonitoringPage); } function mountLoginForm(form) { const feedback = document.getElementById("admin-login-feedback"); const submitButton = form.querySelector('button[type="submit"]'); const submitLabel = form.querySelector("[data-submit-label]"); const submitSpinner = form.querySelector("[data-submit-spinner]"); form.addEventListener("submit", async (event) => { event.preventDefault(); toggleLoading(true); clearFeedback(); const formData = new FormData(form); const payload = { email: String(formData.get("email") || "").trim(), password: String(formData.get("password") || ""), }; try { const response = await fetch(form.dataset.authEndpoint, { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify(payload), }); const authBody = await readJson(response); if (!response.ok) { throw new Error(authBody?.detail || "Nao foi possivel autenticar no admin."); } showFeedback("success", authBody?.message || "Sessao administrativa web iniciada com sucesso."); form.reset(); const redirectTo = authBody?.redirect_to || form.dataset.dashboardHref; if (redirectTo) { window.setTimeout(() => { window.location.assign(redirectTo); }, 250); } } catch (error) { showFeedback("danger", error instanceof Error ? error.message : "Erro inesperado durante o login."); } finally { toggleLoading(false); } }); function toggleLoading(isLoading) { submitButton.disabled = isLoading; submitSpinner.classList.toggle("d-none", !isLoading); submitLabel.textContent = isLoading ? "Validando acesso..." : "Entrar no painel"; } function clearFeedback() { feedback.className = "alert d-none mt-4 mb-0 rounded-4"; feedback.textContent = ""; } function showFeedback(variant, message) { feedback.className = `alert alert-${variant} mt-4 mb-0 rounded-4`; feedback.textContent = message; } } function mountToolReviewBoard(board) { const refreshButton = board.querySelector("[data-admin-tool-refresh]"); const refreshLabel = board.querySelector("[data-tool-refresh-label]"); const refreshSpinner = board.querySelector("[data-tool-refresh-spinner]"); const feedback = document.getElementById("admin-tool-review-feedback"); const queueList = board.querySelector("[data-tool-review-queue-list]"); const publicationList = board.querySelector("[data-tool-publication-list]"); const lifecycleList = board.querySelector("[data-tool-contract-lifecycle]"); const parameterTypes = board.querySelector("[data-tool-parameter-types]"); if (refreshButton) { refreshButton.addEventListener("click", () => { void loadBoard(); }); } void loadBoard(); async function loadBoard() { toggleRefreshing(true); clearFeedback(); const overviewResult = await fetchPanelJson(board.dataset.overviewEndpoint); const contractsResult = await fetchPanelJson(board.dataset.contractsEndpoint); const reviewQueueResult = await fetchPanelJson(board.dataset.reviewQueueEndpoint); const publicationsResult = await fetchPanelJson(board.dataset.publicationsEndpoint); if (!overviewResult.ok && !contractsResult.ok && !reviewQueueResult.ok && !publicationsResult.ok) { showFeedback("warning", overviewResult.message || "Entre com uma sessao administrativa web para carregar esta tela."); } if (overviewResult.ok) { renderOverview(overviewResult.body); } if (contractsResult.ok) { renderContracts(contractsResult.body); } else { renderLockedLifecycle(contractsResult.message); } if (reviewQueueResult.ok) { renderReviewQueue(reviewQueueResult.body); } else { renderLockedQueue(reviewQueueResult.message); } if (publicationsResult.ok) { renderPublications(publicationsResult.body); } else { renderLockedPublications(publicationsResult.message); } setText("[data-tool-review-last-sync]", formatNow()); toggleRefreshing(false); } function toggleRefreshing(isLoading) { if (!refreshButton || !refreshLabel || !refreshSpinner) { return; } refreshButton.disabled = isLoading; refreshSpinner.classList.toggle("d-none", !isLoading); refreshLabel.textContent = isLoading ? "Atualizando..." : "Atualizar leitura"; } function clearFeedback() { feedback.className = "alert d-none rounded-4 mb-4"; feedback.textContent = ""; } function showFeedback(variant, message) { feedback.className = `alert alert-${variant} rounded-4 mb-4`; feedback.textContent = message; } function renderOverview(payload) { const workflow = Array.isArray(payload?.workflow) ? payload.workflow : []; const nextSteps = Array.isArray(payload?.next_steps) ? payload.next_steps : []; setText("[data-tool-review-lifecycle-count]", String(workflow.length || 0)); if (nextSteps.length > 0 && !feedback.textContent) { showFeedback("info", `Proximos passos: ${nextSteps[0]}`); } } function renderContracts(payload) { const lifecycle = Array.isArray(payload?.lifecycle_statuses) ? payload.lifecycle_statuses : []; const parameterTypeList = Array.isArray(payload?.parameter_types) ? payload.parameter_types : []; lifecycleList.innerHTML = lifecycle.length > 0 ? lifecycle.map((item) => `
${escapeHtml(item.label)}
${escapeHtml(item.description)}
`).join("") : `
Nenhuma etapa disponivel.
`; parameterTypes.innerHTML = parameterTypeList.length > 0 ? parameterTypeList.map((item) => `${escapeHtml(item.label)}`).join("") : `Sem tipos`; } function renderLockedLifecycle(message) { lifecycleList.innerHTML = `
Leitura indisponivel
${escapeHtml(message || "A sessao atual nao pode ler o contrato compartilhado.")}
`; parameterTypes.innerHTML = `Bloqueado`; } function renderReviewQueue(payload) { const items = Array.isArray(payload?.items) ? payload.items : []; setText("[data-tool-review-queue-count]", String(items.length)); setText("[data-tool-review-queue-mode]", payload?.queue_mode || "Fila web"); queueList.innerHTML = items.length > 0 ? items.map((item) => `
${escapeHtml(item.gate || "revisao")}

${escapeHtml(item.display_name || item.tool_name || "Tool")}

${escapeHtml(item.tool_name || "")}
${escapeHtml(item.status || "pendente")}

${escapeHtml(item.summary || payload?.message || "Item aguardando analise do time.")}

`).join("") : `

Fila sem itens no momento

${escapeHtml(payload?.message || "Nenhuma tool aguardando revisao agora.")}

`; } function renderLockedQueue(message) { setText("[data-tool-review-queue-count]", "0"); setText("[data-tool-review-queue-mode]", "Bloqueado"); queueList.innerHTML = `

Fila indisponivel

${escapeHtml(message || "A sessao atual nao pode acessar a fila de revisao.")}

`; } function renderPublications(payload) { const items = Array.isArray(payload?.publications) ? payload.publications : []; setText("[data-tool-review-publication-count]", String(items.length)); setText("[data-tool-publication-source]", payload?.source || "Catalogo web"); publicationList.innerHTML = items.length > 0 ? items.slice(0, 9).map((item) => `
${escapeHtml(item.domain || "tool")}

${escapeHtml(item.display_name || item.tool_name || "Tool")}

${escapeHtml(item.tool_name || "")}
v${escapeHtml(String(item.version || 1))}

${escapeHtml(item.description || "Publicacao ativa no catalogo do produto.")}

Status: ${escapeHtml(item.status || "draft")}
Parametros: ${escapeHtml(String(item.parameter_count || 0))}
Autor: ${escapeHtml(item.author_name || item.published_by || "Nao informado")}
${escapeHtml(item.implementation_module || "")}
`).join("") : `

Catalogo ativo vazio

Nenhuma publicacao ativa retornada pela sessao web.

`; } function renderLockedPublications(message) { setText("[data-tool-review-publication-count]", "0"); setText("[data-tool-publication-source]", "Bloqueado"); publicationList.innerHTML = `

Catalogo protegido

${escapeHtml(message || "A sessao atual nao possui permissao para ler as publicacoes ativas.")}

`; } } function mountToolIntakePage(page) { const form = page.querySelector('[data-admin-tool-intake-form="true"]'); const addButton = page.querySelector("[data-add-parameter-row]"); const parameterList = page.querySelector("[data-parameter-list]"); const template = page.querySelector("#admin-tool-parameter-row-template"); const feedback = document.getElementById("admin-tool-intake-feedback"); const preview = page.querySelector("[data-tool-intake-preview]"); const storageStatus = page.querySelector("[data-tool-intake-storage-status]"); const submitLabel = page.querySelector("[data-intake-submit-label]"); const submitSpinner = page.querySelector("[data-intake-submit-spinner]"); const submitButton = form?.querySelector('button[type="submit"]'); if (!form || !parameterList || !template || !feedback || !preview || !storageStatus || !submitLabel || !submitSpinner || !submitButton) { return; } if (addButton) { addButton.addEventListener("click", () => { appendParameterRow(); }); } parameterList.addEventListener("click", (event) => { const target = event.target; if (!(target instanceof HTMLElement) || !target.matches("[data-remove-parameter-row]")) { return; } const rows = parameterList.querySelectorAll("[data-parameter-row]"); const row = target.closest("[data-parameter-row]"); if (!row) { return; } if (rows.length === 1) { clearParameterRow(row); return; } row.remove(); }); form.addEventListener("submit", async (event) => { event.preventDefault(); toggleSubmitting(true); clearFeedback(); const payload = buildPayload(); try { const response = await fetch(page.dataset.intakeEndpoint, { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify(payload), }); const body = await readJson(response); if (!response.ok) { throw new Error(body?.detail || "Nao foi possivel validar o pre-cadastro da tool."); } renderDraftPreview(body); showFeedback("success", body?.message || "Pre-cadastro validado com sucesso."); } catch (error) { storageStatus.textContent = "Falha"; showFeedback("danger", error instanceof Error ? error.message : "Erro inesperado ao validar a nova tool."); } finally { toggleSubmitting(false); } }); function appendParameterRow() { const fragment = template.content.cloneNode(true); parameterList.appendChild(fragment); } function buildPayload() { const formData = new FormData(form); const parameters = Array.from(parameterList.querySelectorAll("[data-parameter-row]")) .map((row) => { const name = row.querySelector('[name="parameter_name"]')?.value?.trim() || ""; const parameterType = row.querySelector('[name="parameter_type"]')?.value || "string"; const description = row.querySelector('[name="parameter_description"]')?.value?.trim() || ""; const required = Boolean(row.querySelector('[name="parameter_required"]')?.checked); return { name, parameter_type: parameterType, description, required }; }) .filter((item) => item.name || item.description); return { domain: String(formData.get("domain") || "").trim(), tool_name: String(formData.get("tool_name") || "").trim(), display_name: String(formData.get("display_name") || "").trim(), description: String(formData.get("description") || "").trim(), business_goal: String(formData.get("business_goal") || "").trim(), parameters, }; } function clearParameterRow(row) { row.querySelectorAll("input").forEach((input) => { if (input.type === "checkbox") { input.checked = true; return; } input.value = ""; }); const select = row.querySelector('select[name="parameter_type"]'); if (select) { select.value = "string"; } } function toggleSubmitting(isLoading) { submitButton.disabled = isLoading; submitSpinner.classList.toggle("d-none", !isLoading); submitLabel.textContent = isLoading ? "Validando..." : "Validar pre-cadastro"; } function clearFeedback() { feedback.className = "alert d-none rounded-4 mb-4"; feedback.textContent = ""; } function showFeedback(variant, message) { feedback.className = `alert alert-${variant} rounded-4 mb-4`; feedback.textContent = message; } function renderDraftPreview(payload) { const draft = payload?.draft_preview; const warnings = Array.isArray(payload?.warnings) ? payload.warnings : []; const nextSteps = Array.isArray(payload?.next_steps) ? payload.next_steps : []; const parameters = Array.isArray(draft?.parameters) ? draft.parameters : []; storageStatus.textContent = payload?.storage_status || "Validado"; preview.innerHTML = `
${escapeHtml(draft?.domain || "tool")}

${escapeHtml(draft?.display_name || "Nova tool")}

${escapeHtml(draft?.tool_name || "")}
${escapeHtml(draft?.status || "draft")}

${escapeHtml(draft?.summary || "")}

Objetivo: ${escapeHtml(draft?.business_goal || "")}
Versao atual: v${escapeHtml(String(draft?.version_number || 1))}
Historico: ${escapeHtml(String(draft?.version_count || 1))} versao(oes)
Parametros: ${escapeHtml(String(draft?.parameter_count || 0))}
Obrigatorios: ${escapeHtml(String(draft?.required_parameter_count || 0))}
Aprovacao: ${draft?.requires_director_approval ? "Diretor obrigatorio" : "Nao"}
${parameters.length > 0 ? parameters.map((item) => `
${escapeHtml(item.name)}
${escapeHtml(item.description)}
${escapeHtml(item.parameter_type)}
`).join("") : `
Sem parametros
A tool foi cadastrada sem parametros de entrada.
`}
Avisos
${warnings.length > 0 ? `
    ${warnings.map((item) => `
  • ${escapeHtml(item)}
  • `).join("")}
` : `
Nenhum aviso extra para este pre-cadastro.
`}
Proximos passos
${nextSteps.length > 0 ? `
    ${nextSteps.map((item) => `
  • ${escapeHtml(item)}
  • `).join("")}
` : `
Sem orientacoes adicionais.
`}
`; } } function mountCollaboratorBoard(board) { const form = board.querySelector('[data-admin-collaborator-form="true"]'); const feedback = document.getElementById("admin-collaborator-feedback"); const list = board.querySelector("[data-collaborator-list]"); const refreshButton = board.querySelector("[data-admin-collaborator-refresh]"); const refreshLabel = board.querySelector("[data-collaborator-refresh-label]"); const refreshSpinner = board.querySelector("[data-collaborator-refresh-spinner]"); const submitButton = form?.querySelector('button[type="submit"]'); const submitLabel = form?.querySelector("[data-collaborator-submit-label]"); const submitSpinner = form?.querySelector("[data-collaborator-submit-spinner]"); if (!form || !feedback || !list || !refreshButton || !refreshLabel || !refreshSpinner || !submitButton || !submitLabel || !submitSpinner) { return; } refreshButton.addEventListener("click", () => { void loadCollaborators(); }); form.addEventListener("submit", async (event) => { event.preventDefault(); toggleSubmitting(true); clearFeedback(); const formData = new FormData(form); const payload = { display_name: String(formData.get("display_name") || "").trim(), email: String(formData.get("email") || "").trim(), password: String(formData.get("password") || ""), is_active: Boolean(formData.get("is_active")), }; try { const response = await fetch(board.dataset.collaboratorCollectionEndpoint, { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify(payload), }); const body = await readJson(response); if (!response.ok) { throw new Error(body?.detail || "Nao foi possivel criar o colaborador."); } showFeedback("success", body?.message || "Colaborador criado com sucesso."); form.reset(); const activeField = form.querySelector('[name="is_active"]'); if (activeField instanceof HTMLInputElement) { activeField.checked = true; } await loadCollaborators(); } catch (error) { showFeedback("danger", error instanceof Error ? error.message : "Erro inesperado ao cadastrar colaborador."); } finally { toggleSubmitting(false); } }); list.addEventListener("click", async (event) => { const target = event.target; if (!(target instanceof HTMLElement) || !target.matches("[data-collaborator-toggle]")) { return; } const collaboratorId = target.dataset.collaboratorId; const nextState = target.dataset.collaboratorNextState === "true"; if (!collaboratorId) { return; } toggleRefreshing(true); clearFeedback(); try { const response = await fetch(`${board.dataset.collaboratorCollectionEndpoint}/${collaboratorId}/status`, { method: "PATCH", credentials: "same-origin", headers: { "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify({ is_active: nextState }), }); const body = await readJson(response); if (!response.ok) { throw new Error(body?.detail || "Nao foi possivel atualizar o status do colaborador."); } showFeedback("success", body?.message || "Status do colaborador atualizado com sucesso."); await loadCollaborators(); } catch (error) { showFeedback("danger", error instanceof Error ? error.message : "Erro inesperado ao atualizar o colaborador."); toggleRefreshing(false); } }); void loadCollaborators(); async function loadCollaborators() { toggleRefreshing(true); const result = await fetchPanelJson(board.dataset.collaboratorCollectionEndpoint); if (result.ok) { renderCollaborators(result.body); } else { list.innerHTML = `

Leitura indisponivel

${escapeHtml(result.message || "Nao foi possivel carregar os colaboradores.")}

`; setText("[data-collaborator-total]", "0"); setText("[data-collaborator-active-count]", "0"); setText("[data-collaborator-inactive-count]", "0"); showFeedback("warning", result.message || "A sessao atual nao pode consultar os colaboradores."); } toggleRefreshing(false); } function renderCollaborators(payload) { const collaborators = Array.isArray(payload?.collaborators) ? payload.collaborators : []; setText("[data-collaborator-total]", String(payload?.total || 0)); setText("[data-collaborator-active-count]", String(payload?.active_count || 0)); setText("[data-collaborator-inactive-count]", String(payload?.inactive_count || 0)); list.innerHTML = collaborators.length > 0 ? collaborators.map((item) => { const statusBadge = item?.is_active ? "bg-success-subtle text-success-emphasis border border-success-subtle" : "bg-secondary-subtle text-secondary-emphasis border border-secondary-subtle"; const actionLabel = item?.is_active ? "Desativar acesso" : "Reativar acesso"; const buttonClass = item?.is_active ? "btn-outline-secondary" : "btn-outline-dark"; const lastLogin = item?.last_login_at ? formatDateTime(item.last_login_at) : "Ainda nao acessou"; return `
${escapeHtml(item?.role || "colaborador")}

${escapeHtml(item?.display_name || "Colaborador")}

${escapeHtml(item?.email || "")}
${item?.is_active ? "Ativo" : "Inativo"}
Ultimo login: ${escapeHtml(lastLogin)}
ID: ${escapeHtml(String(item?.id || "-"))}
`; }).join("") : `

Nenhum colaborador cadastrado ainda

Use o formulario ao lado para criar o primeiro colaborador administrativo.

`; } function toggleSubmitting(isLoading) { submitButton.disabled = isLoading; submitSpinner.classList.toggle("d-none", !isLoading); submitLabel.textContent = isLoading ? "Criando..." : "Criar colaborador"; } function toggleRefreshing(isLoading) { refreshButton.disabled = isLoading; refreshSpinner.classList.toggle("d-none", !isLoading); refreshLabel.textContent = isLoading ? "Atualizando..." : "Atualizar lista"; } function clearFeedback() { feedback.className = "alert d-none rounded-4 mb-4"; feedback.textContent = ""; } function showFeedback(variant, message) { feedback.className = `alert alert-${variant} rounded-4 mb-4`; feedback.textContent = message; } } function mountSystemConfigurationPage(page) { const refreshButton = page.querySelector("[data-admin-system-refresh]"); const refreshLabel = page.querySelector("[data-system-refresh-label]"); const refreshSpinner = page.querySelector("[data-system-refresh-spinner]"); const feedback = document.getElementById("admin-system-configuration-feedback"); const functionalList = page.querySelector("[data-system-functional-list]"); const parentKeys = page.querySelector("[data-system-parent-keys]"); const botSettingsList = page.querySelector("[data-system-bot-settings-list]"); const runtimeSummary = page.querySelector("[data-system-runtime-summary]"); const securitySummary = page.querySelector("[data-system-security-summary]"); const modelRuntimeSummary = page.querySelector("[data-system-model-runtime-summary]"); const sourceList = page.querySelector("[data-system-source-list]"); if (!refreshButton || !refreshLabel || !refreshSpinner || !feedback || !functionalList || !parentKeys || !botSettingsList || !runtimeSummary || !securitySummary || !modelRuntimeSummary || !sourceList) { return; } refreshButton.addEventListener("click", () => { void loadConfiguration(); }); void loadConfiguration(); async function loadConfiguration() { toggleRefreshing(true); clearFeedback(); const [overviewResult, runtimeResult, securityResult, modelRuntimesResult, functionalResult, botGovernanceResult] = await Promise.all([ fetchPanelJson(page.dataset.overviewEndpoint), fetchPanelJson(page.dataset.runtimeEndpoint), fetchPanelJson(page.dataset.securityEndpoint), fetchPanelJson(page.dataset.modelRuntimesEndpoint), fetchPanelJson(page.dataset.functionalEndpoint), fetchPanelJson(page.dataset.botGovernanceEndpoint), ]); if (functionalResult.ok) { renderFunctionalCatalog(functionalResult.body); } else { renderLockedState(functionalList, "Catalogo funcional indisponivel", functionalResult.message || "Nao foi possivel carregar o catalogo funcional."); setText("[data-system-config-count]", "0"); setText("[data-system-functional-mode]", "Bloqueado"); } if (botGovernanceResult.ok) { renderBotGovernance(botGovernanceResult.body); } else { parentKeys.innerHTML = 'Bloqueado'; renderLockedState(botSettingsList, "Governanca do bot indisponivel", botGovernanceResult.message || "Nao foi possivel carregar os campos governados pelo bot."); setText("[data-system-bot-setting-count]", "0"); } if (runtimeResult.ok) { renderRuntime(runtimeResult.body); } else { renderLockedState(runtimeSummary, "Runtime protegido", runtimeResult.message || "A sessao atual nao pode ler o runtime administrativo."); } if (securityResult.ok) { renderSecurity(securityResult.body); } else { renderLockedState(securitySummary, "Seguranca protegida", securityResult.message || "A sessao atual nao pode ler o snapshot de seguranca."); } if (modelRuntimesResult.ok) { renderModelRuntimes(modelRuntimesResult.body); } else { renderLockedState(modelRuntimeSummary, "Separacao tecnica protegida", modelRuntimesResult.message || "A sessao atual nao pode ler os perfis de runtime."); setText("[data-system-runtime-profile-count]", "0"); } if (overviewResult.ok) { renderSources(overviewResult.body); } else { renderLockedState(sourceList, "Overview tecnico protegido", overviewResult.message || "A sessao atual nao pode ler as fontes do snapshot."); setText("[data-system-source-count]", "0"); } const directorOnlyLocked = [overviewResult, runtimeResult, securityResult, modelRuntimesResult].some((result) => !result.ok); if (functionalResult.ok && botGovernanceResult.ok && directorOnlyLocked) { showFeedback("info", "A sessao atual consegue consultar a configuracao funcional do sistema. Blocos de runtime, seguranca e separacao tecnica exigem manage_settings."); } else if (functionalResult.ok && botGovernanceResult.ok) { showFeedback("success", "Snapshot de configuracoes do sistema carregado com sucesso."); } else { showFeedback("warning", "A tela nao conseguiu carregar todas as superficies de configuracao com a sessao atual."); } setText("[data-system-last-sync]", formatNow()); toggleRefreshing(false); } function renderFunctionalCatalog(payload) { const configurations = Array.isArray(payload?.configurations) ? payload.configurations : []; setText("[data-system-config-count]", String(configurations.length)); setText("[data-system-functional-mode]", formatModeLabel(payload?.mode)); functionalList.innerHTML = configurations.length > 0 ? configurations.map((item) => { const writableCount = Array.isArray(item?.fields) ? item.fields.filter((field) => field?.writable).length : 0; const editingLabel = writableCount > 0 ? `${writableCount} campo(s) ajustavel(is)` : "Somente leitura"; const impactLabel = item?.affects_product_runtime ? "Impacta o atendimento" : "Uso interno do admin"; return `
${escapeHtml(formatDomainLabel(item?.domain || "sistema"))}

${escapeHtml(formatConfigTitle(item?.config_key || "configuracao"))}

${escapeHtml(item?.description || "")}
${escapeHtml(formatMutabilityLabel(item?.mutability || "readonly"))}
Campos visiveis: ${escapeHtml(String(Array.isArray(item?.fields) ? item.fields.length : 0))}
Ajustes nesta fase: ${escapeHtml(editingLabel)}
Impacto: ${escapeHtml(impactLabel)}
`; }).join("") : `

Nenhuma configuracao encontrada

O catalogo funcional nao retornou itens nesta leitura.

`; } function renderBotGovernance(payload) { const settings = Array.isArray(payload?.settings) ? payload.settings : []; const parentConfigKeys = Array.isArray(payload?.parent_config_keys) ? payload.parent_config_keys : []; setText("[data-system-bot-setting-count]", String(settings.length)); parentKeys.innerHTML = parentConfigKeys.length > 0 ? parentConfigKeys.map((item) => `${escapeHtml(item)}`).join("") : 'Sem parent keys'; botSettingsList.innerHTML = settings.length > 0 ? settings.slice(0, 12).map((item) => `
${escapeHtml(formatDomainLabel(item?.area || "bot"))}

${escapeHtml(humanizeKey(item?.setting_key || "setting"))}

${escapeHtml(item?.description || "")}
${escapeHtml(formatMutabilityLabel(item?.mutability || "versioned"))}
Grupo: ${escapeHtml(formatConfigTitle(item?.parent_config_key || "-"))}
Escrita direta: ${item?.direct_product_write_allowed ? 'Permitida' : 'Bloqueada'}
`).join("") : `

Nenhum campo governado encontrado

A governanca do bot nao retornou itens nesta leitura.

`; } function renderRuntime(payload) { const runtime = payload?.runtime; if (!runtime) { renderLockedState(runtimeSummary, "Runtime indisponivel", "Nao foi possivel interpretar a resposta do runtime."); return; } runtimeSummary.innerHTML = `
Aplicacao
Nome: ${escapeHtml(runtime?.application?.app_name || "-")}
Ambiente: ${escapeHtml(runtime?.application?.environment || "-")}
Versao: ${escapeHtml(runtime?.application?.version || "-")}
Modo debug: ${runtime?.application?.debug ? 'Ativo' : 'Desligado'}

Detalhes internos de infraestrutura e cookies nao aparecem aqui para manter a tela mais limpa.

`; } function renderSecurity(payload) { const security = payload?.security; if (!security) { renderLockedState(securitySummary, "Seguranca indisponivel", "Nao foi possivel interpretar a resposta de seguranca."); return; } securitySummary.innerHTML = `
Senha e sessao
Tamanho minimo: ${escapeHtml(String(security?.password?.min_length || 0))} caracteres
Requisitos: ${renderPasswordRequirements(security?.password)}
Acesso expira em: ${escapeHtml(String(security?.tokens?.access_token_ttl_minutes || 0))} min
Renovacao disponivel por: ${escapeHtml(String(security?.tokens?.refresh_token_ttl_days || 0))} dias

Informacoes internas de assinatura e bootstrap ficam fora desta tela para reduzir ruido.

`; } function renderModelRuntimes(payload) { const modelRuntimes = payload?.model_runtimes; const profiles = Array.isArray(modelRuntimes?.runtime_profiles) ? modelRuntimes.runtime_profiles : []; const separationRules = Array.isArray(modelRuntimes?.separation_rules) ? modelRuntimes.separation_rules : []; setText("[data-system-runtime-profile-count]", String(profiles.length)); modelRuntimeSummary.innerHTML = profiles.length > 0 ? `
Regras principais
${profiles.map((profile) => `
${escapeHtml(formatRuntimeTargetLabel(profile?.runtime_target || "runtime"))}

${escapeHtml(formatConfigTitle(profile?.config_key || "perfil"))}

${escapeHtml(profile?.description || "")}
${profile?.affects_customer_response ? 'Atendimento' : 'Interno'}
Servico: ${escapeHtml(humanizeKey(profile?.consumed_by_service || "-"))}
Uso principal: ${escapeHtml(formatPurposeLabel(profile?.purpose || "-"))}
Gera tools: ${profile?.can_generate_code ? 'Sim' : 'Nao'}
Rollback separado: ${profile?.rollback_independently ? 'Sim' : 'Nao'}
`).join("")}
` : `

Nenhum perfil retornado

A separacao de runtime nao retornou perfis nesta leitura.

`; } function renderSources(payload) { const sources = Array.isArray(payload?.sources) ? payload.sources : []; setText("[data-system-source-count]", String(sources.length)); sourceList.innerHTML = sources.length > 0 ? sources.map((item) => `
${escapeHtml(formatSourceLabel(item?.source || "origem"))}

${escapeHtml(formatConfigTitle(item?.key || "configuracao"))}

${escapeHtml(item?.description || "")}
${item?.mutable ? 'Pode mudar' : 'Base fixa'}
`).join("") : `

Nenhuma fonte encontrada

O overview tecnico nao retornou fontes nesta leitura.

`; } function renderPasswordRequirements(passwordPolicy) { const requirements = []; if (passwordPolicy?.require_uppercase) requirements.push("letra maiuscula"); if (passwordPolicy?.require_lowercase) requirements.push("letra minuscula"); if (passwordPolicy?.require_digit) requirements.push("numero"); if (passwordPolicy?.require_symbol) requirements.push("simbolo"); return requirements.length > 0 ? escapeHtml(requirements.join(", ")) : "nenhum requisito adicional"; } function renderLockedState(container, title, message) { container.innerHTML = `

${escapeHtml(title)}

${escapeHtml(message)}

`; } function toggleRefreshing(isLoading) { refreshButton.disabled = isLoading; refreshSpinner.classList.toggle("d-none", !isLoading); refreshLabel.textContent = isLoading ? "Atualizando..." : "Atualizar leitura"; } function clearFeedback() { feedback.className = "alert d-none rounded-4 mb-4"; feedback.textContent = ""; } function showFeedback(variant, message) { feedback.className = `alert alert-${variant} rounded-4 mb-4`; feedback.textContent = message; } } function mountSalesRevenueReportsPage(page) { const refreshButton = page.querySelector("[data-admin-commercial-refresh]"); const refreshLabel = page.querySelector("[data-commercial-refresh-label]"); const refreshSpinner = page.querySelector("[data-commercial-refresh-spinner]"); const feedback = document.getElementById("admin-commercial-feedback"); const salesMetrics = page.querySelector("[data-sales-overview-metrics]"); const salesMaterialization = page.querySelector("[data-sales-materialization]"); const salesReportList = page.querySelector("[data-sales-report-list]"); const salesNextSteps = page.querySelector("[data-sales-next-steps]"); const revenueMetrics = page.querySelector("[data-revenue-overview-metrics]"); const revenueMaterialization = page.querySelector("[data-revenue-materialization]"); const revenueReportList = page.querySelector("[data-revenue-report-list]"); const revenueNextSteps = page.querySelector("[data-revenue-next-steps]"); if (!refreshButton || !refreshLabel || !refreshSpinner || !feedback || !salesMetrics || !salesMaterialization || !salesReportList || !salesNextSteps || !revenueMetrics || !revenueMaterialization || !revenueReportList || !revenueNextSteps) { return; } refreshButton.addEventListener("click", () => { void loadReports(); }); void loadReports(); async function loadReports() { toggleRefreshing(true); clearFeedback(); const [salesResult, revenueResult] = await Promise.all([ fetchPanelJson(page.dataset.salesOverviewEndpoint), fetchPanelJson(page.dataset.revenueOverviewEndpoint), ]); if (salesResult.ok) { renderDomainOverview({ kind: "sales", payload: salesResult.body, metricsTarget: salesMetrics, materializationTarget: salesMaterialization, reportsTarget: salesReportList, nextStepsTarget: salesNextSteps }); } else { renderLockedState(salesMetrics, "Vendas indisponivel", salesResult.message || "Nao foi possivel carregar o overview de vendas."); salesMaterialization.innerHTML = ""; salesReportList.innerHTML = ""; salesNextSteps.innerHTML = ""; setText("[data-sales-report-count]", "0"); } if (revenueResult.ok) { renderDomainOverview({ kind: "revenue", payload: revenueResult.body, metricsTarget: revenueMetrics, materializationTarget: revenueMaterialization, reportsTarget: revenueReportList, nextStepsTarget: revenueNextSteps }); } else { renderLockedState(revenueMetrics, "Arrecadacao indisponivel", revenueResult.message || "Nao foi possivel carregar o overview de arrecadacao."); revenueMaterialization.innerHTML = ""; revenueReportList.innerHTML = ""; revenueNextSteps.innerHTML = ""; setText("[data-revenue-report-count]", "0"); } if (salesResult.ok && revenueResult.ok) { const salesPayload = salesResult.body; const revenuePayload = revenueResult.body; const datasetCount = uniqueCount(salesPayload?.source_dataset_keys, revenuePayload?.source_dataset_keys); const syncStrategy = salesPayload?.materialization?.sync_strategy === revenuePayload?.materialization?.sync_strategy ? salesPayload?.materialization?.sync_strategy : "mixed"; setText("[data-commercial-dataset-count]", String(datasetCount)); setText("[data-commercial-sync-strategy]", formatSyncStrategyLabel(syncStrategy || "--")); showFeedback("success", "Relatorios de vendas e arrecadacao carregados com sucesso na sessao do painel."); } else if (salesResult.ok || revenueResult.ok) { const onlyLoaded = salesResult.ok ? "vendas" : "arrecadacao"; const datasetCount = salesResult.ok ? (Array.isArray(salesResult.body?.source_dataset_keys) ? salesResult.body.source_dataset_keys.length : 0) : (Array.isArray(revenueResult.body?.source_dataset_keys) ? revenueResult.body.source_dataset_keys.length : 0); const syncStrategy = salesResult.ok ? salesResult.body?.materialization?.sync_strategy : revenueResult.body?.materialization?.sync_strategy; setText("[data-commercial-dataset-count]", String(datasetCount)); setText("[data-commercial-sync-strategy]", formatSyncStrategyLabel(syncStrategy || "--")); showFeedback("warning", `A tela carregou apenas o overview de ${onlyLoaded} com a sessao atual.`); } else { setText("[data-commercial-dataset-count]", "0"); setText("[data-commercial-sync-strategy]", "--"); showFeedback("warning", "Nao foi possivel carregar os relatorios comerciais na sessao atual."); } setText("[data-commercial-last-sync]", formatNow()); toggleRefreshing(false); } function renderDomainOverview({ kind, payload, metricsTarget, materializationTarget, reportsTarget, nextStepsTarget }) { const reports = Array.isArray(payload?.reports) ? payload.reports : []; const metrics = Array.isArray(payload?.metrics) ? payload.metrics : []; const nextSteps = Array.isArray(payload?.next_steps) ? payload.next_steps : []; const reportCountSelector = kind === "sales" ? "[data-sales-report-count]" : "[data-revenue-report-count]"; setText(reportCountSelector, String(reports.length)); metricsTarget.innerHTML = metrics.length > 0 ? `
${metrics.map((item) => `
${escapeHtml(item?.label || item?.key || "metrica")}
${escapeHtml(item?.value || "0")}
${escapeHtml(item?.description || "")}
`).join("")}
` : `

Metricas nao disponiveis

O overview nao retornou metricas nesta leitura.

`; materializationTarget.innerHTML = payload?.materialization ? `
Atualizacao da tela
Ritmo: ${escapeHtml(formatSyncStrategyLabel(payload?.materialization?.sync_strategy || "-"))}
Camada: ${escapeHtml(formatStorageLabel(payload?.materialization?.storage_shape || "-"))}
Consulta: ${escapeHtml(formatQuerySurfaceLabel(payload?.materialization?.query_surface || "-"))}
` : ""; reportsTarget.innerHTML = reports.length > 0 ? reports.map((item) => `

${escapeHtml(item?.label || humanizeKey(item?.report_key || "relatorio"))}

${escapeHtml(item?.description || "")}
${escapeHtml(formatGranularityLabel(item?.default_granularity || "aggregate"))}
Indicadores: ${escapeHtml(String((item?.supported_metric_keys || []).length))}Recortes: ${escapeHtml(String((item?.supported_dimension_fields || []).length))}
`).join("") : `

Nenhum relatorio previsto

O overview nao retornou relatorios para este dominio.

`; nextStepsTarget.innerHTML = nextSteps.length > 0 ? nextSteps.map((item) => `
${escapeHtml(item)}
`).join("") : `

Sem proximos passos

Nenhuma orientacao adicional foi retornada para este overview.

`; } function renderLockedState(container, title, message) { container.innerHTML = `

${escapeHtml(title)}

${escapeHtml(message)}

`; } function toggleRefreshing(isLoading) { refreshButton.disabled = isLoading; refreshSpinner.classList.toggle("d-none", !isLoading); refreshLabel.textContent = isLoading ? "Atualizando..." : "Atualizar leitura"; } function clearFeedback() { feedback.className = "alert d-none rounded-4 mb-4"; feedback.textContent = ""; } function showFeedback(variant, message) { feedback.className = `alert alert-${variant} rounded-4 mb-4`; feedback.textContent = message; } } function mountRentalReportsPage(page) { const refreshButton = page.querySelector("[data-admin-rental-refresh]"); const refreshLabel = page.querySelector("[data-rental-refresh-label]"); const refreshSpinner = page.querySelector("[data-rental-refresh-spinner]"); const feedback = document.getElementById("admin-rental-feedback"); const overviewMetrics = page.querySelector("[data-rental-overview-metrics]"); const materialization = page.querySelector("[data-rental-materialization]"); const reportList = page.querySelector("[data-rental-report-list]"); const nextSteps = page.querySelector("[data-rental-next-steps]"); if (!refreshButton || !refreshLabel || !refreshSpinner || !feedback || !overviewMetrics || !materialization || !reportList || !nextSteps) { return; } refreshButton.addEventListener("click", () => { void loadOverview(); }); void loadOverview(); async function loadOverview() { toggleRefreshing(true); clearFeedback(); const result = await fetchPanelJson(page.dataset.rentalOverviewEndpoint); if (result.ok) { const payload = result.body; const metrics = Array.isArray(payload?.metrics) ? payload.metrics : []; const reports = Array.isArray(payload?.reports) ? payload.reports : []; const datasets = Array.isArray(payload?.source_dataset_keys) ? payload.source_dataset_keys : []; const plannedSteps = Array.isArray(payload?.next_steps) ? payload.next_steps : []; setText("[data-rental-report-count]", String(reports.length)); setText("[data-rental-dataset-count]", String(datasets.length)); setText("[data-rental-sync-strategy]", formatSyncStrategyLabel(payload?.materialization?.sync_strategy || "--")); setText("[data-rental-source-domain]", formatDomainLabel(payload?.source_domain || "--")); overviewMetrics.innerHTML = metrics.length > 0 ? `
${metrics.map((item) => `
${escapeHtml(item?.label || item?.key || "metrica")}
${escapeHtml(item?.value || "0")}
${escapeHtml(item?.description || "")}
`).join("")}
` : `

Metricas nao disponiveis

O overview de locacao nao retornou metricas nesta leitura.

`; materialization.innerHTML = payload?.materialization ? `
Atualizacao da tela
Ritmo: ${escapeHtml(formatSyncStrategyLabel(payload?.materialization?.sync_strategy || "-"))}
Camada: ${escapeHtml(formatStorageLabel(payload?.materialization?.storage_shape || "-"))}
Consulta: ${escapeHtml(formatQuerySurfaceLabel(payload?.materialization?.query_surface || "-"))}
` : ""; reportList.innerHTML = reports.length > 0 ? reports.map((item) => `

${escapeHtml(item?.label || humanizeKey(item?.report_key || "relatorio"))}

${escapeHtml(item?.description || "")}
${escapeHtml(formatGranularityLabel(item?.default_granularity || "aggregate"))}
Indicadores: ${escapeHtml(String((item?.supported_metric_keys || []).length))}Recortes: ${escapeHtml(String((item?.supported_dimension_fields || []).length))}Filtros: ${escapeHtml(String((item?.supported_filter_fields || []).length))}
`).join("") : `

Nenhum relatorio previsto

O overview nao retornou relatorios de locacao nesta leitura.

`; nextSteps.innerHTML = plannedSteps.length > 0 ? plannedSteps.map((item) => `
${escapeHtml(item)}
`).join("") : `

Sem proximos passos

Nenhuma orientacao adicional foi retornada para locacao.

`; showFeedback("success", "Relatorios de locacao carregados com sucesso na sessao do painel."); } else { setText("[data-rental-report-count]", "0"); setText("[data-rental-dataset-count]", "0"); setText("[data-rental-sync-strategy]", "--"); setText("[data-rental-source-domain]", "--"); overviewMetrics.innerHTML = `

Locacao indisponivel

${escapeHtml(result.message || "Nao foi possivel carregar o overview de locacao.")}

`; materialization.innerHTML = ""; reportList.innerHTML = ""; nextSteps.innerHTML = ""; showFeedback("warning", result.message || "Nao foi possivel carregar os relatorios de locacao na sessao atual."); } setText("[data-rental-last-sync]", formatNow()); toggleRefreshing(false); } function toggleRefreshing(isLoading) { refreshButton.disabled = isLoading; refreshSpinner.classList.toggle("d-none", !isLoading); refreshLabel.textContent = isLoading ? "Atualizando..." : "Atualizar leitura"; } function clearFeedback() { feedback.className = "alert d-none rounded-4 mb-4"; feedback.textContent = ""; } function showFeedback(variant, message) { feedback.className = `alert alert-${variant} rounded-4 mb-4`; feedback.textContent = message; } } function mountBotMonitoringPage(page) { const refreshButton = page.querySelector("[data-admin-bot-monitoring-refresh]"); const refreshLabel = page.querySelector("[data-bot-monitoring-refresh-label]"); const refreshSpinner = page.querySelector("[data-bot-monitoring-refresh-spinner]"); const feedback = document.getElementById("admin-bot-monitoring-feedback"); const botFlowMetrics = page.querySelector("[data-bot-flow-overview-metrics]"); const botFlowMaterialization = page.querySelector("[data-bot-flow-materialization]"); const botFlowReportList = page.querySelector("[data-bot-flow-report-list]"); const botFlowNextSteps = page.querySelector("[data-bot-flow-next-steps]"); const telemetryMetrics = page.querySelector("[data-bot-telemetry-overview-metrics]"); const telemetryMaterialization = page.querySelector("[data-bot-telemetry-materialization]"); const telemetryReportList = page.querySelector("[data-bot-telemetry-report-list]"); const telemetryNextSteps = page.querySelector("[data-bot-telemetry-next-steps]"); if (!refreshButton || !refreshLabel || !refreshSpinner || !feedback || !botFlowMetrics || !botFlowMaterialization || !botFlowReportList || !botFlowNextSteps || !telemetryMetrics || !telemetryMaterialization || !telemetryReportList || !telemetryNextSteps) { return; } refreshButton.addEventListener("click", () => { void loadMonitoring(); }); void loadMonitoring(); async function loadMonitoring() { toggleRefreshing(true); clearFeedback(); const [botFlowResult, telemetryResult] = await Promise.all([ fetchPanelJson(page.dataset.botFlowOverviewEndpoint), fetchPanelJson(page.dataset.telemetryOverviewEndpoint), ]); if (botFlowResult.ok) { renderDomainOverview({ kind: "flow", payload: botFlowResult.body, metricsTarget: botFlowMetrics, materializationTarget: botFlowMaterialization, reportsTarget: botFlowReportList, nextStepsTarget: botFlowNextSteps }); } else { renderLockedState(botFlowMetrics, "Fluxo do bot indisponivel", botFlowResult.message || "Nao foi possivel carregar o overview operacional do bot."); botFlowMaterialization.innerHTML = ""; botFlowReportList.innerHTML = ""; botFlowNextSteps.innerHTML = ""; setText("[data-bot-flow-report-count]", "0"); } if (telemetryResult.ok) { renderDomainOverview({ kind: "telemetry", payload: telemetryResult.body, metricsTarget: telemetryMetrics, materializationTarget: telemetryMaterialization, reportsTarget: telemetryReportList, nextStepsTarget: telemetryNextSteps }); } else { renderLockedState(telemetryMetrics, "Telemetria indisponivel", telemetryResult.message || "Nao foi possivel carregar o overview de telemetria conversacional."); telemetryMaterialization.innerHTML = ""; telemetryReportList.innerHTML = ""; telemetryNextSteps.innerHTML = ""; setText("[data-bot-telemetry-report-count]", "0"); } if (botFlowResult.ok && telemetryResult.ok) { const botFlowPayload = botFlowResult.body; const telemetryPayload = telemetryResult.body; const datasetCount = uniqueCount(botFlowPayload?.source_dataset_keys, telemetryPayload?.source_dataset_keys); const syncStrategy = botFlowPayload?.materialization?.sync_strategy === telemetryPayload?.materialization?.sync_strategy ? botFlowPayload?.materialization?.sync_strategy : "mixed"; setText("[data-bot-monitoring-dataset-count]", String(datasetCount)); setText("[data-bot-monitoring-sync-strategy]", formatSyncStrategyLabel(syncStrategy || "--")); showFeedback("success", "Fluxo operacional do bot e telemetria conversacional carregados com sucesso na sessao do painel."); } else if (botFlowResult.ok || telemetryResult.ok) { const onlyLoaded = botFlowResult.ok ? "fluxo do bot" : "telemetria conversacional"; const datasetCount = botFlowResult.ok ? (Array.isArray(botFlowResult.body?.source_dataset_keys) ? botFlowResult.body.source_dataset_keys.length : 0) : (Array.isArray(telemetryResult.body?.source_dataset_keys) ? telemetryResult.body.source_dataset_keys.length : 0); const syncStrategy = botFlowResult.ok ? botFlowResult.body?.materialization?.sync_strategy : telemetryResult.body?.materialization?.sync_strategy; setText("[data-bot-monitoring-dataset-count]", String(datasetCount)); setText("[data-bot-monitoring-sync-strategy]", formatSyncStrategyLabel(syncStrategy || "--")); showFeedback("warning", `A tela carregou apenas ${onlyLoaded} com a sessao atual.`); } else { setText("[data-bot-monitoring-dataset-count]", "0"); setText("[data-bot-monitoring-sync-strategy]", "--"); showFeedback("warning", "Nao foi possivel carregar o monitoramento operacional do bot na sessao atual."); } setText("[data-bot-monitoring-last-sync]", formatNow()); toggleRefreshing(false); } function renderDomainOverview({ kind, payload, metricsTarget, materializationTarget, reportsTarget, nextStepsTarget }) { const reports = Array.isArray(payload?.reports) ? payload.reports : []; const metrics = Array.isArray(payload?.metrics) ? payload.metrics : []; const nextSteps = Array.isArray(payload?.next_steps) ? payload.next_steps : []; const reportCountSelector = kind === "flow" ? "[data-bot-flow-report-count]" : "[data-bot-telemetry-report-count]"; setText(reportCountSelector, String(reports.length)); metricsTarget.innerHTML = metrics.length > 0 ? `
${metrics.map((item) => `
${escapeHtml(item?.label || item?.key || "metrica")}
${escapeHtml(item?.value || "0")}
${escapeHtml(item?.description || "")}
`).join("")}
` : `

Metricas nao disponiveis

O overview nao retornou metricas nesta leitura.

`; materializationTarget.innerHTML = payload?.materialization ? `
Atualizacao da tela
Ritmo: ${escapeHtml(formatSyncStrategyLabel(payload?.materialization?.sync_strategy || "-"))}
Camada: ${escapeHtml(formatStorageLabel(payload?.materialization?.storage_shape || "-"))}
Consulta: ${escapeHtml(formatQuerySurfaceLabel(payload?.materialization?.query_surface || "-"))}
` : ""; reportsTarget.innerHTML = reports.length > 0 ? reports.map((item) => `

${escapeHtml(item?.label || humanizeKey(item?.report_key || "relatorio"))}

${escapeHtml(item?.description || "")}
${escapeHtml(formatGranularityLabel(item?.default_granularity || "aggregate"))}
Indicadores: ${escapeHtml(String((item?.supported_metric_keys || []).length))}Recortes: ${escapeHtml(String((item?.supported_dimension_fields || []).length))}
`).join("") : `

Nenhum relatorio previsto

O overview nao retornou relatorios para este dominio.

`; nextStepsTarget.innerHTML = nextSteps.length > 0 ? nextSteps.map((item) => `
${escapeHtml(item)}
`).join("") : `

Sem proximos passos

Nenhuma orientacao adicional foi retornada para este overview.

`; } function renderLockedState(container, title, message) { container.innerHTML = `

${escapeHtml(title)}

${escapeHtml(message)}

`; } function toggleRefreshing(isLoading) { refreshButton.disabled = isLoading; refreshSpinner.classList.toggle("d-none", !isLoading); refreshLabel.textContent = isLoading ? "Atualizando..." : "Atualizar leitura"; } function clearFeedback() { feedback.className = "alert d-none rounded-4 mb-4"; feedback.textContent = ""; } function showFeedback(variant, message) { feedback.className = `alert alert-${variant} rounded-4 mb-4`; feedback.textContent = message; } } function humanizeKey(value) { const raw = String(value || "").trim(); if (!raw) { return "-"; } return raw .replace(/[_-]+/g, " ") .replace(/\b\w/g, (char) => char.toUpperCase()); } function formatFriendlyLabel(value, mapping, fallback = "-") { const normalized = String(value || "").trim().toLowerCase(); if (!normalized) { return fallback; } return mapping[normalized] || humanizeKey(normalized); } function formatConfigTitle(value) { return formatFriendlyLabel(value, { application: "Aplicacao", database: "Banco administrativo", security: "Politicas de acesso", panel_session: "Sessao do painel", functional_configuration_contracts: "Catalogo funcional", bot_governed_configuration_contracts: "Ajustes do atendimento", model_runtime_separation: "Separacao de modelos", write_governance: "Protecao de escrita", atendimento_runtime_profile: "Modelo do atendimento", tool_generation_runtime_profile: "Geracao de tools", published_runtime_state: "Estado publicado" }, "Configuracao"); } function formatModeLabel(value) { return formatFriendlyLabel(value, { shared_contract_bootstrap: "Contrato base", sales_contract_bootstrap: "Estrutura inicial", revenue_contract_bootstrap: "Estrutura inicial", rental_contract_bootstrap: "Estrutura inicial", bot_flow_contract_bootstrap: "Estrutura inicial", conversation_telemetry_contract_bootstrap: "Estrutura inicial", mixed: "Leituras combinadas" }, "Leitura base"); } function formatMutabilityLabel(value) { return formatFriendlyLabel(value, { readonly: "Somente leitura", read_only: "Somente leitura", versioned: "Versionado", mutable: "Editavel", governed: "Governado" }, "Somente leitura"); } function formatGranularityLabel(value) { return formatFriendlyLabel(value, { aggregate: "Visao consolidada", daily: "Por dia", weekly: "Por semana", monthly: "Por mes" }, "Visao consolidada"); } function formatSyncStrategyLabel(value) { return formatFriendlyLabel(value, { etl_incremental: "Atualizacao em lote", snapshot_refresh: "Atualizacao por snapshot", mixed: "Leituras combinadas" }, "Nao informado"); } function formatStorageLabel(value) { return formatFriendlyLabel(value, { snapshot_table: "Snapshot consolidado", dedicated_view: "Visao preparada" }, "Nao informado"); } function formatQuerySurfaceLabel(value) { return formatFriendlyLabel(value, { dedicated_view: "Consulta preparada", analytical_view: "Consulta analitica", report_endpoint: "Consulta do painel" }, "Nao informado"); } function formatDomainLabel(value) { return formatFriendlyLabel(value, { sistema: "Sistema", sales: "Vendas", arrecadacao: "Arrecadacao", rental: "Locacao", fluxo_bot: "Fluxo do bot", telemetria_conversacional: "Telemetria conversacional", bot: "Atendimento" }, "-"); } function formatSourceLabel(value) { return formatFriendlyLabel(value, { env: "Ambiente", runtime: "Aplicacao", shared_contract: "Contrato compartilhado", runtime_guard: "Protecao ativa" }, "Origem"); } function formatRuntimeTargetLabel(value) { return formatFriendlyLabel(value, { atendimento: "Atendimento", tool_generation: "Geracao de tools" }, "Runtime"); } function formatPurposeLabel(value) { return formatFriendlyLabel(value, { customer_response: "Resposta ao cliente", tool_generation: "Geracao de tools", decision_support: "Apoio a decisao" }, "Uso interno"); } function uniqueCount(...collections) { return new Set( collections.flatMap((items) => Array.isArray(items) ? items : []).filter(Boolean) ).size; } async function fetchPanelJson(url) { const response = await fetch(url, { credentials: "same-origin", headers: { Accept: "application/json" }, }); const body = await readJson(response); if (response.ok) { return { ok: true, body }; } const defaultMessage = response.status === 401 ? "Entre com uma sessao administrativa web para visualizar esta area." : body?.detail || "Nao foi possivel carregar os dados desta superficie."; return { ok: false, body, message: defaultMessage }; } async function readJson(response) { try { return await response.json(); } catch { return null; } } function setText(selector, value) { const target = document.querySelector(selector); if (target) { target.textContent = value; } } function formatNow() { return new Date().toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" }); } function escapeHtml(value) { return String(value || "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function formatDateTime(value) { const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) { return String(value || ""); } return parsed.toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", }); }