// ========== CONFIG ========== const SUPABASE_URL = 'https://ftujctzhsitfdlctbohr.supabase.co'; const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZ0dWpjdHpoc2l0ZmRsY3Rib2hyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Njg0MDQzNzcsImV4cCI6MjA4Mzk4MDM3N30.NivluvmqGq7Jc-zpZHsCdjUxC4GzWiAWoR4Ee0N78GI'; const supabaseClient = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY); const MATRIZ_SIZE = 490; const MATRIZ_PADDING = 20; const DRAW_SIZE = MATRIZ_SIZE - (MATRIZ_PADDING * 2); const RAIO_PONTO = 18; const RAIO_PG = 22; const MIN_COORDS_FOR_CLUSTER = 15; let currentCompany = null; let currentUser = null; let matrizData = []; // Estado dos respondentes individuais (com main_style) let activeLabels = {}; let uniqueLabelsMap = {}; const predefinedColors = ['#FF5733', '#33FF57', '#5733FF', '#F6D833', '#33F6D8', '#D833F6', '#FF335E', '#5E33FF', '#3EFF33', '#33FFAE']; let labelColors = {}; let colorIndex = 0; // Estado dos batches let batches = []; let batchData = {}; let activeBatches = {}; // Estado do modal let amostraAtiva = true; let tipoSelecionado = null; function getRandomColor() { const l = '0123456789ABCDEF'; let c = '#'; for (let i = 0; i < 6; i++) c += l[Math.floor(Math.random() * 16)]; return c; } function getColorForLabel(id) { if (!labelColors[id]) labelColors[id] = colorIndex < predefinedColors.length ? predefinedColors[colorIndex++] : getRandomColor(); return labelColors[id]; } function calcularPosicaoSVG(f, e, c, a) { f = parseFloat(f) || 0; e = parseFloat(e) || 0; c = parseFloat(c) || 0; a = parseFloat(a) || 0; const normalX = (c - a) / 100; const normalY = (f - e) / 100; return { svgX: MATRIZ_PADDING + (DRAW_SIZE / 2) + (normalX * DRAW_SIZE / 2), svgY: MATRIZ_PADDING + (DRAW_SIZE / 2) - (normalY * DRAW_SIZE / 2) }; } // ========== INIT ========== async function initDash() { try { const { data: { session }, error: se } = await supabaseClient.auth.getSession(); if (se || !session) { window.location.href = '/dash/login'; return; } currentUser = session.user; const { data: cd, error: ce } = await supabaseClient.rpc('get_current_company'); if (ce || !cd.success) { await supabaseClient.auth.signOut(); window.location.href = '/dash/login'; return; } currentCompany = cd.company; updateHeader(); loadNotifications(); await loadBatchesAndRespondents(); initModal(); document.getElementById('loading-overlay').classList.add('hidden'); } catch (e) { console.error('Init error:', e); window.location.href = '/dash/login'; } } function updateHeader() { const d = (currentCompany.monthly_credits - currentCompany.monthly_credits_used) + currentCompany.extra_credits; document.getElementById('credits-total').textContent = d; document.getElementById('user-name').textContent = currentCompany.name; const btn = document.getElementById('btn-diagnostico-completo'); if (currentCompany.diagnostico_url) { btn.href = currentCompany.diagnostico_url; btn.style.display = 'inline-flex'; } else { btn.style.display = 'none'; } } // ========== NOTIFICATIONS ========== async function loadNotifications() { const { data: n, error } = await supabaseClient .from('notifications') .select('*') .eq('company_id', currentCompany.id) .order('created_at', { ascending: false }) .limit(20); if (error) return; const uc = n.filter(x => !x.read).length; const b = document.getElementById('notification-badge'); b.textContent = uc; b.classList.toggle('show', uc > 0); const l = document.getElementById('notifications-list'); if (n.length === 0) { l.innerHTML = '

Nenhuma notificação

'; return; } l.innerHTML = n.map(x => `

${x.title}

${x.message || ''}

