1280 lines
56 KiB
JavaScript
1280 lines
56 KiB
JavaScript
/**
|
||
* PAINEL DE ADMINISTRAÇÃO DE DADOS
|
||
*
|
||
* Interface para gerenciar o cache de dados, atualizar CSVs
|
||
* e monitorar o status do sistema de dados.
|
||
*/
|
||
|
||
// Variáveis globais para gerenciamento de configurações
|
||
let adminConfigManager = null;
|
||
let backupManager = null;
|
||
|
||
/**
|
||
* Inicializa o sistema de configurações administrativas
|
||
*/
|
||
async function initAdminConfig() {
|
||
try {
|
||
// Evitar duplicação: todos os managers são carregados via index.html
|
||
// Aguarde até que as classes e instâncias globais estejam disponíveis
|
||
const ready = await waitForManagers();
|
||
if (!ready) {
|
||
console.error('❌ Managers não carregados após tempo de espera.');
|
||
return false;
|
||
}
|
||
|
||
// Vincular instâncias globais, sem recriar
|
||
adminConfigManager = window.adminConfigManager || (typeof AdminConfigManager !== 'undefined' ? new AdminConfigManager() : null);
|
||
backupManager = window.backupManager || (typeof BackupManager !== 'undefined' ? new BackupManager() : null);
|
||
|
||
console.log('✅ Sistema de configurações administrativas inicializado');
|
||
return true;
|
||
} catch (error) {
|
||
console.error('❌ Erro ao inicializar sistema de configurações:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Aguarda até que os managers essenciais estejam disponíveis
|
||
* Retorna true quando AdminConfigManager, BackupManager e ToastManager existem
|
||
*/
|
||
async function waitForManagers(timeoutMs = 3000, intervalMs = 100) {
|
||
const start = Date.now();
|
||
return new Promise((resolve) => {
|
||
const check = () => {
|
||
const classesReady = (typeof AdminConfigManager !== 'undefined') && (typeof BackupManager !== 'undefined') && (typeof ToastManager !== 'undefined');
|
||
const instancesReady = !!(window.adminConfigManager || window.backupManager || window.toastManager);
|
||
if (classesReady || instancesReady) {
|
||
resolve(true);
|
||
return;
|
||
}
|
||
if (Date.now() - start >= timeoutMs) {
|
||
resolve(false);
|
||
return;
|
||
}
|
||
setTimeout(check, intervalMs);
|
||
};
|
||
check();
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Carrega um script dinamicamente
|
||
* @param {string} src - Caminho do script
|
||
* @returns {Promise} Promise que resolve quando o script for carregado
|
||
*/
|
||
function loadScript(src) {
|
||
return new Promise((resolve, reject) => {
|
||
const script = document.createElement('script');
|
||
script.src = src;
|
||
script.onload = resolve;
|
||
script.onerror = reject;
|
||
document.head.appendChild(script);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Abre o painel de administração de dados
|
||
*/
|
||
async function abrirPainelDados() {
|
||
console.log('🔧 Abrindo painel de administração de dados');
|
||
|
||
// Inicializar sistema de configurações se necessário
|
||
if (!adminConfigManager) {
|
||
const configLoaded = await initAdminConfig();
|
||
if (!configLoaded) {
|
||
alert('❌ Erro ao carregar sistema de configurações');
|
||
return;
|
||
}
|
||
}
|
||
|
||
const stats = window.dataManager.getCacheStats();
|
||
const metadata = window.dataManager.getMetadata();
|
||
const adminConfig = adminConfigManager ? adminConfigManager.getConfig() : null;
|
||
|
||
const modalHTML = `
|
||
<div class="modal active" id="modal-admin-dados" onclick="fecharPainelDados(event)">
|
||
<div class="modal-content" onclick="event.stopPropagation()" style="max-width: 800px; background:#0a0a0f;">
|
||
<div class="modal-header">
|
||
<div class="modal-title">🗄️ Administração de Dados</div>
|
||
<button class="close-btn" onclick="fecharPainelDados()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
|
||
<!-- Status Geral -->
|
||
<div class="card" style="background: var(--color-bg-1); margin-bottom: 20px;">
|
||
<h3 style="color: var(--color-primary); margin: 0 0 16px 0;">📊 Status do Sistema</h3>
|
||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px;">
|
||
<div>
|
||
<div style="font-size: 12px; color: var(--color-text-secondary);">Versão</div>
|
||
<div style="font-size: 18px; font-weight: bold;">${stats.version}</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size: 12px; color: var(--color-text-secondary);">Cache</div>
|
||
<div style="font-size: 18px; font-weight: bold; color: ${stats.hasCache ? 'var(--color-success)' : 'var(--color-error)'};">
|
||
${stats.hasCache ? '✅ Ativo' : '❌ Vazio'}
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size: 12px; color: var(--color-text-secondary);">Última Atualização</div>
|
||
<div style="font-size: 14px; font-weight: bold;">
|
||
${stats.lastUpdate ? new Date(stats.lastUpdate).toLocaleString('pt-BR') : 'Nunca'}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Ações Rápidas -->
|
||
<div class="card" style="background: var(--color-bg-1); margin-bottom: 20px;">
|
||
<h3 style="color: var(--color-primary); margin: 0 0 16px 0;">⚡ Ações Rápidas</h3>
|
||
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
|
||
<button class="btn btn-primary" onclick="atualizarTodosDados()">
|
||
🔄 Atualizar Todos os Dados
|
||
</button>
|
||
<button class="btn btn-success" onclick="abrirImportadorCSV()">
|
||
📥 Importar CSV
|
||
</button>
|
||
<button class="btn btn-warning" onclick="limparCacheCompleto()">
|
||
🗑️ Limpar Cache
|
||
</button>
|
||
<button class="btn btn-info" onclick="exportarDados()">
|
||
📤 Exportar Dados
|
||
</button>
|
||
<button class="btn btn-success" onclick="verificarIntegridade()">
|
||
🔍 Verificar Integridade
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Status por Tipo -->
|
||
<div class="card" style="background: var(--color-bg-1);">
|
||
<h3 style="color: var(--color-primary); margin: 0 0 16px 0;">📋 Status por Tipo de Perfil</h3>
|
||
<div class="table-container">
|
||
<table style="width: 100%; border-collapse: collapse;">
|
||
<thead>
|
||
<tr style="background: var(--color-bg-2);">
|
||
<th style="padding: 12px; text-align: left; border-bottom: 2px solid var(--color-border);">Tipo</th>
|
||
<th style="padding: 12px; text-align: center; border-bottom: 2px solid var(--color-border);">Status</th>
|
||
<th style="padding: 12px; text-align: center; border-bottom: 2px solid var(--color-border);">Itens</th>
|
||
<th style="padding: 12px; text-align: center; border-bottom: 2px solid var(--color-border);">Ações</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
${Object.entries(stats.types).map(([key, info]) => `
|
||
<tr style="border-bottom: 1px solid var(--color-border);">
|
||
<td style="padding: 12px;">
|
||
<div style="font-weight: bold;">${info.name}</div>
|
||
<div style="font-size: 12px; color: var(--color-text-secondary);">
|
||
Fonte: ${info.meta?.source || '—'}
|
||
</div>
|
||
<div style="font-size: 12px; color: var(--color-text-secondary);">
|
||
Atualizado: ${info.meta?.lastUpdate ? new Date(info.meta.lastUpdate).toLocaleString('pt-BR') : '—'}
|
||
</div>
|
||
<div style="font-size: 12px; color: var(--color-text-secondary);">
|
||
Documento (.md): ${info.meta?.docSource || '—'}
|
||
</div>
|
||
<div style="font-size: 12px; color: var(--color-text-secondary);">
|
||
Status do documento: ${info.meta?.docStatus || '—'}
|
||
</div>
|
||
<div style="margin-top: 6px; display: flex; gap: 8px; align-items: center;">
|
||
<input type="text" id="docfile-${key}" class="form-control" placeholder="conhecimento/.../arquivo.md ou BD/.../arquivo.md" value="${info.meta?.docSource || ''}" style="max-width: 420px;">
|
||
<button class="btn btn-sm btn-secondary" onclick="salvarDocFonte('${key}')">📄 Definir .md</button>
|
||
</div>
|
||
</td>
|
||
<td style="padding: 12px; text-align: center;">
|
||
<span class="badge badge-${info.cached ? 'success' : 'error'}">
|
||
${info.cached ? '✅ Cached' : '❌ Vazio'}
|
||
</span>
|
||
</td>
|
||
<td style="padding: 12px; text-align: center; font-weight: bold;">
|
||
${info.count.toLocaleString('pt-BR')}
|
||
</td>
|
||
<td style="padding: 12px; text-align: center;">
|
||
<button class="btn btn-sm btn-primary" onclick="atualizarTipoEspecifico('${key}')">
|
||
🔄 Atualizar
|
||
</button>
|
||
<button class="btn btn-sm btn-warning" onclick="limparTipoEspecifico('${key}')">
|
||
🗑️ Limpar
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
`).join('')}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Configurações Administrativas -->
|
||
<div class="card" style="background: var(--color-bg-1); margin-top: 20px;">
|
||
<h3 style="color: var(--color-primary); margin: 0 0 16px 0;">⚙️ Configurações Administrativas</h3>
|
||
|
||
<!-- Configurações Gerais -->
|
||
<div style="margin-bottom: 20px;">
|
||
<h4 style="color: var(--color-text-secondary); margin: 0 0 12px 0;">Aplicação</h4>
|
||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
|
||
<div>
|
||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--color-text-secondary);">Nome da Aplicação</label>
|
||
<input type="text" id="app-name" class="form-control" value="${adminConfig?.appName || 'SteelBase'}"
|
||
onchange="salvarConfiguracao('appName', this.value)" placeholder="Nome da aplicação">
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--color-text-secondary);">Versão</label>
|
||
<input type="text" id="app-version" class="form-control" value="${adminConfig?.version || '1.0.0'}"
|
||
onchange="salvarConfiguracao('version', this.value)" placeholder="Versão">
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--color-text-secondary);">Tema</label>
|
||
<select id="app-theme" class="form-control" onchange="salvarConfiguracao('themeDefault', this.value)">
|
||
<option value="escuro" ${adminConfig?.themeDefault === 'escuro' ? 'selected' : ''}>Escuro</option>
|
||
<option value="claro" ${adminConfig?.themeDefault === 'claro' ? 'selected' : ''}>Claro</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--color-text-secondary);">Idioma</label>
|
||
<select id="app-language" class="form-control" onchange="salvarConfiguracao('appLanguage', this.value)">
|
||
<option value="pt-BR" ${adminConfig?.appLanguage === 'pt-BR' ? 'selected' : ''}>Português (BR)</option>
|
||
<option value="en-US" ${adminConfig?.appLanguage === 'en-US' ? 'selected' : ''}>English (US)</option>
|
||
<option value="es-ES" ${adminConfig?.appLanguage === 'es-ES' ? 'selected' : ''}>Español (ES)</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--color-text-secondary);">Subtítulo</label>
|
||
<input type="text" id="app-subtitle" class="form-control" value="${adminConfig?.appSubtitle || ''}"
|
||
onchange="salvarConfiguracao('appSubtitle', this.value)" placeholder="Subtítulo">
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--color-text-secondary);">Rodapé</label>
|
||
<input type="text" id="app-footer" class="form-control" value="${adminConfig?.footerText || ''}"
|
||
onchange="salvarConfiguracao('footerText', this.value)" placeholder="Texto do rodapé">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Branding / Logotipo -->
|
||
<div style="margin-bottom: 20px;">
|
||
<h4 style="color: var(--color-text-secondary); margin: 0 0 12px 0;">Identidade Visual (Logotipo)</h4>
|
||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; align-items: start;">
|
||
<div>
|
||
<label style="display: block; margin-bottom: 6px; font-size: 12px; color: var(--color-text-secondary);">Upload do Logotipo (PNG/SVG)</label>
|
||
<input type="file" id="branding-logo-input" accept="image/png,image/svg+xml" class="form-control">
|
||
<small style="color: var(--color-text-secondary); display: block; margin-top: 6px;">Tamanho recomendado: 192x192 ou maior. SVG também é suportado.</small>
|
||
<div style="display:flex; gap:8px; margin-top:10px;">
|
||
<button class="btn btn-primary" onclick="salvarLogoBranding()">Salvar Logotipo</button>
|
||
<button class="btn btn-warning" onclick="removerLogoBranding()">Restaurar Padrão</button>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 6px; font-size: 12px; color: var(--color-text-secondary);">Preview</label>
|
||
<div id="branding-logo-preview" class="branding-logo-preview">${adminConfig?.branding?.logo ? `<img src="${adminConfig.branding.logo}" alt="Preview"/>` : '<div class="branding-logo-fallback">🏗️</div>'}</div>
|
||
<div style="margin-top:8px; display:flex; gap:8px; align-items:center;">
|
||
<label style="font-size: 12px; color: var(--color-text-secondary);">Usar em PWA</label>
|
||
<input type="checkbox" ${adminConfig?.branding?.useInPWA ? 'checked' : ''} onchange="salvarConfiguracao('branding.useInPWA', this.checked)">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Configurações de Backup -->
|
||
<div style="margin-bottom: 20px;">
|
||
<h4 style="color: var(--color-text-secondary); margin: 0 0 12px 0;">Backup Automático</h4>
|
||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
|
||
<div>
|
||
<label style="display: flex; align-items: center; gap: 8px;">
|
||
<input type="checkbox" id="auto-backup"
|
||
${adminConfig?.backup?.autoBackup ? 'checked' : ''}
|
||
onchange="salvarConfiguracao('backup.autoBackup', this.checked)">
|
||
<span style="font-size: 12px;">Ativar backup automático</span>
|
||
</label>
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--color-text-secondary);">Intervalo (minutos)</label>
|
||
<input type="number" id="backup-interval" class="form-control"
|
||
value="${adminConfig?.backup?.intervalMinutes || 60}" min="5" max="1440"
|
||
onchange="salvarConfiguracao('backup.intervalMinutes', parseInt(this.value))">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Ações de Configuração -->
|
||
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
|
||
<button class="btn btn-primary" onclick="criarBackupManual()">
|
||
💾 Criar Backup Manual
|
||
</button>
|
||
<button class="btn btn-info" onclick="abrirGerenciadorBackups()">
|
||
📂 Gerenciar Backups
|
||
</button>
|
||
<button class="btn btn-warning" onclick="exportarConfiguracoes()">
|
||
📤 Exportar Configurações
|
||
</button>
|
||
<button class="btn btn-success" onclick="importarConfiguracoes()">
|
||
📥 Importar Configurações
|
||
</button>
|
||
<button class="btn btn-danger" onclick="resetarConfiguracoes()">
|
||
🔄 Resetar Configurações
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Preferências e Modo Padrão -->
|
||
<div class="card" style="background: var(--color-bg-1); margin-top: 20px;">
|
||
<h3 style="color: var(--color-primary); margin: 0 0 16px 0;">🧩 Preferências</h3>
|
||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px;">
|
||
<div>
|
||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--color-text-secondary);">Modo Padrão</label>
|
||
<select id="default-mode" class="form-control" onchange="salvarConfiguracao('modeDefault', this.value)">
|
||
<option value="simples" ${adminConfig?.modeDefault === 'simples' ? 'selected' : ''}>Simples</option>
|
||
<option value="expert" ${adminConfig?.modeDefault === 'expert' || adminConfig?.modeDefault === 'experto' ? 'selected' : ''}>Expert</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--color-text-secondary);">Alternar Expert</label>
|
||
<button class="btn btn-secondary" id="adminExpertToggle" onclick="toggleExpertMode()">
|
||
${document.documentElement.classList.contains('expert-mode') ? '🔬 Expert Ativo' : '🎯 Alternar Expert'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Ferramentas Visíveis (Modo Simples) -->
|
||
<div class="card" style="background: var(--color-bg-1); margin-top: 20px;">
|
||
<h3 style="color: var(--color-primary); margin: 0 0 16px 0;">🔧 Ferramentas Visíveis (Modo Simples)</h3>
|
||
<p style="color: var(--color-text-secondary); margin-bottom: 12px;">No Modo Expert, todas as ferramentas ficam visíveis.</p>
|
||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 8px;">
|
||
${Object.keys(adminConfig?.toolsVisibility || {}).map(tool => `
|
||
<label style="display:flex; align-items:center; gap:8px; font-size:14px;">
|
||
<input type="checkbox" ${adminConfig.toolsVisibility[tool] ? 'checked' : ''} onchange="salvarConfiguracao('toolsVisibility.${tool}', this.checked)">
|
||
<span>${tool}</span>
|
||
</label>
|
||
`).join('')}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Log de Atividades -->
|
||
<div class="card" style="background: var(--color-bg-1); margin-top: 20px;">
|
||
<h3 style="color: var(--color-primary); margin: 0 0 16px 0;">📝 Log de Atividades</h3>
|
||
<div id="admin-log" style="background: #000; color: #0f0; padding: 16px; border-radius: 8px; font-family: monospace; font-size: 12px; max-height: 200px; overflow-y: auto;">
|
||
${metadata ? `
|
||
<div>✅ Última atualização: ${new Date(metadata.lastUpdate).toLocaleString('pt-BR')}</div>
|
||
<div>📊 Tipos carregados: ${metadata.successCount}/${metadata.totalTypes}</div>
|
||
${metadata.errorCount > 0 ? `<div style="color: #f00;">❌ Erros: ${metadata.errorCount}</div>` : ''}
|
||
` : '<div>⚠️ Nenhum log disponível</div>'}
|
||
${adminConfig ? `<div>⚙️ Configurações: ${adminConfig.version}</div>` : ''}
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-secondary" onclick="fecharPainelDados()">Fechar</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// Remover modal existente se houver
|
||
const existingModal = document.getElementById('modal-admin-dados');
|
||
if (existingModal) {
|
||
existingModal.remove();
|
||
}
|
||
|
||
// Adicionar estilos CSS para os campos de formulário
|
||
addAdminPanelStyles();
|
||
|
||
// Adicionar novo modal
|
||
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
||
}
|
||
|
||
/**
|
||
* Fecha o painel de administração
|
||
*/
|
||
function fecharPainelDados(event) {
|
||
if (event && event.target !== event.currentTarget) return;
|
||
|
||
const modal = document.getElementById('modal-admin-dados');
|
||
if (modal) {
|
||
modal.remove();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Atualiza todos os dados
|
||
*/
|
||
async function atualizarTodosDados() {
|
||
if (!confirm('🔄 Deseja atualizar todos os dados? Isso pode levar alguns segundos.')) {
|
||
return;
|
||
}
|
||
|
||
const logEl = document.getElementById('admin-log');
|
||
if (logEl) {
|
||
logEl.innerHTML = '<div>🔄 Iniciando atualização completa...</div>';
|
||
}
|
||
|
||
try {
|
||
const results = await window.dataManager.updateAllData();
|
||
|
||
if (logEl) {
|
||
logEl.innerHTML = `
|
||
<div>✅ Atualização completa finalizada!</div>
|
||
<div>📊 Sucessos: ${results.success.length}</div>
|
||
${results.errors.length > 0 ? `<div style="color: #f00;">❌ Erros: ${results.errors.length}</div>` : ''}
|
||
<div style="margin-top: 8px;">Detalhes:</div>
|
||
${results.success.map(s => `<div>✅ ${s.name}: ${s.count} itens</div>`).join('')}
|
||
${results.errors.map(e => `<div style="color: #f00;">❌ ${e.name}: ${e.error}</div>`).join('')}
|
||
`;
|
||
}
|
||
|
||
// Recarregar painel
|
||
setTimeout(() => {
|
||
fecharPainelDados();
|
||
abrirPainelDados();
|
||
}, 2000);
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao atualizar dados:', error);
|
||
if (logEl) {
|
||
logEl.innerHTML = `<div style="color: #f00;">❌ Erro: ${error.message}</div>`;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Limpa todo o cache
|
||
*/
|
||
function limparCacheCompleto() {
|
||
if (!confirm('🗑️ Deseja limpar todo o cache? Os dados serão recarregados na próxima vez.')) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
window.dataManager.clearCache();
|
||
alert('✅ Cache limpo com sucesso!');
|
||
|
||
// Recarregar painel
|
||
fecharPainelDados();
|
||
abrirPainelDados();
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao limpar cache:', error);
|
||
alert('❌ Erro ao limpar cache: ' + error.message);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Atualiza um tipo específico de dados
|
||
*/
|
||
async function atualizarTipoEspecifico(tipo) {
|
||
if (!confirm(`🔄 Deseja atualizar os dados de ${tipo}?`)) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// Ler docSource do campo (se preenchido)
|
||
const input = document.getElementById(`docfile-${tipo}`);
|
||
let docSource = input ? input.value.trim() : null;
|
||
if (docSource) {
|
||
docSource = window.dataManager.normalizeDocSource(docSource);
|
||
}
|
||
// Atualizar via DataManager (com registro de metadata por tipo)
|
||
const result = await window.dataManager.updateTypeData(tipo, { docSource });
|
||
const config = window.dataManager.csvConfigs[tipo];
|
||
|
||
const docMsg = result.docSource ? `\n📄 Documento: ${result.docSource} (${result.docStatus || '—'})` : '';
|
||
alert(`✅ ${config.displayName} atualizado com sucesso! ${result.count} itens carregados.${docMsg}`);
|
||
|
||
// Recarregar painel
|
||
fecharPainelDados();
|
||
abrirPainelDados();
|
||
|
||
} catch (error) {
|
||
console.error(`❌ Erro ao atualizar ${tipo}:`, error);
|
||
alert(`❌ Erro ao atualizar ${tipo}: ` + error.message);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Salva a fonte de documento (.md) para um tipo
|
||
*/
|
||
function salvarDocFonte(tipo) {
|
||
try {
|
||
const input = document.getElementById(`docfile-${tipo}`);
|
||
if (!input) return;
|
||
const value = input.value.trim();
|
||
const normalized = window.dataManager.normalizeDocSource(value);
|
||
// Atualizar metadados mantendo os demais campos
|
||
const prev = window.dataManager.getTypeMetadata(tipo) || {};
|
||
const updated = { ...prev, docSource: normalized || null };
|
||
// Tentar validar imediatamente para refletir status
|
||
if (normalized) {
|
||
fetch(normalized, { cache: 'no-cache' })
|
||
.then(resp => {
|
||
updated.docStatus = resp.ok ? 'ok' : `não encontrado (HTTP ${resp.status})`;
|
||
window.dataManager.setTypeMetadata(tipo, updated);
|
||
alert('📄 Documento fonte definido para ' + tipo + `\nArquivo: ${normalized}\nStatus: ${updated.docStatus}`);
|
||
// Atualizar UI do painel
|
||
fecharPainelDados();
|
||
abrirPainelDados();
|
||
})
|
||
.catch(err => {
|
||
updated.docStatus = 'erro ao carregar';
|
||
window.dataManager.setTypeMetadata(tipo, updated);
|
||
alert('⚠️ Não foi possível validar o documento. Caminho salvo como: ' + normalized);
|
||
fecharPainelDados();
|
||
abrirPainelDados();
|
||
});
|
||
} else {
|
||
window.dataManager.setTypeMetadata(tipo, updated);
|
||
alert('📄 Documento fonte definido para ' + tipo);
|
||
fecharPainelDados();
|
||
abrirPainelDados();
|
||
}
|
||
} catch (e) {
|
||
console.error('❌ Erro ao salvar doc fonte:', e);
|
||
alert('❌ Erro ao salvar doc fonte: ' + e.message);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Limpa cache de um tipo específico
|
||
*/
|
||
function limparTipoEspecifico(tipo) {
|
||
if (!confirm(`🗑️ Deseja limpar o cache de ${tipo}?`)) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
localStorage.removeItem(`acoCalcPro_cache_${tipo}`);
|
||
alert(`✅ Cache de ${tipo} limpo com sucesso!`);
|
||
|
||
// Recarregar painel
|
||
fecharPainelDados();
|
||
abrirPainelDados();
|
||
|
||
} catch (error) {
|
||
console.error(`❌ Erro ao limpar cache de ${tipo}:`, error);
|
||
alert(`❌ Erro ao limpar cache: ` + error.message);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Exporta dados para JSON
|
||
*/
|
||
function exportarDados() {
|
||
try {
|
||
const stats = window.dataManager.getCacheStats();
|
||
const exportData = {
|
||
metadata: window.dataManager.getMetadata(),
|
||
stats: stats,
|
||
exportedAt: new Date().toISOString()
|
||
};
|
||
|
||
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = `aco-calc-pro-data-export-${Date.now()}.json`;
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
|
||
alert('✅ Dados exportados com sucesso!');
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao exportar dados:', error);
|
||
alert('❌ Erro ao exportar dados: ' + error.message);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Verifica integridade dos dados
|
||
*/
|
||
async function verificarIntegridade() {
|
||
const logEl = document.getElementById('admin-log');
|
||
if (logEl) {
|
||
logEl.innerHTML = '<div>🔍 Verificando integridade dos dados...</div>';
|
||
}
|
||
|
||
try {
|
||
const stats = window.dataManager.getCacheStats();
|
||
const issues = [];
|
||
|
||
// Verificar cada tipo
|
||
for (const [key, info] of Object.entries(stats.types)) {
|
||
if (!info.cached) {
|
||
issues.push(`❌ ${info.name}: Sem cache`);
|
||
} else if (info.count === 0) {
|
||
issues.push(`⚠️ ${info.name}: Cache vazio`);
|
||
}
|
||
}
|
||
|
||
if (logEl) {
|
||
if (issues.length === 0) {
|
||
logEl.innerHTML = '<div>✅ Todos os dados estão íntegros!</div>';
|
||
} else {
|
||
logEl.innerHTML = `
|
||
<div>⚠️ Problemas encontrados:</div>
|
||
${issues.map(i => `<div>${i}</div>`).join('')}
|
||
`;
|
||
}
|
||
}
|
||
|
||
if (issues.length === 0) {
|
||
alert('✅ Todos os dados estão íntegros!');
|
||
} else {
|
||
alert(`⚠️ ${issues.length} problema(s) encontrado(s). Verifique o log.`);
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao verificar integridade:', error);
|
||
if (logEl) {
|
||
logEl.innerHTML = `<div style="color: #f00;">❌ Erro: ${error.message}</div>`;
|
||
}
|
||
}
|
||
}
|
||
|
||
console.log('✅ Admin Panel carregado');
|
||
|
||
/**
|
||
* Adiciona estilos CSS para o painel de administração
|
||
*/
|
||
function addAdminPanelStyles() {
|
||
// Verificar se os estilos já existem
|
||
if (document.querySelector('#admin-panel-styles')) {
|
||
return;
|
||
}
|
||
|
||
const styles = document.createElement('style');
|
||
styles.id = 'admin-panel-styles';
|
||
styles.textContent = `
|
||
/* Estilos para campos de formulário do painel admin */
|
||
.form-control {
|
||
width: 100%;
|
||
padding: 8px 12px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 6px;
|
||
background: var(--color-bg-2);
|
||
color: var(--color-text);
|
||
font-size: 14px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.form-control:focus {
|
||
outline: none;
|
||
border-color: var(--color-primary);
|
||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||
}
|
||
|
||
.form-control:hover {
|
||
border-color: var(--color-primary-light);
|
||
}
|
||
|
||
/* Estilos para checkboxes */
|
||
input[type="checkbox"] {
|
||
width: 16px;
|
||
height: 16px;
|
||
accent-color: var(--color-primary);
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* Estilos para badges */
|
||
.badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 4px 8px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.badge-success {
|
||
background: rgba(16, 185, 129, 0.1);
|
||
color: #10b981;
|
||
border: 1px solid rgba(16, 185, 129, 0.2);
|
||
}
|
||
|
||
.badge-error {
|
||
background: rgba(239, 68, 68, 0.1);
|
||
color: #ef4444;
|
||
border: 1px solid rgba(239, 68, 68, 0.2);
|
||
}
|
||
|
||
.badge-primary {
|
||
background: rgba(59, 130, 246, 0.1);
|
||
color: #3b82f6;
|
||
border: 1px solid rgba(59, 130, 246, 0.2);
|
||
}
|
||
|
||
.badge-secondary {
|
||
background: rgba(107, 114, 128, 0.1);
|
||
color: #6b7280;
|
||
border: 1px solid rgba(107, 114, 128, 0.2);
|
||
}
|
||
|
||
/* Estilos para botões pequenos */
|
||
.btn-sm {
|
||
padding: 6px 12px;
|
||
font-size: 12px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
/* Estilos para containers de tabelas */
|
||
.table-container {
|
||
background: var(--color-bg-2);
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
border: 1px solid var(--color-border);
|
||
}
|
||
/* Cabeçalhos de tabela no painel Admin: alto contraste */
|
||
.modal-admin .table-container th {
|
||
background: var(--color-bg-2);
|
||
color: var(--color-gray-200);
|
||
font-weight: 700;
|
||
border-bottom: 2px solid var(--color-border);
|
||
text-align: left;
|
||
padding: 10px;
|
||
}
|
||
.modal-admin .table-container th sup {
|
||
color: inherit;
|
||
font-weight: 700;
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Animações para inputs */
|
||
@keyframes input-focus {
|
||
from {
|
||
transform: scale(0.98);
|
||
opacity: 0.8;
|
||
}
|
||
to {
|
||
transform: scale(1);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.form-control:focus {
|
||
animation: input-focus 0.2s ease-out;
|
||
}
|
||
|
||
/* Responsividade para o painel */
|
||
@media (max-width: 768px) {
|
||
.modal-content {
|
||
margin: 10px;
|
||
max-height: 90vh;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.form-control {
|
||
font-size: 16px; /* Previne zoom no iOS */
|
||
}
|
||
}
|
||
|
||
/* Branding preview */
|
||
.branding-logo-preview {
|
||
width: 180px;
|
||
height: 180px;
|
||
border: 1px dashed var(--color-border);
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: var(--color-bg-2);
|
||
}
|
||
.branding-logo-preview img {
|
||
max-width: 100%;
|
||
max-height: 100%;
|
||
object-fit: contain;
|
||
border-radius: 8px;
|
||
}
|
||
.branding-logo-fallback {
|
||
font-size: 64px;
|
||
}
|
||
`;
|
||
|
||
document.head.appendChild(styles);
|
||
}
|
||
|
||
|
||
// ========================================
|
||
// ATALHO DE TECLADO
|
||
// ========================================
|
||
|
||
/**
|
||
* Atalho de teclado: Ctrl + Shift + D
|
||
*/
|
||
document.addEventListener('keydown', function(e) {
|
||
if (e.ctrlKey && e.shiftKey && e.key === 'D') {
|
||
e.preventDefault();
|
||
abrirPainelDados();
|
||
console.log('🗄️ Painel de dados aberto via atalho de teclado');
|
||
}
|
||
});
|
||
|
||
console.log('💡 Dica: Pressione Ctrl + Shift + D para abrir o painel de administração de dados');
|
||
|
||
/**
|
||
* Salva uma configuração específica
|
||
* @param {string} key - Chave da configuração (ex: 'app.name')
|
||
* @param {*} value - Valor a ser salvo
|
||
*/
|
||
async function salvarConfiguracao(key, value) {
|
||
try {
|
||
if (!adminConfigManager) {
|
||
console.error('❌ AdminConfigManager não inicializado');
|
||
return;
|
||
}
|
||
|
||
// Atualizar configuração
|
||
adminConfigManager.updateConfig(key, value);
|
||
|
||
// Mostrar feedback visual se o ToastManager estiver disponível
|
||
if (window.toastManager) {
|
||
window.toastManager.success(`Configuração "${key}" salva com sucesso!`);
|
||
} else {
|
||
console.log(`✅ Configuração "${key}" salva com sucesso`);
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao salvar configuração:', error);
|
||
if (window.toastManager) {
|
||
window.toastManager.error(`Erro ao salvar configuração: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Salva o logotipo enviado no campo de branding
|
||
*/
|
||
async function salvarLogoBranding() {
|
||
try {
|
||
const input = document.getElementById('branding-logo-input');
|
||
if (!input || !input.files || !input.files[0]) {
|
||
alert('Selecione um arquivo de imagem (PNG ou SVG).');
|
||
return;
|
||
}
|
||
const file = input.files[0];
|
||
const reader = new FileReader();
|
||
reader.onload = async (e) => {
|
||
const dataUrl = e.target.result;
|
||
// Persistir em admin config
|
||
await salvarConfiguracao('branding.logo', dataUrl);
|
||
// Atualizar preview
|
||
const preview = document.getElementById('branding-logo-preview');
|
||
if (preview) {
|
||
preview.innerHTML = `<img src="${dataUrl}" alt="Preview">`;
|
||
}
|
||
// Aplicar imediatamente
|
||
if (window.applyAdminConfig) window.applyAdminConfig();
|
||
if (window.toastManager) window.toastManager.success('Logotipo salvo e aplicado!');
|
||
};
|
||
reader.readAsDataURL(file);
|
||
} catch (error) {
|
||
console.error('❌ Erro ao salvar logotipo:', error);
|
||
if (window.toastManager) {
|
||
window.toastManager.error('Erro ao salvar logotipo: ' + error.message);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Remove o logotipo personalizado e restaura o padrão
|
||
*/
|
||
function removerLogoBranding() {
|
||
try {
|
||
salvarConfiguracao('branding.logo', null);
|
||
const preview = document.getElementById('branding-logo-preview');
|
||
if (preview) {
|
||
preview.innerHTML = '<div class="branding-logo-fallback">🏗️</div>';
|
||
}
|
||
if (window.applyAdminConfig) window.applyAdminConfig();
|
||
if (window.toastManager) window.toastManager.info('Logotipo restaurado para o padrão');
|
||
} catch (error) {
|
||
console.error('❌ Erro ao remover logotipo:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Aplica um tema específico
|
||
* @param {string} themeName - Nome do tema (dark, light, auto)
|
||
*/
|
||
function aplicarTema(themeName) {
|
||
try {
|
||
if (!adminConfigManager) {
|
||
console.error('❌ AdminConfigManager não inicializado');
|
||
return;
|
||
}
|
||
|
||
// Aplicar tema usando o AdminConfigManager
|
||
adminConfigManager.applyTheme(themeName);
|
||
|
||
console.log(`✅ Tema "${themeName}" aplicado com sucesso`);
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao aplicar tema:', error);
|
||
if (window.toastManager) {
|
||
window.toastManager.error(`Erro ao aplicar tema: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Cria um backup manual
|
||
*/
|
||
async function criarBackupManual() {
|
||
try {
|
||
if (!backupManager) {
|
||
console.error('❌ BackupManager não inicializado');
|
||
if (window.toastManager) {
|
||
window.toastManager.error('Sistema de backup não inicializado');
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Mostrar loading
|
||
const loadingToast = window.toastManager ? window.toastManager.loading('Criando backup...') : null;
|
||
|
||
// Criar backup
|
||
const backup = await backupManager.createBackup();
|
||
|
||
// Remover loading
|
||
if (loadingToast && window.toastManager) {
|
||
window.toastManager.removeToast(loadingToast);
|
||
}
|
||
|
||
// Mostrar sucesso
|
||
if (window.toastManager) {
|
||
window.toastManager.success(`Backup criado com sucesso! ID: ${backup.id}`);
|
||
}
|
||
|
||
console.log('✅ Backup manual criado:', backup);
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao criar backup:', error);
|
||
if (window.toastManager) {
|
||
window.toastManager.error(`Erro ao criar backup: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Abre o gerenciador de backups
|
||
*/
|
||
function abrirGerenciadorBackups() {
|
||
try {
|
||
if (!backupManager) {
|
||
console.error('❌ BackupManager não inicializado');
|
||
if (window.toastManager) {
|
||
window.toastManager.error('Sistema de backup não inicializado');
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Obter lista de backups
|
||
const backups = backupManager.getBackups();
|
||
|
||
if (backups.length === 0) {
|
||
if (window.toastManager) {
|
||
window.toastManager.info('Nenhum backup disponível');
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Criar modal de gerenciamento
|
||
const modalHTML = `
|
||
<div class="modal active" id="modal-backup-manager" onclick="fecharGerenciadorBackups(event)">
|
||
<div class="modal-content" onclick="event.stopPropagation()" style="max-width: 600px;">
|
||
<div class="modal-header">
|
||
<div class="modal-title">📂 Gerenciador de Backups</div>
|
||
<button class="close-btn" onclick="fecharGerenciadorBackups()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="table-container">
|
||
<table style="width: 100%; border-collapse: collapse;">
|
||
<thead>
|
||
<tr style="background: var(--color-bg-2);">
|
||
<th style="padding: 12px; text-align: left; border-bottom: 2px solid var(--color-border);">Data</th>
|
||
<th style="padding: 12px; text-align: center; border-bottom: 2px solid var(--color-border);">Tipo</th>
|
||
<th style="padding: 12px; text-align: center; border-bottom: 2px solid var(--color-border);">Tamanho</th>
|
||
<th style="padding: 12px; text-align: center; border-bottom: 2px solid var(--color-border);">Ações</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
${backups.map(backup => `
|
||
<tr style="border-bottom: 1px solid var(--color-border);">
|
||
<td style="padding: 12px;">${(() => { const d = Number(backup.createdAt ?? backup.timestamp); return isNaN(d) ? '—' : new Date(d).toLocaleString('pt-BR'); })()}</td>
|
||
<td style="padding: 12px; text-align: center;">
|
||
<span class="badge badge-${backup.type === 'manual' ? 'primary' : 'secondary'}">
|
||
${backup.type === 'manual' ? 'MANUAL' : 'AUTOMÁTICO'}
|
||
</span>
|
||
</td>
|
||
<td style="padding: 12px; text-align: center;">${(() => { const sb = Number(backup.sizeBytes); if (backup.size) return backup.size; return isNaN(sb) ? '—' : (window.backupManager ? window.backupManager.formatBytes(sb) : `${sb} B`); })()}</td>
|
||
<td style="padding: 12px; text-align: center;">
|
||
<button class="btn btn-sm btn-success" onclick="restaurarBackup('${backup.id || backup.timestamp}')">
|
||
↻ Restaurar
|
||
</button>
|
||
<button class="btn btn-sm btn-danger" onclick="removerBackup('${backup.id || backup.timestamp}')">
|
||
🗑️ Remover
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
`).join('')}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-secondary" onclick="fecharGerenciadorBackups()">Fechar</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// Adicionar modal ao body
|
||
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao abrir gerenciador de backups:', error);
|
||
if (window.toastManager) {
|
||
window.toastManager.error(`Erro ao abrir gerenciador: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Fecha o gerenciador de backups
|
||
*/
|
||
function fecharGerenciadorBackups(event) {
|
||
if (event && event.target !== event.currentTarget) return;
|
||
|
||
const modal = document.getElementById('modal-backup-manager');
|
||
if (modal) {
|
||
modal.remove();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Restaura um backup específico
|
||
* @param {string} backupId - ID do backup
|
||
*/
|
||
async function restaurarBackup(backupId) {
|
||
try {
|
||
if (!backupManager) {
|
||
console.error('❌ BackupManager não inicializado');
|
||
return;
|
||
}
|
||
|
||
const resumo = backupManager.getBackup(backupId);
|
||
const dataStr = resumo ? new Date(resumo.createdAt || resumo.timestamp).toLocaleString('pt-BR') : 'desconhecida';
|
||
if (!confirm(`⚠️ Tem certeza que deseja restaurar o backup de ${dataStr}?\n\nIsso substituirá todas as configurações atuais.`)) {
|
||
return;
|
||
}
|
||
|
||
// Mostrar loading
|
||
const loadingToast = window.toastManager ? window.toastManager.loading('Restaurando backup...') : null;
|
||
|
||
// Restaurar backup
|
||
await backupManager.restoreBackup(backupId);
|
||
|
||
// Remover loading
|
||
if (loadingToast && window.toastManager) {
|
||
window.toastManager.removeToast(loadingToast);
|
||
}
|
||
|
||
// Mostrar sucesso
|
||
if (window.toastManager) {
|
||
window.toastManager.success('Backup restaurado com sucesso!');
|
||
}
|
||
|
||
// Recarregar painel
|
||
fecharGerenciadorBackups();
|
||
fecharPainelDados();
|
||
setTimeout(() => abrirPainelDados(), 500);
|
||
|
||
console.log('✅ Backup restaurado:', backupId);
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao restaurar backup:', error);
|
||
if (window.toastManager) {
|
||
window.toastManager.error(`Erro ao restaurar backup: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Remove um backup específico
|
||
* @param {string} backupId - ID do backup
|
||
*/
|
||
function removerBackup(backupId) {
|
||
try {
|
||
if (!backupManager) {
|
||
console.error('❌ BackupManager não inicializado');
|
||
return;
|
||
}
|
||
|
||
if (!confirm('⚠️ Tem certeza que deseja remover este backup?')) {
|
||
return;
|
||
}
|
||
|
||
// Remover backup
|
||
backupManager.removeBackup(backupId);
|
||
|
||
// Mostrar sucesso
|
||
if (window.toastManager) {
|
||
window.toastManager.success('Backup removido com sucesso!');
|
||
}
|
||
|
||
// Recarregar gerenciador
|
||
fecharGerenciadorBackups();
|
||
setTimeout(() => abrirGerenciadorBackups(), 100);
|
||
|
||
console.log('✅ Backup removido:', backupId);
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao remover backup:', error);
|
||
if (window.toastManager) {
|
||
window.toastManager.error(`Erro ao remover backup: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Exporta as configurações atuais
|
||
*/
|
||
function exportarConfiguracoes() {
|
||
try {
|
||
if (!adminConfigManager) {
|
||
console.error('❌ AdminConfigManager não inicializado');
|
||
if (window.toastManager) {
|
||
window.toastManager.error('Sistema de configurações não inicializado');
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Obter configurações atuais
|
||
const config = adminConfigManager.getConfig();
|
||
|
||
// Criar blob e download
|
||
const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = `aco-calc-pro-config-${Date.now()}.json`;
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
|
||
// Mostrar sucesso
|
||
if (window.toastManager) {
|
||
window.toastManager.success('Configurações exportadas com sucesso!');
|
||
}
|
||
|
||
console.log('✅ Configurações exportadas');
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao exportar configurações:', error);
|
||
if (window.toastManager) {
|
||
window.toastManager.error(`Erro ao exportar configurações: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Importa configurações de um arquivo
|
||
*/
|
||
function importarConfiguracoes() {
|
||
try {
|
||
if (!adminConfigManager) {
|
||
console.error('❌ AdminConfigManager não inicializado');
|
||
if (window.toastManager) {
|
||
window.toastManager.error('Sistema de configurações não inicializado');
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Criar input file
|
||
const input = document.createElement('input');
|
||
input.type = 'file';
|
||
input.accept = '.json';
|
||
|
||
input.onchange = async (e) => {
|
||
const file = e.target.files[0];
|
||
if (!file) return;
|
||
|
||
try {
|
||
// Ler arquivo
|
||
const text = await file.text();
|
||
const config = JSON.parse(text);
|
||
|
||
// Confirmar importação
|
||
if (!confirm('⚠️ Tem certeza que deseja importar estas configurações?\n\nIsso substituirá as configurações atuais.')) {
|
||
return;
|
||
}
|
||
|
||
// Importar configurações
|
||
await adminConfigManager.setConfig(config);
|
||
|
||
// Mostrar sucesso
|
||
if (window.toastManager) {
|
||
window.toastManager.success('Configurações importadas com sucesso!');
|
||
}
|
||
|
||
// Recarregar painel
|
||
fecharPainelDados();
|
||
setTimeout(() => abrirPainelDados(), 500);
|
||
|
||
console.log('✅ Configurações importadas');
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao importar configurações:', error);
|
||
if (window.toastManager) {
|
||
window.toastManager.error(`Erro ao importar configurações: ${error.message}`);
|
||
}
|
||
}
|
||
};
|
||
|
||
input.click();
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao importar configurações:', error);
|
||
if (window.toastManager) {
|
||
window.toastManager.error(`Erro ao importar configurações: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Reseta as configurações para os valores padrão
|
||
*/
|
||
function resetarConfiguracoes() {
|
||
try {
|
||
if (!adminConfigManager) {
|
||
console.error('❌ AdminConfigManager não inicializado');
|
||
if (window.toastManager) {
|
||
window.toastManager.error('Sistema de configurações não inicializado');
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (!confirm('⚠️ Tem certeza que deseja resetar todas as configurações?\n\nIsso irá restaurar os valores padrão.')) {
|
||
return;
|
||
}
|
||
|
||
// Resetar configurações
|
||
adminConfigManager.resetConfig();
|
||
|
||
// Mostrar sucesso
|
||
if (window.toastManager) {
|
||
window.toastManager.success('Configurações resetadas com sucesso!');
|
||
}
|
||
|
||
// Recarregar painel
|
||
fecharPainelDados();
|
||
setTimeout(() => abrirPainelDados(), 500);
|
||
|
||
console.log('✅ Configurações resetadas');
|
||
|
||
} catch (error) {
|
||
console.error('❌ Erro ao resetar configurações:', error);
|
||
if (window.toastManager) {
|
||
window.toastManager.error(`Erro ao resetar configurações: ${error.message}`);
|
||
}
|
||
}
|
||
}
|