Initial commit SteelBase - Oficiais e Funcionando
This commit is contained in:
643
js/core/admin-config-manager.js
Normal file
643
js/core/admin-config-manager.js
Normal file
@@ -0,0 +1,643 @@
|
||||
/**
|
||||
* AdminConfigManager - Gerenciador de Configurações Administrativas
|
||||
* Responsável por persistir e gerenciar todas as configurações do painel administrativo
|
||||
*/
|
||||
class AdminConfigManager {
|
||||
constructor() {
|
||||
this.configKey = 'acoCalcPro_admin_config';
|
||||
this.version = '1.0.0';
|
||||
this.defaultConfig = {
|
||||
// Informações básicas da aplicação
|
||||
appName: 'SteelBase',
|
||||
appSubtitle: 'Plataforma Técnica com Base de Dados de Materiais Brasileiros',
|
||||
footerText: '© 2025 SteelBase v7.5 PROFESSIONAL EDITION',
|
||||
// Branding (logotipo e identidade)
|
||||
branding: {
|
||||
logo: null, // DataURL (PNG/SVG) do logotipo
|
||||
logoAlt: 'Logo',
|
||||
themeColor: '#21808d',
|
||||
backgroundColor: '#fcfcf9',
|
||||
useInPWA: true,
|
||||
useInReports: true,
|
||||
useInFooter: true
|
||||
},
|
||||
|
||||
// Configurações de tema e modo
|
||||
themeDefault: 'escuro',
|
||||
modeDefault: 'simples',
|
||||
|
||||
// Visibilidade de ferramentas (baseado nas ferramentas existentes)
|
||||
toolsVisibility: {
|
||||
'cev': true,
|
||||
'seletor': true,
|
||||
'equivalencias': true,
|
||||
'comparativo': true,
|
||||
'parafusos': true,
|
||||
'layout': true,
|
||||
'cantoneiras': true,
|
||||
'perfis-i': true,
|
||||
'perfis-u': true,
|
||||
'perfis-w': true,
|
||||
'perfis-hp': true,
|
||||
'barras-chatas': true,
|
||||
'barras-redondas': true,
|
||||
'barras-quadradas': true,
|
||||
'barras-hexagonais': true,
|
||||
'tubos-circulares': true,
|
||||
'tubos-quadrados': true,
|
||||
'tubos-retangulares': true,
|
||||
'chapas': true,
|
||||
'solda': true,
|
||||
'tintas': true,
|
||||
'fixadores': true,
|
||||
'calculadoras': true
|
||||
},
|
||||
|
||||
// Configurações de atualização e backup
|
||||
dataRefreshInterval: 24, // horas
|
||||
autoBackup: true,
|
||||
backupInterval: 7, // dias
|
||||
lastBackup: null,
|
||||
|
||||
// Estado da UI
|
||||
uiState: {
|
||||
activeSidebarTab: 0,
|
||||
collapsedSections: [],
|
||||
appliedFilters: {},
|
||||
tableSort: {},
|
||||
columnWidths: {},
|
||||
scrollPositions: {}
|
||||
},
|
||||
|
||||
// Versionamento
|
||||
version: this.version
|
||||
};
|
||||
|
||||
// Inicializar com migração se necessário
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inicializa o gerenciador, executando migrações se necessário
|
||||
*/
|
||||
initialize() {
|
||||
try {
|
||||
const savedVersion = this.getVersion();
|
||||
if (savedVersion !== this.version) {
|
||||
console.log(`🔄 Migrando config de ${savedVersion} para ${this.version}`);
|
||||
this.migrateConfig(savedVersion);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Erro ao inicializar AdminConfigManager:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migra configurações de versões antigas
|
||||
* @param {string} fromVersion - Versão de origem
|
||||
*/
|
||||
migrateConfig(fromVersion) {
|
||||
try {
|
||||
console.log(`📦 Iniciando migração de ${fromVersion} para ${this.version}`);
|
||||
|
||||
const currentConfig = this.getConfig();
|
||||
let migratedConfig = { ...currentConfig };
|
||||
|
||||
// Migração de versões muito antigas (0.x.x)
|
||||
if (fromVersion.startsWith('0.')) {
|
||||
console.log('📋 Migrando de versão 0.x.x - aplicando estrutura padrão');
|
||||
|
||||
// Garantir que todas as chaves padrão existam
|
||||
migratedConfig = {
|
||||
...this.defaultConfig,
|
||||
...migratedConfig,
|
||||
version: this.version,
|
||||
lastModified: Date.now()
|
||||
};
|
||||
|
||||
// Migrar configurações antigas se existirem
|
||||
if (migratedConfig.appTitle && !migratedConfig.appName) {
|
||||
migratedConfig.appName = migratedConfig.appTitle;
|
||||
delete migratedConfig.appTitle;
|
||||
}
|
||||
|
||||
if (migratedConfig.defaultTheme && !migratedConfig.themeDefault) {
|
||||
migratedConfig.themeDefault = migratedConfig.defaultTheme;
|
||||
delete migratedConfig.defaultTheme;
|
||||
}
|
||||
}
|
||||
|
||||
// Migração de versão 1.x.x para versões mais recentes
|
||||
if (fromVersion.startsWith('1.') && this.version.startsWith('2.')) {
|
||||
console.log('🆙 Atualizando estrutura para versão 2.x.x');
|
||||
|
||||
// Adicionar novos campos se não existirem
|
||||
if (!migratedConfig.toolsVisibility) {
|
||||
migratedConfig.toolsVisibility = { ...this.defaultConfig.toolsVisibility };
|
||||
}
|
||||
|
||||
if (!migratedConfig.backupSettings) {
|
||||
migratedConfig.backupSettings = { ...this.defaultConfig.backupSettings };
|
||||
}
|
||||
|
||||
if (!migratedConfig.uiState) {
|
||||
migratedConfig.uiState = { ...this.defaultConfig.uiState };
|
||||
}
|
||||
}
|
||||
|
||||
// Sempre atualizar versão e timestamp
|
||||
migratedConfig.version = this.version;
|
||||
migratedConfig.lastModified = Date.now();
|
||||
|
||||
// Salvar configurações migradas
|
||||
this.saveConfig(migratedConfig);
|
||||
|
||||
console.log('✅ Migração concluída com sucesso');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erro durante migração:', error);
|
||||
// Em caso de erro, resetar para config padrão
|
||||
this.resetConfig();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém a versão atual das configurações salvas
|
||||
*/
|
||||
getVersion() {
|
||||
try {
|
||||
const saved = localStorage.getItem(this.configKey);
|
||||
if (saved) {
|
||||
const parsed = JSON.parse(saved);
|
||||
return parsed.version || '0.0.0';
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Erro ao obter versão:', error);
|
||||
}
|
||||
return '0.0.0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Salva as configurações no localStorage
|
||||
* @param {Object} config - Configurações a serem salvas
|
||||
* @returns {Object} Configurações salvas
|
||||
*/
|
||||
saveConfig(config) {
|
||||
try {
|
||||
const currentConfig = this.getConfig();
|
||||
const configToSave = {
|
||||
...currentConfig,
|
||||
...config,
|
||||
version: this.version,
|
||||
lastModified: Date.now()
|
||||
};
|
||||
|
||||
localStorage.setItem(this.configKey, JSON.stringify(configToSave));
|
||||
console.log('✅ Configurações admin salvas com sucesso');
|
||||
|
||||
// Disparar evento de mudança de config
|
||||
this.notifyConfigChange(configToSave);
|
||||
|
||||
return configToSave;
|
||||
} catch (error) {
|
||||
console.error('❌ Erro ao salvar configurações:', error);
|
||||
throw new Error('Falha ao salvar configurações: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém as configurações salvas ou retorna as padrões
|
||||
* @returns {Object} Configurações atuais
|
||||
*/
|
||||
getConfig() {
|
||||
try {
|
||||
const saved = localStorage.getItem(this.configKey);
|
||||
if (saved) {
|
||||
const parsed = JSON.parse(saved);
|
||||
// Validar estrutura básica
|
||||
if (this.validateConfig(parsed)) {
|
||||
return { ...this.defaultConfig, ...parsed };
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Erro ao carregar configurações, usando padrões:', error);
|
||||
}
|
||||
|
||||
return { ...this.defaultConfig };
|
||||
}
|
||||
|
||||
/**
|
||||
* Reseta as configurações para os valores padrão
|
||||
* @returns {Object} Configurações padrão
|
||||
*/
|
||||
resetConfig() {
|
||||
try {
|
||||
localStorage.setItem(this.configKey, JSON.stringify(this.defaultConfig));
|
||||
console.log('🔄 Configurações resetadas para padrão');
|
||||
|
||||
// Disparar evento de mudança de config
|
||||
this.notifyConfigChange(this.defaultConfig);
|
||||
|
||||
return { ...this.defaultConfig };
|
||||
} catch (error) {
|
||||
console.error('❌ Erro ao resetar configurações:', error);
|
||||
throw new Error('Falha ao resetar configurações: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza uma configuração específica
|
||||
* @param {string} key - Chave da configuração
|
||||
* @param {*} value - Novo valor
|
||||
* @returns {Object} Configurações atualizadas
|
||||
*/
|
||||
updateConfig(key, value) {
|
||||
const config = this.getConfig();
|
||||
|
||||
// Validar chaves específicas
|
||||
if (!this.validateKeyValue(key, value)) {
|
||||
throw new Error(`Valor inválido para ${key}: ${value}`);
|
||||
}
|
||||
|
||||
// Atualizar valor aninhado se necessário
|
||||
if (key.includes('.')) {
|
||||
this.setNestedValue(config, key, value);
|
||||
} else {
|
||||
config[key] = value;
|
||||
}
|
||||
|
||||
return this.saveConfig(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém uma configuração específica
|
||||
* @param {string} key - Chave da configuração
|
||||
* @param {*} defaultValue - Valor padrão se não existir
|
||||
* @returns {*} Valor da configuração
|
||||
*/
|
||||
getConfigValue(key, defaultValue = null) {
|
||||
const config = this.getConfig();
|
||||
|
||||
if (key.includes('.')) {
|
||||
return this.getNestedValue(config, key, defaultValue);
|
||||
}
|
||||
|
||||
return config[key] !== undefined ? config[key] : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define visibilidade de uma ferramenta específica
|
||||
* @param {string} toolName - Nome da ferramenta
|
||||
* @param {boolean} visible - Visibilidade
|
||||
* @returns {Object} Configurações atualizadas
|
||||
*/
|
||||
setToolVisibility(toolName, visible) {
|
||||
return this.updateConfig(`toolsVisibility.${toolName}`, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém visibilidade de uma ferramenta
|
||||
* @param {string} toolName - Nome da ferramenta
|
||||
* @returns {boolean} Visibilidade da ferramenta
|
||||
*/
|
||||
getToolVisibility(toolName) {
|
||||
return this.getConfigValue(`toolsVisibility.${toolName}`, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aplica as configurações ao aplicativo
|
||||
*/
|
||||
applyConfig() {
|
||||
const config = this.getConfig();
|
||||
|
||||
try {
|
||||
// Aplicar nome do aplicativo
|
||||
if (config.appName) {
|
||||
document.title = config.appName;
|
||||
const titleElements = document.querySelectorAll('.app-title');
|
||||
titleElements.forEach(el => el.textContent = config.appName);
|
||||
}
|
||||
|
||||
// Aplicar subtítulo
|
||||
if (config.appSubtitle) {
|
||||
const subtitleElements = document.querySelectorAll('.app-subtitle');
|
||||
subtitleElements.forEach(el => el.textContent = config.appSubtitle);
|
||||
}
|
||||
|
||||
// Aplicar texto do rodapé
|
||||
if (config.footerText) {
|
||||
const footerEl = document.getElementById('appFooter');
|
||||
if (footerEl) {
|
||||
const p = footerEl.querySelector('p');
|
||||
if (p) p.textContent = config.footerText; else footerEl.textContent = config.footerText;
|
||||
}
|
||||
}
|
||||
|
||||
// Aplicar visibilidade de ferramentas
|
||||
this.applyToolsVisibility(config.toolsVisibility);
|
||||
|
||||
// Aplicar tema padrão
|
||||
if (config.themeDefault && window.themeManager) {
|
||||
window.themeManager.setTheme(config.themeDefault);
|
||||
}
|
||||
|
||||
// Aplicar modo padrão (simples/expert)
|
||||
if (config.modeDefault) {
|
||||
const isExpertDesired = ['expert', 'experto'].includes(config.modeDefault);
|
||||
const isExpertActive = document.documentElement.classList.contains('expert-mode');
|
||||
if (typeof window.toggleExpertMode === 'function' && isExpertDesired !== isExpertActive) {
|
||||
window.toggleExpertMode();
|
||||
}
|
||||
}
|
||||
|
||||
// Aplicar branding (logotipo, favicon, manifest)
|
||||
this.applyBranding();
|
||||
|
||||
// Refiltrar ferramentas após aplicar modo/tema
|
||||
if (typeof window.filterToolsByMode === 'function') {
|
||||
window.filterToolsByMode();
|
||||
}
|
||||
|
||||
console.log('✅ Configurações aplicadas com sucesso');
|
||||
} catch (error) {
|
||||
console.error('❌ Erro ao aplicar configurações:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aplica visibilidade de ferramentas
|
||||
* @param {Object} toolsVisibility - Objeto com visibilidade das ferramentas
|
||||
*/
|
||||
applyToolsVisibility(toolsVisibility) {
|
||||
const expertActive = document.documentElement.classList.contains('expert-mode');
|
||||
Object.entries(toolsVisibility).forEach(([tool, visible]) => {
|
||||
const displayValue = expertActive ? '' : (visible ? '' : 'none');
|
||||
const toolElement = document.querySelector(`[data-tool="${tool}"]`);
|
||||
if (toolElement) {
|
||||
toolElement.style.display = displayValue;
|
||||
}
|
||||
|
||||
// Também atualizar navegação se existir
|
||||
const navElement = document.querySelector(`[data-nav="${tool}"]`);
|
||||
if (navElement) {
|
||||
navElement.style.display = displayValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Valida a estrutura das configurações
|
||||
* @param {Object} config - Configurações a validar
|
||||
* @returns {boolean} Se é válido
|
||||
*/
|
||||
validateConfig(config) {
|
||||
if (!config || typeof config !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verificar campos obrigatórios
|
||||
const requiredFields = ['appName', 'version'];
|
||||
for (const field of requiredFields) {
|
||||
if (!config[field]) {
|
||||
console.warn(`⚠️ Campo obrigatório ausente: ${field}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valida chave e valor específicos
|
||||
* @param {string} key - Chave a validar
|
||||
* @param {*} value - Valor a validar
|
||||
* @returns {boolean} Se é válido
|
||||
*/
|
||||
validateKeyValue(key, value) {
|
||||
// Validações específicas por tipo de config
|
||||
switch (key) {
|
||||
case 'themeDefault':
|
||||
return ['escuro', 'claro'].includes(value);
|
||||
case 'modeDefault':
|
||||
return ['simples', 'experto', 'expert'].includes(value);
|
||||
case 'dataRefreshInterval':
|
||||
return typeof value === 'number' && value >= 1 && value <= 168; // 1 hora a 1 semana
|
||||
case 'backupInterval':
|
||||
return typeof value === 'number' && value >= 1 && value <= 30; // 1 a 30 dias
|
||||
case 'autoBackup':
|
||||
return typeof value === 'boolean';
|
||||
case 'branding.logo':
|
||||
return value === null || typeof value === 'string';
|
||||
default:
|
||||
return true; // Aceitar outras chaves
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define valor em propriedade aninhada
|
||||
* @param {Object} obj - Objeto alvo
|
||||
* @param {string} path - Caminho (ex: 'toolsVisibility.cev')
|
||||
* @param {*} value - Valor a definir
|
||||
*/
|
||||
setNestedValue(obj, path, value) {
|
||||
const keys = path.split('.');
|
||||
let current = obj;
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
if (!current[keys[i]]) {
|
||||
current[keys[i]] = {};
|
||||
}
|
||||
current = current[keys[i]];
|
||||
}
|
||||
|
||||
current[keys[keys.length - 1]] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém valor de propriedade aninhada
|
||||
* @param {Object} obj - Objeto alvo
|
||||
* @param {string} path - Caminho (ex: 'toolsVisibility.cev')
|
||||
* @param {*} defaultValue - Valor padrão
|
||||
* @returns {*} Valor obtido
|
||||
*/
|
||||
getNestedValue(obj, path, defaultValue = null) {
|
||||
const keys = path.split('.');
|
||||
let current = obj;
|
||||
|
||||
for (const key of keys) {
|
||||
if (current && typeof current === 'object' && key in current) {
|
||||
current = current[key];
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifica mudanças de configuração via eventos customizados
|
||||
* @param {Object} newConfig - Nova configuração
|
||||
*/
|
||||
notifyConfigChange(newConfig) {
|
||||
const event = new CustomEvent('adminConfigChanged', {
|
||||
detail: { config: newConfig },
|
||||
bubbles: true
|
||||
});
|
||||
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporta as configurações como JSON
|
||||
* @returns {string} Configurações em formato JSON
|
||||
*/
|
||||
exportConfig() {
|
||||
const config = this.getConfig();
|
||||
return JSON.stringify(config, null, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Importa configurações de JSON
|
||||
* @param {string} jsonString - Configurações em formato JSON
|
||||
* @returns {Object} Configurações importadas
|
||||
*/
|
||||
importConfig(jsonString) {
|
||||
try {
|
||||
const importedConfig = JSON.parse(jsonString);
|
||||
|
||||
if (!this.validateConfig(importedConfig)) {
|
||||
throw new Error('Configurações inválidas');
|
||||
}
|
||||
|
||||
// Atualizar versão para a atual
|
||||
importedConfig.version = this.version;
|
||||
|
||||
return this.saveConfig(importedConfig);
|
||||
} catch (error) {
|
||||
console.error('❌ Erro ao importar configurações:', error);
|
||||
throw new Error('Falha ao importar configurações: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém estatísticas do sistema de config
|
||||
* @returns {Object} Estatísticas
|
||||
*/
|
||||
getStats() {
|
||||
const config = this.getConfig();
|
||||
const savedSize = localStorage.getItem(this.configKey)?.length || 0;
|
||||
|
||||
return {
|
||||
version: this.version,
|
||||
savedVersion: config.version,
|
||||
lastModified: config.lastModified || null,
|
||||
size: savedSize,
|
||||
toolsCount: Object.keys(config.toolsVisibility).length,
|
||||
visibleTools: Object.values(config.toolsVisibility).filter(v => v).length
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Aplica branding: logotipo no header, favicon e manifest PWA dinamicamente
|
||||
*/
|
||||
applyBranding() {
|
||||
try {
|
||||
const config = this.getConfig();
|
||||
const branding = config.branding || {};
|
||||
|
||||
// Atualizar tema/cores meta
|
||||
if (branding.themeColor) {
|
||||
const metaTheme = document.querySelector('meta[name="theme-color"]');
|
||||
if (metaTheme) metaTheme.setAttribute('content', branding.themeColor);
|
||||
}
|
||||
|
||||
// Header logo
|
||||
const logoEl = document.getElementById('appLogo');
|
||||
if (logoEl) {
|
||||
if (branding.logo) {
|
||||
logoEl.innerHTML = `<img src="${branding.logo}" alt="${branding.logoAlt || 'Logo'}" class="app-logo-img">`;
|
||||
} else {
|
||||
// Fallback texto padrão
|
||||
logoEl.textContent = '🏗️ ' + (config.appName || 'SteelBase');
|
||||
}
|
||||
}
|
||||
|
||||
// Favicon
|
||||
if (branding.logo) {
|
||||
let favicon = document.querySelector('link[rel="icon"]');
|
||||
if (!favicon) {
|
||||
favicon = document.createElement('link');
|
||||
favicon.setAttribute('rel', 'icon');
|
||||
document.head.appendChild(favicon);
|
||||
}
|
||||
favicon.setAttribute('href', branding.logo);
|
||||
}
|
||||
|
||||
// Manifest PWA dinâmico (ícones com base no logo)
|
||||
if (branding.useInPWA) {
|
||||
const icons = [];
|
||||
if (branding.logo) {
|
||||
const isSvg = branding.logo.startsWith('data:image/svg');
|
||||
const type = isSvg ? 'image/svg+xml' : 'image/png';
|
||||
icons.push({ src: branding.logo, sizes: '192x192', type, purpose: 'any maskable' });
|
||||
icons.push({ src: branding.logo, sizes: '512x512', type, purpose: 'any maskable' });
|
||||
}
|
||||
|
||||
const manifestObj = {
|
||||
name: config.appName || 'SteelBase',
|
||||
short_name: (config.appName || 'SteelBase'),
|
||||
description: 'Plataforma profissional de cálculos de engenharia estrutural',
|
||||
start_url: '/',
|
||||
display: 'standalone',
|
||||
background_color: branding.backgroundColor || '#fcfcf9',
|
||||
theme_color: branding.themeColor || '#21808d',
|
||||
orientation: 'any',
|
||||
icons: icons.length ? icons : undefined,
|
||||
categories: ['productivity', 'utilities', 'business'],
|
||||
lang: 'pt-BR',
|
||||
dir: 'ltr'
|
||||
};
|
||||
|
||||
const manifestJson = JSON.stringify(manifestObj);
|
||||
const blob = new Blob([manifestJson], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
let manifestLink = document.querySelector('link[rel="manifest"]');
|
||||
if (!manifestLink) {
|
||||
manifestLink = document.createElement('link');
|
||||
manifestLink.setAttribute('rel', 'manifest');
|
||||
document.head.appendChild(manifestLink);
|
||||
}
|
||||
manifestLink.setAttribute('href', url);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Erro ao aplicar branding:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Placeholder: caso necessário, futuramente podemos redimensionar ícones.
|
||||
}
|
||||
|
||||
// Criar instância global
|
||||
window.adminConfigManager = new AdminConfigManager();
|
||||
|
||||
// Função global para aplicar configurações (chamada pelo main.js)
|
||||
window.applyAdminConfig = async function() {
|
||||
try {
|
||||
// Aplicar config padrão
|
||||
window.adminConfigManager.applyConfig();
|
||||
|
||||
// Branding pode ter necessidade de gerar ícones async
|
||||
// Se os ícones forem Promises, aguardar (método atual retorna sync exceto resize async)
|
||||
// Para simplificar, reaplicar branding após pequeno atraso se houver logo
|
||||
const cfg = window.adminConfigManager.getConfig();
|
||||
if (cfg.branding && cfg.branding.logo) {
|
||||
// Pequeno atraso para garantir DOM pronto
|
||||
setTimeout(() => window.adminConfigManager.applyBranding(), 50);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('⚠️ Erro em applyAdminConfig:', err);
|
||||
}
|
||||
};
|
||||
|
||||
console.log('🚀 AdminConfigManager inicializado com sucesso');
|
||||
Reference in New Issue
Block a user