`).join(''); } // ========== BATCHES & RESPONDENTS ========== async function loadBatchesAndRespondents() { const { data: batchesData, error: batchError } = await supabaseClient .from('diagnosis_batches') .select('*') .eq('company_id', currentCompany.id) .order('diagnosis_date', { ascending: false }); if (batchError) { console.error('Erro ao carregar batches:', batchError); batches = []; } else { batches = batchesData || []; } const { data: resp, error: respError } = await supabaseClient .from('respondents') .select('*') .eq('company_id', currentCompany.id) .order('completed_at', { ascending: false }); if (respError) { console.error('Erro ao carregar respondentes:', respError); return; } const individuais = resp.filter(r => r.main_style && !r.batch_id); const semBatch = resp.filter(r => !r.main_style && !r.batch_id); const comBatch = resp.filter(r => r.batch_id); batchData = {}; batches.forEach(batch => { batchData[batch.id] = { id: batch.id, label: batch.label, color: batch.color || '#3B82F6', type: batch.type, puras: [], aspiracionais: [] }; if (!activeBatches[batch.id]) { activeBatches[batch.id] = { visible: true, showAs: true, showCluster: true }; } }); comBatch.forEach(r => { if (!r.coord_flexible || !batchData[r.batch_id]) return; const coord = { f: r.coord_flexible, e: r.coord_stable, c: r.coord_independent, a: r.coord_interdependent }; if (r.label && r.label.startsWith('as-')) { batchData[r.batch_id].aspiracionais.push(coord); } else { batchData[r.batch_id].puras.push(coord); } }); renderRespondentsList(individuais); const pontos = []; uniqueLabelsMap = generateUniqueLabels(individuais); individuais.forEach(r => { if (r.coord_flexible != null) { const lb = uniqueLabelsMap[r.id] || { shortLabel: r.name.split(' ')[0] }; pontos.push({ f: r.coord_flexible, e: r.coord_stable, c: r.coord_independent, a: r.coord_interdependent, id: r.id, shortLabel: lb.shortLabel, tipo: 'individual' }); } }); semBatch.forEach(r => { if (r.coord_flexible != null) { pontos.push({ f: r.coord_flexible, e: r.coord_stable, c: r.coord_independent, a: r.coord_interdependent, tipo: 'semBatch' }); } }); drawMatriz(pontos); } function renderRespondentsList(individuais) { const list = document.getElementById('respondentes-list'); document.getElementById('respondentes-count').textContent = `${individuais.length} pessoa${individuais.length !== 1 ? 's' : ''}`; if (individuais.length === 0) { list.innerHTML = `

Nenhum respondente ainda.
Gere um link para começar.

