Lumina Skin Intelligence
Plataforma Profissional de Skincare
Acesso exclusivo para profissionais autorizados
`; const blob = new Blob([html], {type: 'text/html'}); const url = URL.createObjectURL(blob); const win = window.open(url, '_blank'); if (!win) { const a = document.createElement('a'); a.href = url; a.download = 'rotina-lumina.html'; a.click(); } setTimeout(() => URL.revokeObjectURL(url), 30000); return url; } return { salvarRotina, carregarRotina, deletarRotina, gerarPDF, criarRotina, CATEGORIAS }; })(); `; const blob = new Blob([html], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `lumina_rotina_${perfil}_${Date.now()}.html`; a.click(); URL.revokeObjectURL(url); return true; } function getStatus() { return { version: VERSION, minha: !!localStorage.getItem(KEYS.minha), mae: !!localStorage.getItem(KEYS.mae), categorias: Object.keys(CATEGORIAS).length }; } return { criarRotina, salvarRotina, carregarRotina, deletarRotina, gerarPDF, getStatus, CATEGORIAS, VERSION }; })(); // ═══════════════════════════════════════════════════════════════════════════ // GS_SHARE_ROTINA v1.0 — Compartilhamento com QR Code · Lumina v15.8 // Gera link local (base64 localStorage) + QR Code para acesso mobile // ═══════════════════════════════════════════════════════════════════════════ window.GS_SHARE_ROTINA = (function() { 'use strict'; const KEY_PREFIX = 'lumina_share_v1_'; const MAX_SHARES = 10; // Máximo de rotinas compartilhadas salvas // Gera ID único para o compartilhamento function gerarId() { return Date.now().toString(36) + Math.random().toString(36).substr(2, 5); } // Salva rotina no localStorage e retorna o ID function salvarParaCompartilhar(rotina, perfil) { try { // Limpar compartilhamentos antigos se exceder o limite const keys = Object.keys(localStorage).filter(k => k.startsWith(KEY_PREFIX)); if (keys.length >= MAX_SHARES) { // Remove o mais antigo keys.sort(); localStorage.removeItem(keys[0]); } const id = gerarId(); const payload = JSON.stringify({ rotina, perfil, ts: Date.now() }); localStorage.setItem(KEY_PREFIX + id, payload); return id; } catch(e) { console.error('[GS_SHARE_ROTINA] Erro ao salvar:', e); return null; } } // Gera URL de compartilhamento com hash function gerarURL(id) { const base = window.location.href.split('#')[0].split('?')[0]; return base + '#share=' + id; } // Verifica se há rotina compartilhada na URL atual function verificarURLShare() { const hash = window.location.hash; if (!hash.startsWith('#share=')) return null; const id = hash.replace('#share=', ''); try { const raw = localStorage.getItem(KEY_PREFIX + id); if (!raw) return null; return JSON.parse(raw); } catch(e) { return null; } } // Gera QR Code em um elemento DOM function gerarQRCode(url, elementId) { try { const el = document.getElementById(elementId); if (!el) return false; el.innerHTML = ''; if (window.QRCode) { new window.QRCode(el, { text: url, width: 180, height: 180, colorDark: '#3A2A1E', colorLight: '#FDF8F5', correctLevel: window.QRCode.CorrectLevel ? window.QRCode.CorrectLevel.M : 1 }); return true; } else { // Fallback: imagem via API pública offline-first const encoded = encodeURIComponent(url); el.innerHTML = '
' + url + '
'; return true; } } catch(e) { console.error('[GS_SHARE_ROTINA] Erro QR:', e); return false; } } // Compartilhar rotina — salva + gera URL + retorna dados function compartilhar(rotina, perfil) { const id = salvarParaCompartilhar(rotina, perfil); if (!id) return { ok: false, erro: 'Erro ao salvar rotina para compartilhamento' }; const url = gerarURL(id); return { ok: true, id, url }; } // Copiar URL para clipboard async function copiarURL(url) { try { if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(url); return true; } // Fallback para browsers sem clipboard API const ta = document.createElement('textarea'); ta.value = url; ta.style.position = 'fixed'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.focus(); ta.select(); const ok = document.execCommand('copy'); document.body.removeChild(ta); return ok; } catch(e) { return false; } } return { compartilhar, gerarQRCode, copiarURL, verificarURLShare, gerarURL }; })(); // ═══════════════════════════════════════════════════════════════════════════ // GS_AUDIT_INTERNAL v1.0 — Auditoria Automática · Lumina v15.8 // Verifica integridade de todos os módulos a cada 60s // Exibe badge de status no Admin e salva logs no localStorage // ═══════════════════════════════════════════════════════════════════════════ window.GS_AUDIT_INTERNAL = (function() { 'use strict'; const KEY_LOGS = 'lumina_audit_logs_v1'; const KEY_STATUS = 'lumina_audit_status_v1'; const INTERVAL_MS = 60000; // 60 segundos const MODULOS = [ { id: 'GS_FORTRESS', check: () => !!window.GS_FORTRESS }, { id: 'GS_PROMO', check: () => !!window.GS_PROMO }, { id: 'GS_CUPONS', check: () => !!window.GS_CUPONS }, { id: 'GS_IMMORTAL', check: () => !!window.GS_IMMORTAL }, { id: 'GS_ROUTINE_AI', check: () => !!window.GS_ROUTINE_AI && typeof window.GS_ROUTINE_AI.criarRotina === 'function' }, { id: 'GS_LOGO_MANAGER',check: () => !!window.GS_LOGO_MANAGER && typeof window.GS_LOGO_MANAGER.getConfig === 'function' }, { id: 'GS_SHARE_ROTINA',check: () => !!window.GS_SHARE_ROTINA && typeof window.GS_SHARE_ROTINA.compartilhar === 'function' }, { id: 'GS_AUDIT_INTERNAL', check: () => true }, { id: 'localStorage', check: () => { try { localStorage.setItem('__audit_test__','1'); localStorage.removeItem('__audit_test__'); return true; } catch(e){ return false; } } }, { id: 'React', check: () => !!window.React }, { id: 'Firebase', check: () => !!window.firebase }, { id: 'QRCode', check: () => !!window.QRCode }, { id: 'ServiceWorker', check: () => 'serviceWorker' in navigator }, { id: 'Network', check: () => navigator.onLine !== false }, ]; let _interval = null; let _lastResult = null; function runAudit() { const ts = new Date().toLocaleString('pt-BR'); const results = MODULOS.map(m => { let ok = false; try { ok = m.check(); } catch(e) { ok = false; } return { id: m.id, ok, ts }; }); const passed = results.filter(r => r.ok).length; const total = results.length; const status = passed === total ? 'PERFEITO' : passed >= total * 0.8 ? 'PARCIAL' : 'CRITICO'; _lastResult = { ts, passed, total, status, results }; // Salvar no localStorage try { localStorage.setItem(KEY_STATUS, JSON.stringify({ ts, passed, total, status })); // Manter últimos 20 logs const logs = JSON.parse(localStorage.getItem(KEY_LOGS) || '[]'); logs.unshift({ ts, passed, total, status }); if (logs.length > 20) logs.splice(20); localStorage.setItem(KEY_LOGS, JSON.stringify(logs)); } catch(e) {} // Atualizar badge no Admin se visível _updateBadge(passed, total, status); // Disparar evento para componentes React window.dispatchEvent(new CustomEvent('lumina:audit_complete', { detail: _lastResult })); // Alertar se crítico if (status === 'CRITICO' && window.LUMINA_TOAST) { window.LUMINA_TOAST('⚠️ Auditoria detectou problemas críticos! Verifique o Admin.', 'error'); } console.log('[GS_AUDIT_INTERNAL] ' + ts + ' — ' + passed + '/' + total + ' (' + status + ')'); return _lastResult; } function _updateBadge(passed, total, status) { try { const badge = document.getElementById('lumina-audit-badge'); if (!badge) return; const color = status === 'PERFEITO' ? '#30A060' : status === 'PARCIAL' ? '#E08030' : '#E03030'; const icon = status === 'PERFEITO' ? '✅' : status === 'PARCIAL' ? '⚠️' : '🚨'; badge.innerHTML = icon + ' ' + passed + '/' + total; badge.style.background = color; badge.style.display = 'inline-block'; } catch(e) {} } function getStatus() { return _lastResult; } function getLogs() { try { return JSON.parse(localStorage.getItem(KEY_LOGS) || '[]'); } catch(e) { return []; } } function start() { if (_interval) clearInterval(_interval); // Primeira auditoria após 3s (aguardar módulos carregarem) setTimeout(runAudit, 3000); _interval = setInterval(runAudit, INTERVAL_MS); console.log('[GS_AUDIT_INTERNAL] Iniciado — auditoria a cada 60s ✅'); } function stop() { if (_interval) { clearInterval(_interval); _interval = null; } } // Auto-iniciar if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', start); } else { setTimeout(start, 1000); } return { runAudit, getStatus, getLogs, start, stop }; })(); // ═══════════════════════════════════════════════════════════════════════════ // GS_LOGO_MANAGER v1.0 — Logo Editável · Sincronização Total · Lumina v15.8 // ═══════════════════════════════════════════════════════════════════════════ window.GS_LOGO_MANAGER = (function() { 'use strict'; const KEY = 'lumina_logo_config_v1'; const DEFAULT = { tipo: 'emoji', // 'emoji' | 'texto' | 'imagem' emoji: '🌹', texto: 'Lumina Skin', textoSub: 'Intelligence', imagemBase64: null, corPrimaria: '#C8748A', corSecundaria: '#C9956C', tamanhoEmoji: 48, fonteTitulo: 'Cormorant Garamond, Georgia, serif' }; function getConfig() { try { const s = localStorage.getItem(KEY); return s ? { ...DEFAULT, ...JSON.parse(s) } : { ...DEFAULT }; } catch(e) { return { ...DEFAULT }; } } function salvarConfig(cfg) { try { localStorage.setItem(KEY, JSON.stringify(cfg)); aplicarLogo(cfg); window.dispatchEvent(new CustomEvent('lumina:logo_updated', { detail: cfg })); return true; } catch(e) { return false; } } function aplicarLogo(cfg) { if (!cfg) cfg = getConfig(); // ── Splash screen ────────────────────────────────────────────── const splashLogo = document.getElementById('splash-logo'); if (splashLogo) { if (cfg.tipo === 'imagem' && cfg.imagemBase64) { splashLogo.innerHTML = ''; } else if (cfg.tipo === 'texto') { splashLogo.innerHTML = '' + cfg.texto + ''; } else { splashLogo.innerHTML = cfg.emoji || DEFAULT.emoji; splashLogo.style.fontSize = (cfg.tamanhoEmoji || 48) + 'px'; } } // ── Auth wall logo ───────────────────────────────────────────── const authLogo = document.getElementById('splash-logo'); // (mesmo elemento, já tratado acima) // ── Título splash ────────────────────────────────────────────── const splashTitle = document.getElementById('splash-title'); if (splashTitle) splashTitle.textContent = cfg.texto + ' ' + cfg.textoSub; // ── Auth wall nome ───────────────────────────────────────────── const authNome = document.querySelector('#lumina-auth-wall [style*="font-size:24px"]'); if (authNome) authNome.textContent = cfg.texto; // ── Favicon dinâmico (emoji) ─────────────────────────────────── if (cfg.tipo === 'emoji') { const canvas = document.createElement('canvas'); canvas.width = 32; canvas.height = 32; const ctx = canvas.getContext('2d'); ctx.font = '28px serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(cfg.emoji || '🌹', 16, 16); const link = document.querySelector("link[rel*='icon']") || document.createElement('link'); link.rel = 'shortcut icon'; link.href = canvas.toDataURL(); document.head.appendChild(link); } // ── CSS vars para cor primária ───────────────────────────────── document.documentElement.style.setProperty('--rose', cfg.corPrimaria); document.documentElement.style.setProperty('--gold', cfg.corSecundaria); // ── Armazena globalmente ─────────────────────────────────────── window.__LUMINA_LOGO__ = cfg; } function resetar() { localStorage.removeItem(KEY); aplicarLogo(DEFAULT); window.dispatchEvent(new CustomEvent('lumina:logo_updated', { detail: DEFAULT })); return true; } // Aplicar ao carregar document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { aplicarLogo(getConfig()); }, 300); }); // Re-aplicar após auth unlock window.addEventListener('lumina:unlocked', function() { setTimeout(function() { aplicarLogo(getConfig()); }, 200); }); return { getConfig, salvarConfig, aplicarLogo, resetar, DEFAULT, KEY }; })(); console.log('[GS_LOGO_MANAGER] v1.0 carregado ✅'); console.log('[GS_ROUTINE_AI] v1.0 carregado ✅');