`; return; } list.innerHTML = individuais.map(r => { const lb = uniqueLabelsMap[r.id] || { shortLabel: r.name.split(' ')[0] }; const ia = activeLabels[r.id] !== false; const co = getColorForLabel(r.id); const rl = r.report_url || `/dash/relatorio/${r.id}`; const ie = r.report_url && r.report_url.startsWith('http'); return `
${r.name}
${r.assessment_type === 'qore_scan' ? 'Qore Scan' : r.assessment_type === 'manual' ? 'Manual' : 'Quick Scan'} • ${formatDate(r.completed_at)}
${r.main_style}
`; }).join(''); } // ========== MATRIZ ========== function drawMatriz(pontos) { const w = MATRIZ_SIZE, h = MATRIZ_SIZE; d3.select('#matrix').html(''); const svg = d3.select('#matrix').append('svg') .attr('width', '100%') .attr('height', '100%') .attr('viewBox', `0 0 ${w} ${h}`); matrizData = pontos.map(p => { const pos = calcularPosicaoSVG(p.f, p.e, p.c, p.a); return { ...p, svgX: pos.svgX, svgY: pos.svgY }; }); const semBatch = matrizData.filter(d => d.tipo === 'semBatch'); const opSemBatch = semBatch.length > 100 ? 0.08 : semBatch.length > 50 ? 0.12 : 0.2; semBatch.forEach(d => { svg.append('circle') .attr('cx', d.svgX) .attr('cy', d.svgY) .attr('r', RAIO_PONTO) .attr('fill', '#FFFFFF') .attr('opacity', opSemBatch); }); Object.keys(batchData).forEach(batchId => { const batch = batchData[batchId]; const state = activeBatches[batchId]; if (!state || !state.visible) return; const opPuras = batch.puras.length > 100 ? 0.15 : batch.puras.length > 50 ? 0.25 : 0.4; batch.puras.forEach(coord => { const pos = calcularPosicaoSVG(coord.f, coord.e, coord.c, coord.a); svg.append('circle') .attr('cx', pos.svgX) .attr('cy', pos.svgY) .attr('r', RAIO_PONTO) .attr('fill', batch.color) .attr('opacity', opPuras); }); if (state.showAs && batch.aspiracionais.length > 0) { batch.aspiracionais.forEach(coord => { const pos = calcularPosicaoSVG(coord.f, coord.e, coord.c, coord.a); svg.append('circle') .attr('cx', pos.svgX) .attr('cy', pos.svgY) .attr('r', RAIO_PONTO) .attr('fill', '#ea3659') .attr('opacity', 0.4); }); } if (state.showCluster && batch.puras.length >= MIN_COORDS_FOR_CLUSTER) { const pontosPG = batch.puras.map(p => ({ x: parseFloat(p.a) || 0, y: parseFloat(p.e) || 0 })); drawPGForBatch(svg, pontosPG, batch.label, batch.color); } if (state.showAs && state.showCluster && batch.aspiracionais.length >= MIN_COORDS_FOR_CLUSTER) { const pontosAS = batch.aspiracionais.map(p => ({ x: parseFloat(p.a) || 0, y: parseFloat(p.e) || 0 })); drawPGForBatch(svg, pontosAS, batch.label + 'ₐ', '#ea3659'); } }); const individuais = matrizData.filter(d => d.tipo === 'individual'); individuais.forEach(d => { if (!activeLabels.hasOwnProperty(d.id)) activeLabels[d.id] = true; if (activeLabels[d.id] === false) return; const co = getColorForLabel(d.id); svg.append('circle') .attr('cx', d.svgX) .attr('cy', d.svgY) .attr('r', RAIO_PONTO) .attr('fill', co) .attr('opacity', 0.7); svg.append('text') .attr('x', d.svgX) .attr('y', d.svgY + 4) .attr('fill', 'white') .attr('font-size', '10px') .attr('text-anchor', 'middle') .text(d.shortLabel); }); drawStruct(svg, w, h); updateBatchControls(); } function drawPGForBatch(svg, pontos, label, color) { if (pontos.length < 3) return; const cl = kMeansClusterizacao(pontos, NUM_CLUSTERS_PG); const cu = unificarClustersProximos(cl); let cv = cu .filter(c => c.densidade / pontos.length >= MIN_PERCENTUAL_CLUSTER) .sort((a, b) => b.densidade - a.densidade); if (cv.length === 0) return; const c = cv[0]; const pos = calcularPosicaoSVG(100 - c.centro.y, c.centro.y, 100 - c.centro.x, c.centro.x); animRadar(svg, pos.svgX, pos.svgY, RAIO_PG, color); svg.append('circle') .attr('cx', pos.svgX) .attr('cy', pos.svgY) .attr('r', RAIO_PG) .attr('fill', color) .attr('opacity', 0.3); const shortLabel = label.length > 8 ? label.substring(0, 8) : label; svg.append('text') .attr('x', pos.svgX) .attr('y', pos.svgY + 5) .attr('fill', 'white') .attr('font-size', '10px') .attr('font-weight', 'bold') .attr('text-anchor', 'middle') .text(shortLabel); } function drawStruct(svg, w, h) { const dg = 'white', lg = '#A781F3', cx = w / 2, cy = h / 2; svg.append('line').attr('x1', cx).attr('y1', 0).attr('x2', cx).attr('y2', h).attr('stroke', dg).attr('stroke-width', 1); svg.append('line').attr('x1', 0).attr('y1', cy).attr('x2', w).attr('y2', cy).attr('stroke', dg).attr('stroke-width', 1); svg.append('line').attr('x1', 0).attr('y1', 0).attr('x2', w).attr('y2', h).attr('stroke', lg).attr('stroke-width', 1).attr('stroke-dasharray', '5,5'); svg.append('line').attr('x1', w).attr('y1', 0).attr('x2', 0).attr('y2', h).attr('stroke', lg).attr('stroke-width', 1).attr('stroke-dasharray', '5,5'); [1, 2, 3].forEach(i => { const o = w / 6 * i; svg.append('rect') .attr('x', o).attr('y', o) .attr('width', w - 2 * o).attr('height', h - 2 * o) .attr('stroke', lg).attr('stroke-width', 1) .attr('stroke-dasharray', '5,5').attr('fill', 'none'); }); const ql = [ { x: w * 0.15, y: h * 0.3, t: 'APRENDIZADO' }, { x: w * 0.35, y: h * 0.08, t: 'PRAZER' }, { x: w * 0.65, y: h * 0.08, t: 'PROPÓSITO' }, { x: w * 0.85, y: h * 0.3, t: 'ACOLHIMENTO' }, { x: w * 0.85, y: h * 0.72, t: 'ORDEM' }, { x: w * 0.65, y: h * 0.92, t: 'SEGURANÇA' }, { x: w * 0.35, y: h * 0.92, t: 'AUTORIDADE' }, { x: w * 0.15, y: h * 0.72, t: 'RESULTADO' } ]; ql.forEach(l => svg.append('text') .attr('x', l.x).attr('y', l.y) .attr('fill', 'white').attr('font-size', '9px') .attr('text-anchor', 'middle').attr('opacity', 0.6) .text(l.t)); } function animRadar(svg, x, y, r, co) { for (let i = 0; i < 3; i++) { const c = svg.append('circle') .attr('cx', x).attr('cy', y).attr('r', r) .attr('fill', co).attr('opacity', 0); (function rp() { c.attr('r', r).attr('opacity', 0) .transition().duration(1000).attr('r', r * 3).attr('opacity', 0.1) .transition().duration(1000).attr('r', r * 6).attr('opacity', 0) .on('end', rp); })(); } for (let i = 0; i < 3; i++) { const c = svg.append('circle') .attr('cx', x).attr('cy', y).attr('r', r) .attr('fill', 'none').attr('stroke', co) .attr('stroke-width', 2).attr('opacity', 0.05); (function rp() { c.transition().duration(2000).attr('r', r * 4).attr('opacity', 0) .transition().duration(0).attr('r', r).attr('opacity', 0.15) .on('end', rp); })(); } } // ========== BATCH CONTROLS ========== function updateBatchControls() { const controlsContainer = document.getElementById('matriz-controls'); controlsContainer.innerHTML = ''; if (batches.length === 0) return; const mainContainer = document.createElement('div'); mainContainer.style.cssText = 'display: flex; flex-direction: column; gap: 12px; width: 100%;'; const activeControlsDiv = document.createElement('div'); activeControlsDiv.className = 'batch-active-controls'; activeControlsDiv.style.cssText = 'display: flex; flex-direction: column; gap: 8px;'; const availableBatchesDiv = document.createElement('div'); availableBatchesDiv.className = 'batch-available'; availableBatchesDiv.style.cssText = 'display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;'; batches.forEach(batch => { const data = batchData[batch.id]; const state = activeBatches[batch.id] || { visible: false, showAs: true, showCluster: true }; const hasAs = data && data.aspiracionais.length > 0; const hasCluster = data && data.puras.length >= MIN_COORDS_FOR_CLUSTER; const batchBtn = document.createElement('button'); batchBtn.className = `batch-toggle-btn ${state.visible ? 'active' : ''}`; batchBtn.style.cssText = ` display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 20px; background: ${state.visible ? 'rgba(255,255,255,0.15)' : 'rgba(255,255,255,0.05)'}; border: 2px solid ${state.visible ? batch.color : 'rgba(255,255,255,0.2)'}; color: ${state.visible ? '#fff' : 'rgba(255,255,255,0.5)'}; font-size: 12px; font-weight: 500; cursor: pointer; transition: all 0.2s; `; batchBtn.innerHTML = ` ${batch.label} (${data ? data.puras.length : 0}) `; batchBtn.onclick = () => toggleBatchVisibility(batch.id); availableBatchesDiv.appendChild(batchBtn); if (state.visible && (hasAs || hasCluster)) { const controlRow = document.createElement('div'); controlRow.className = 'batch-control-row'; controlRow.style.cssText = ` display: flex; align-items: center; gap: 8px; padding: 6px 12px; border-radius: 8px; background: rgba(255,255,255,0.05); border-left: 3px solid ${batch.color}; `; const labelSpan = document.createElement('span'); labelSpan.style.cssText = `font-size: 12px; color: ${batch.color}; font-weight: 500; min-width: 80px;`; labelSpan.textContent = batch.label; controlRow.appendChild(labelSpan); if (hasAs) { const asBtn = document.createElement('button'); asBtn.className = `control-toggle ${state.showAs ? 'active' : ''}`; asBtn.style.cssText = ` padding: 4px 8px; border-radius: 4px; font-size: 11px; background: ${state.showAs ? 'rgba(234, 54, 89, 0.3)' : 'rgba(255,255,255,0.1)'}; border: 1px solid ${state.showAs ? '#ea3659' : 'rgba(255,255,255,0.2)'}; color: ${state.showAs ? '#ea3659' : 'rgba(255,255,255,0.5)'}; cursor: pointer; `; asBtn.textContent = `as (${data.aspiracionais.length})`; asBtn.onclick = (e) => { e.stopPropagation(); toggleBatchAs(batch.id); }; controlRow.appendChild(asBtn); } if (hasCluster) { const clusterBtn = document.createElement('button'); clusterBtn.className = `control-toggle ${state.showCluster ? 'active' : ''}`; clusterBtn.style.cssText = ` padding: 4px 8px; border-radius: 4px; font-size: 11px; background: ${state.showCluster ? `${batch.color}33` : 'rgba(255,255,255,0.1)'}; border: 1px solid ${state.showCluster ? batch.color : 'rgba(255,255,255,0.2)'}; color: ${state.showCluster ? batch.color : 'rgba(255,255,255,0.5)'}; cursor: pointer; `; clusterBtn.textContent = 'pG'; clusterBtn.onclick = (e) => { e.stopPropagation(); toggleBatchCluster(batch.id); }; controlRow.appendChild(clusterBtn); } activeControlsDiv.appendChild(controlRow); } }); if (activeControlsDiv.children.length > 0) { mainContainer.appendChild(activeControlsDiv); } mainContainer.appendChild(availableBatchesDiv); controlsContainer.appendChild(mainContainer); } function toggleBatchVisibility(batchId) { if (!activeBatches[batchId]) { activeBatches[batchId] = { visible: true, showAs: true, showCluster: true }; } else { activeBatches[batchId].visible = !activeBatches[batchId].visible; } redrawMatriz(); } function toggleBatchAs(batchId) { if (activeBatches[batchId]) { activeBatches[batchId].showAs = !activeBatches[batchId].showAs; } redrawMatriz(); } function toggleBatchCluster(batchId) { if (activeBatches[batchId]) { activeBatches[batchId].showCluster = !activeBatches[batchId].showCluster; } redrawMatriz(); } function toggleRespondentLabel(id) { activeLabels[id] = activeLabels[id] === false ? true : false; const ia = activeLabels[id] !== false; const co = getColorForLabel(id); document.querySelectorAll(`.respondente-style[data-id="${id}"]`).forEach(b => { b.classList.toggle('inactive', !ia); b.style.background = ia ? co : 'rgba(255,255,255,0.1)'; b.style.color = ia ? '#fff' : 'rgba(255,255,255,0.4)'; }); redrawMatriz(); } function redrawMatriz() { if (matrizData.length === 0 && Object.keys(batchData).length === 0) { drawMatriz([]); return; } drawMatriz(matrizData); } // ========== MODAL LOGIC ========== function initModal() { const ta = document.getElementById('toggle-amostra'); const ac = document.getElementById('amostra-config'); const cc = document.getElementById('config-completo'); const s2 = document.getElementById('diag-step-2'); ta.addEventListener('click', () => { amostraAtiva = !amostraAtiva; ta.classList.toggle('active', amostraAtiva); ac.style.display = amostraAtiva ? 'block' : 'none'; recalc(); }); document.querySelectorAll('input[name="diag-type"]').forEach(r => r.addEventListener('change', e => { tipoSelecionado = e.target.value; document.querySelectorAll('.radio-option[data-type]').forEach(o => o.classList.remove('selected')); e.target.closest('.radio-option').classList.add('selected'); cc.style.display = tipoSelecionado === 'completo' ? 'block' : 'none'; s2.classList.remove('hidden'); recalc(); })); ['diag-populacao', 'diag-confianca', 'diag-erro', 'diag-recortes', 'diag-perguntas'].forEach(id => { const el = document.getElementById(id); if (el) { el.addEventListener('input', recalc); el.addEventListener('change', recalc); } }); } function recalc() { const pop = parseInt(document.getElementById('diag-populacao').value) || 0; const conf = parseInt(document.getElementById('diag-confianca').value) || 95; const err = parseInt(document.getElementById('diag-erro').value) || 2; const rec = parseInt(document.getElementById('diag-recortes').value) || 2; const per = parseInt(document.getElementById('diag-perguntas').value) || 0; let resp = pop; if (amostraAtiva && pop > 0) { resp = calcularAmostra(pop, conf, err); document.getElementById('amostra-resultado').textContent = resp; document.getElementById('amostra-detail').textContent = `${conf}% de confiança, ${err}% de margem de erro`; } else if (!amostraAtiva && pop > 0) { document.getElementById('amostra-resultado').textContent = pop; document.getElementById('amostra-detail').textContent = 'Censo completo (100% dos colaboradores)'; } else { document.getElementById('amostra-resultado').textContent = '--'; document.getElementById('amostra-detail').textContent = 'Preencha a quantidade de colaboradores'; } let preco = 0; if (resp > 0 && tipoSelecionado) { preco = tipoSelecionado === 'completo' ? calcularPrecoCompleto(resp, rec, per) : calcularPrecoPulso(resp); } document.getElementById('preco-resultado').textContent = preco.toLocaleString('pt-BR'); document.getElementById('btn-solicitar-orcamento').disabled = preco === 0 || !tipoSelecionado; } function resetModal() { document.getElementById('diag-populacao').value = ''; document.getElementById('diag-step-2').classList.add('hidden'); document.querySelectorAll('input[name="diag-type"]').forEach(r => r.checked = false); document.querySelectorAll('.radio-option[data-type]').forEach(o => o.classList.remove('selected')); tipoSelecionado = null; amostraAtiva = true; document.getElementById('toggle-amostra').classList.add('active'); document.getElementById('amostra-config').style.display = 'block'; document.getElementById('config-completo').style.display = 'block'; document.getElementById('diag-form-content').classList.remove('hidden'); document.getElementById('diag-success').classList.add('hidden'); document.getElementById('diag-footer').style.display = 'flex'; recalc(); } // ========== EVENT LISTENERS ========== document.getElementById('btn-novo-diagnostico').addEventListener('click', () => { resetModal(); openModal('modal-novo-diagnostico'); }); document.getElementById('btn-solicitar-orcamento').addEventListener('click', async () => { const btn = document.getElementById('btn-solicitar-orcamento'); btn.disabled = true; btn.textContent = 'Enviando...'; const pop = parseInt(document.getElementById('diag-populacao').value) || 0; const conf = parseInt(document.getElementById('diag-confianca').value) || 95; const err = parseInt(document.getElementById('diag-erro').value) || 2; const rec = parseInt(document.getElementById('diag-recortes').value) || 2; const per = parseInt(document.getElementById('diag-perguntas').value) || 0; const resp = amostraAtiva ? calcularAmostra(pop, conf, err) : pop; const preco = tipoSelecionado === 'completo' ? calcularPrecoCompleto(resp, rec, per) : calcularPrecoPulso(resp); const tn = tipoSelecionado === 'completo' ? 'Diagnóstico Completo' : 'Pulso Rápido'; try { const { data: qd, error: qe } = await supabaseClient.from('quote_requests').insert({ company_id: currentCompany.id, tipo: tipoSelecionado, populacao: pop, respondentes: resp, amostra: amostraAtiva, confianca: amostraAtiva ? conf : null, erro: amostraAtiva ? err : null, recortes: tipoSelecionado === 'completo' ? rec : null, perguntas: tipoSelecionado === 'completo' ? per : null, preco_estimado: preco }).select().single(); if (qe) throw qe; await supabaseClient.from('notifications').insert({ company_id: currentCompany.id, title: '📋 Orçamento solicitado', message: `Seu pedido de ${tn} (R$ ${preco.toLocaleString('pt-BR')}) foi recebido. Em breve entraremos em contato.`, type: 'quote', reference_id: qd.id, read: false }); await supabaseClient.from('admin_alerts').insert({ type: 'quote_request', title: `Novo orçamento: ${currentCompany.name}`, message: `${tn} • ${resp} respondentes • R$ ${preco.toLocaleString('pt-BR')}`, reference_id: qd.id, company_id: currentCompany.id }); document.getElementById('diag-form-content').classList.add('hidden'); document.getElementById('diag-success').classList.remove('hidden'); document.getElementById('diag-footer').style.display = 'none'; loadNotifications(); } catch (e) { console.error(e); alert('Erro ao enviar orçamento.'); btn.disabled = false; btn.textContent = 'Solicitar Orçamento'; } }); document.getElementById('btn-gerar-link').addEventListener('click', () => { document.getElementById('link-result').style.display = 'none'; document.getElementById('btn-confirmar-link').style.display = 'inline-flex'; document.getElementById('btn-confirmar-link').disabled = false; document.getElementById('btn-confirmar-link').textContent = 'Gerar Link'; openModal('modal-gerar-link'); }); document.getElementById('btn-confirmar-link').addEventListener('click', async () => { const t = document.querySelector('input[name="assessment-type"]:checked').value; const btn = document.getElementById('btn-confirmar-link'); btn.disabled = true; btn.textContent = 'Gerando...'; try { const { data, error } = await supabaseClient.rpc('create_assessment_link', { p_assessment_type: t, p_max_responses: t === 'quick_scan' ? 10 : 1 }); if (error) throw error; if (!data.success) throw new Error(data.error); document.getElementById('generated-link').value = data.url; document.getElementById('link-result').style.display = 'block'; btn.style.display = 'none'; const { data: cd } = await supabaseClient.rpc('get_current_company'); if (cd.success) { currentCompany = cd.company; updateHeader(); } loadNotifications(); } catch (e) { alert(e.message || 'Erro ao gerar link'); btn.disabled = false; btn.textContent = 'Gerar Link'; } }); function copyLink() { const i = document.getElementById('generated-link'); i.select(); document.execCommand('copy'); event.target.textContent = 'Copiado!'; setTimeout(() => event.target.textContent = 'Copiar', 2000); } document.getElementById('btn-autodiagnostico').addEventListener('click', () => window.location.href = '/dash/autodiagnostico'); document.getElementById('notification-btn').addEventListener('click', () => openModal('modal-notificacoes')); document.getElementById('mark-all-read').addEventListener('click', async () => { await supabaseClient.from('notifications').update({ read: true }).eq('company_id', currentCompany.id); loadNotifications(); }); document.getElementById('clear-read').addEventListener('click', async () => { await supabaseClient.from('notifications').delete().eq('company_id', currentCompany.id).eq('read', true); loadNotifications(); }); function openModal(id) { document.getElementById(id).classList.add('show'); } function closeModal(id) { document.getElementById(id).classList.remove('show'); } function formatDate(ds) { return new Date(ds).toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric' }); } // ========== INIT ========== initDash();