HEX
Server: Apache
System: Linux vpshost11508.publiccloud.com.br 5.15.179-grsec-vpshost-10.lc.el8.x86_64 #1 SMP Mon Apr 7 12:04:45 -03 2025 x86_64
User: wicomm2 (10002)
PHP: 8.3.0
Disabled: apache_child_terminate,dl,escapeshellarg,escapeshellcmd,exec,link,mail,openlog,passthru,pcntl_alarm,pcntl_exec,pcntl_fork,pcntl_get_last_error,pcntl_getpriority,pcntl_setpriority,pcntl_signal,pcntl_signal_dispatch,pcntl_sigprocmask,pcntl_sigtimedwait,pcntl_sigwaitinfo,pcntl_strerror,pcntl_wait,pcntl_waitpid,pcntl_wexitstatus,pcntl_wifexited,pcntl_wifsignaled,pcntl_wifstopped,pcntl_wstopsig,pcntl_wtermsig,php_check_syntax,php_strip_whitespace,popen,proc_close,proc_open,shell_exec,symlink,system
Upload Files
File: /home/storage/5/78/dd/wicomm2/public_html/clientes/lookai/index.php
<?php $cfg = require __DIR__ . '/config.php';
date_default_timezone_set($cfg['app']['timezone']); ?>
<!doctype html>
<html lang="pt-br">
  <head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title><?= htmlspecialchars($cfg['app']['name']) ?></title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
    <style>
      body {
        background: #0f1116;
        color: #f1f1f1;
        font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Arial;
        margin: 0
      }
      .container {
        max-width: 1400px;
        margin: 0 auto;
        padding: 28px
      }
      header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 18px
      }
      h1 {
        font-size: 22px;
        margin: 0
      }
      select,
      button {
        background: #1a1d24;
        color: #f1f1f1;
        border: 1px solid #333;
        padding: 8px 12px;
        border-radius: 8px
      }
      button.primary {
        background: #5c6df4;
        border: none;
        cursor: pointer
      }
      .tabs {
        display: flex;
        gap: 10px;
        margin: 12px 0 20px
      }
      .tab {
        padding: 8px 16px;
        border-radius: 10px;
        background: #1a1d24;
        cursor: pointer
      }
      .tab.active {
        background: #5c6df4
      }
      .grid {
        display: grid;
        grid-template-columns: repeat(12, 1fr);
        gap: 16px
      }
      .card {
        background: #171a21;
        border: 1px solid #262a35;
        border-radius: 14px;
        padding: 16px;
        box-shadow: 0 0 12px #0003
      }
      .kpis {
        display: grid;
        grid-template-columns: repeat(6, 1fr);
        gap: 12px
      }
      .kpi {
        background: #20232a;
        border: 1px solid #2a2f3b;
        border-radius: 10px;
        padding: 14px;
        text-align: center
      }
      .kpi h3 {
        margin: 0 0 8px;
        font-weight: 600;
        color: #bfc3cc;
        font-size: 13px
      }
      .kpi .v {
        font-weight: 800;
        font-size: 20px
      }
      .table {
        width: 100%;
        border-collapse: collapse
      }
      .table th,
      .table td {
        padding: 10px;
        border-bottom: 1px solid #2a2f3b;
        text-align: left;
        font-size: 14px
      }
      .muted {
        color: #9aa0aa;
        font-size: 12px
      }
      .chart-wrap {
        height: 280px
      }
      #ai-output {
        white-space: pre-wrap;
        background: #20232a;
        border: 1px solid #2a2f3b;
        border-radius: 10px;
        padding: 14px
      }
      #ai-output p,
      #ai-output ul {
        font-size: 14px;
        line-height: 1.4;
      }
    </style>
  </head>
  <body>
    <div id="authModal" style="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,1);display:flex;align-items:center;justify-content:center;z-index:9999;">
      <div style="background:#1a1d24;padding:30px;border-radius:10px;text-align:center;width:320px;">
        <h3 style="margin-bottom:15px;color:#f1f1f1;">Autenticação</h3>
        <p style="color:#aaa;font-size:13px;margin-bottom:15px;">Insira o token de acesso para continuar</p>
        <input type="password" id="tokenInput" placeholder="Token" style="width:100%;padding:8px;border-radius:6px;border:1px solid #333;background:#0f1116;color:#fff;">
        <button id="checkToken" style="margin-top:15px;width:100%;background:#5c6df4;color:#fff;padding:8px;border:none;border-radius:6px;cursor:pointer;">Acessar</button>
        <p id="errorMsg" style="color:#ff6666;font-size:12px;margin-top:10px;display:none;">Token inválido.</p>
      </div>
    </div>
    <script>
      const CORRECT_TOKEN = 'L1omruSe+gP.|,BGFSY,!,.a2yEZ6z+|;MrS]dmOt1o|+ZEJo.g!YgOV';

      document.getElementById('checkToken').addEventListener('click', ()=>{
        const val = document.getElementById('tokenInput').value.trim();
        if(val === CORRECT_TOKEN){
          localStorage.setItem('auth_token', val);
          document.getElementById('authModal').style.display = 'none';
        } else {
          document.getElementById('errorMsg').style.display = 'block';
        }
      });

      // Verifica se o token já está salvo
      window.addEventListener('DOMContentLoaded', ()=>{
        const saved = localStorage.getItem('auth_token');
        if(saved === CORRECT_TOKEN){
          document.getElementById('authModal').style.display = 'none';
        }
      });
    </script>
    <div class="container">
      <header>
        <h1><?= htmlspecialchars($cfg['app']['name']) ?></h1>
        <div>
          <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
          <script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
          <label>Período:
            <input type="text" id="date-range" placeholder="Selecione o intervalo" style="background:#1a1d24;color:#f1f1f1;border:1px solid #333;padding:8px 12px;border-radius:8px;width:220px;">
          </label>
          <button id="refresh" class="primary">Atualizar</button>
        </div>
      </header>

      <div class="tabs">
        <div class="tab active" data-area="CRO">CRO</div>
        <div class="tab" data-area="BI">BI</div>
        <div class="tab" data-area="SEO">SEO</div>
      </div>

      <div class="grid">
        <div class="card" style="grid-column:1/13">
          <h3>KPIs principais</h3>
          <div class="kpis">
            <div class="kpi">
              <h3>Sessões</h3>
              <div class="v" id="kpi-sessions">--</div>
            </div>
            <div class="kpi">
              <h3>Receita</h3>
              <div class="v" id="kpi-revenue">--</div>
            </div>
            <div class="kpi">
              <h3>Meta mensal</h3>
              <div class="v" id="kpi-goal">--</div>
              <div id="goal-bar" style="height:8px;background:#2a2f3b;border-radius:6px;overflow:hidden;margin-top:6px;">
                <div id="goal-progress" style="height:8px;width:0;background:#26d7a0;transition:width .4s"></div>
              </div>
            </div>
            <div class="kpi">
              <h3>Novos usuários</h3>
              <div class="v" id="kpi-users">--</div>
            </div>
            <div class="kpi">
              <h3>Taxa de conversão</h3>
              <div class="v" id="kpi-conv-rate">--</div>
            </div>
            <div class="kpi">
              <h3>AOV</h3>
              <div class="v" id="kpi-aov">--</div>
            </div>
          </div>
        </div>

        <div class="card" style="grid-column:1/13">
          <h3>Sessões diárias</h3>
          <div class="chart-wrap">
            <canvas id="chart-sessions"></canvas>
          </div>
        </div>
        <div class="card" style="grid-column:1/13">
          <h3>Receita diária</h3>
          <div class="chart-wrap">
            <canvas id="chart-revenue"></canvas>
          </div>
        </div>

        <div class="card" style="grid-column:1/7">
          <h3>Top 5 produtos (por receita)</h3>
          <table class="table" id="tbl-top"></table>
        </div>
        <div class="card" style="grid-column:7/13">
          <h3>Receita mensal (MoM)</h3>
          <div class="chart-wrap">
            <canvas id="chart-monthly"></canvas>
          </div>
          <div class="muted" id="mom-note"></div>
        </div>

        <div class="card" style="grid-column:1/13">
          <h3>Funil de Conversão</h3>
          <div id="funnel-visual" style="display:flex;justify-content:flex-start;align-items:center;gap:20px;padding:10px 0"></div>
        </div>

        <div class="card" style="grid-column:1/13; display:none;">
          <h3>Resumo de Produtos</h3>
          <div id="product-summary" style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
            <div>
              <h4>Top 3 por conversão</h4>
              <ul id="top-products"></ul>
            </div>
            <div>
              <h4>Baixa adição ao carrinho</h4>
              <ul id="low-cart-products"></ul>
            </div>
          </div>
        </div>

        <div class="card" style="grid-column:1/13">
          <h3>Desempenho por Canal</h3>
          <div class="chart-wrap">
            <canvas id="chart-channels"></canvas>
          </div>
        </div>

        <div class="card" style="grid-column:1/13">
          <h3>Insights por IA (<span id="area-tag">CRO</span>)</h3>
          <div id="ai-output">Carregando…</div>
        </div>
      </div>
    </div>

    <script>
      let area = 'CRO', days = 60;
          let cs, cr, cm; // charts
      
          const el = id => document.getElementById(id);
      
          async function fetchAnalyze() {
            const qs = new URLSearchParams({ area, days: String(days) });
            if (window.customDateRange && window.customDateRange.start && window.customDateRange.end) {
              qs.set('start', window.customDateRange.start);
              qs.set('end', window.customDateRange.end);
            }
            const r = await fetch(`api/analyze.php?${qs.toString()}`);
            return r.json();
          }
          async function fetchAI(context) {
            const r = await fetch('api/ai.php', {
              method:'POST', headers:{'Content-Type':'application/json'},
              body: JSON.stringify({ area, context })
            });
            return r.json();
          }
      
          function n(v,d=0){ return Number(v||0).toLocaleString('pt-BR',{minimumFractionDigits:d,maximumFractionDigits:d}); }
      
          function renderKPIs(k){
            el('kpi-sessions').textContent = n(k.sessions);
            el('kpi-revenue').textContent  = 'R$ ' + n(k.revenue, 2);
            el('kpi-users').textContent    = n(k.new_users);
            el('kpi-conv-rate').textContent = n(k.conv_rate,1) + '%';
            el('kpi-aov').textContent      = 'R$ ' + n(k.aov,2);
          }
          function renderProductSummary(items){
            if(!items || items.length === 0) return;
            const sortedByCart = [...items].sort((a,b)=>(b.add_to_cart/b.view_item)-(a.add_to_cart/a.view_item));
            const top3 = sortedByCart.slice(0,3);
            const low3 = sortedByCart.slice(-3);
      
            el('top-products').innerHTML = top3.map(i=>
              `<li>${i.item_name} — ${(i.add_to_cart/i.view_item*100).toFixed(1)}%</li>`
            ).join('');
      
            el('low-cart-products').innerHTML = low3.map(i=>
              `<li>${i.item_name} — ${(i.add_to_cart/i.view_item*100).toFixed(1)}%</li>`
            ).join('');
          }
      
          function buildLine(canvasId, label, labels, data, color){
            const ctx = el(canvasId).getContext('2d');
            const inst = new Chart(ctx, {
              type:'line',
              data:{ labels, datasets:[{ label, data, borderColor:color, tension:.25, pointRadius:0, fill:false }] },
              options:{ responsive:true, maintainAspectRatio:false, plugins:{legend:{display:false}}, scales:{ x:{ grid:{color:'#222'}}, y:{ grid:{color:'#222'} } } }
            });
            return inst;
          }
      
          function renderSeries(series){
            if (cs) cs.destroy(); if (cr) cr.destroy();
            cs = buildLine('chart-sessions', 'Sessões', series.labels, series.sessions, '#8390ff');
            cr = buildLine('chart-revenue',  'Receita',  series.labels, series.revenue,  '#26d7a0');
          }
      
          function renderTop(items){
            const tbl = el('tbl-top');
            tbl.innerHTML = '<tr><th>Produto</th><th>Receita</th><th>Qtd</th></tr>' +
              (items||[]).map(i=>`<tr><td>${i.item_name}</td><td>R$ ${n(i.item_revenue,2)}</td><td>${n(i.items_purchased)}</td></tr>`).join('');
          }
      
          function renderMonthly(monthly, mom){
            if (cm) cm.destroy();
            const ctx = el('chart-monthly').getContext('2d');
            cm = new Chart(ctx, {
              type:'bar',
              data:{ labels: monthly.labels||[], datasets:[{ label:'Receita', data: monthly.revenue||[] }]},
              options:{ responsive:true, maintainAspectRatio:false, plugins:{legend:{display:false}}, scales:{ x:{ grid:{color:'#222'}}, y:{ grid:{color:'#222'} } } }
            });
            el('mom-note').textContent = (mom===null||mom===undefined) ? '' : `Variação mês/mês: ${n(mom,1)}%`;
          }
      
          // Função para converter markdown básico para HTML
          function markdownToHTML(md){
        if (!md) return '';
      
        // helpers
        const mdInline = (s) =>
          s
            .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>') // **bold**
            .replace(/_(.+?)_/g, '<em>$1</em>');              // _italic_
      
        const lines = md.replace(/\r\n/g, '\n').split('\n');
        let html = '';
        let inUL = false, inOL = false;
        let para = '';
      
        const closePara = () => {
          if (para.trim()) { html += `<p>${para.trim()}</p>`; para = ''; }
        };
        const closeLists = () => {
          if (inUL) { html += '</ul>'; inUL = false; }
          if (inOL) { html += '</ol>'; inOL = false; }
        };
      
        for (let raw of lines) {
          const line = raw.trimRight();
      
          // linha em branco => fecha parágrafo e listas
          if (/^\s*$/.test(line)) { 
            closePara(); 
            closeLists();
            continue;
          }
      
          // headings
          const h = line.match(/^(#{1,6})\s+(.*)$/);
          if (h) {
            closePara(); closeLists();
            const level = h[1].length;
            html += `<h${level}>${mdInline(h[2].trim())}</h${level}>`;
            continue;
          }
      
          // lista ordenada: "1. item"
          const ol = line.match(/^\d+\.\s+(.*)$/);
          if (ol) {
            closePara();
            if (!inOL) { closeLists(); html += '<ol>'; inOL = true; }
            html += `<li>${mdInline(ol[1].trim())}</li>`;
            continue;
          }
      
          // lista com hífen: "- item"
          const ul = line.match(/^-+\s+(.*)$/) || line.match(/^-\s+(.*)$/);
          if (ul) {
            closePara();
            if (!inUL) { closeLists(); html += '<ul>'; inUL = true; }
            html += `<li>${mdInline(ul[1].trim())}</li>`;
            continue;
          }
      
          // linha normal => acumula no parágrafo atual
          para += (para ? ' ' : '') + mdInline(line);
        }
      
        // fecha estruturas abertas
        closePara();
        closeLists();
      
        return html;
      }
      
          function renderFunnel(funnel){
        const container = el('funnel-visual');
        if(!funnel || funnel.length === 0){
          container.innerHTML = '<p class="muted">Sem dados de funil disponíveis</p>';
          return;
        }
      
        const max = Math.max(...funnel.map(f=>f.count));
      
        container.style.flexDirection = 'column';
        container.style.alignItems = 'center';
        container.innerHTML = funnel.map((f, i) => {
          const width = 90 - i * 10; // cada etapa mais estreita
          const opacity = 0.4 + i * 0.1; // leve diferença visual
          return `
            <div style="
              width:${width}%;
              background:#5c6df4;
              height:40px;
              border-radius:8px;
              margin:4px 0;
              display:flex;
              align-items:center;
              justify-content:center;
              color:#fff;
              font-size:13px;
              font-weight:600;
              opacity:${opacity};
              transition:width 0.3s;
            ">
              ${f.event_name}: ${f.count.toLocaleString('pt-BR')}
            </div>
          `;
        }).join('');
      }
      
          async function loadAll(){
            const data = await fetchAnalyze();
            if (!data.ok) { el('ai-output').textContent = data.error || 'Falha'; return; }
      
            console.log(data);
      
            renderKPIs(data.kpis);
            renderSeries(data.series);
            renderTop(data.top_items);
            renderMonthly(data.monthly, data.monthly_mom);
            // Adiciona renderização da meta mensal
            renderGoal(data, <?= json_encode(preg_replace('/[^\d,]/', '', $cfg['ecommerce']['goal'])) ?>);
            renderFunnel(data.funnel_events);
            renderProductSummary(data.top_items);
            renderChannels(data.channels);
      
            el('area-tag').textContent = area;
            el('ai-output').textContent = 'Gerando insights…';
      
            const cacheKey = `ai_cache_${area}`;
            const cache = JSON.parse(localStorage.getItem(cacheKey) || '{}');
            const today = new Date().toISOString().slice(0,10);
      
            if(cache.date === today && cache.text){
              el('ai-output').innerHTML = markdownToHTML(cache.text) + '<br><br><small class="muted">🔁 Insight carregado do cache (últimas 24h)</small>';
              return;
            }
      
            const ai = await fetchAI({
              kpis:data.kpis, trend:data.trend, series:data.series,
              top_items:data.top_items, monthly:data.monthly, channels:data.channels
            });
      
            if(ai.text){
              localStorage.setItem(cacheKey, JSON.stringify({ date: today, text: ai.text }));
            }
      
            el('ai-output').innerHTML = markdownToHTML(ai.text || 'Sem resposta da IA.');
          }
      
          // Função para renderizar progresso da meta mensal (ajustada para filtro de datas)
          function renderGoal(data, goal){
            if(!data || !data.series || !data.series.labels) return;

            // Converte meta de string "R$ 15.672.897" para número
            const numericGoal = parseFloat(String(goal).replace(/[^\d,.-]/g, '').replace(/\./g, '').replace(',', '.')) || 0;

            // Determina intervalo selecionado (customDateRange ou padrão)
            let startDate, endDate;
            if (window.customDateRange) {
              startDate = new Date(window.customDateRange.start);
              endDate = new Date(window.customDateRange.end);
            } else {
              endDate = new Date();
              startDate = new Date();
              startDate.setDate(endDate.getDate() - days);
            }

            // Usa o mesmo valor do KPI de receita (já filtrado no backend)
            let monthlyRevenue = parseFloat(data.kpis.revenue || 0);

            // Normaliza escala caso a receita esteja 100x maior que a meta (ex.: centavos x reais)
            if (numericGoal > 0 && monthlyRevenue > numericGoal * 20) {
              monthlyRevenue = monthlyRevenue / 100;
            }

            // Calcula progresso em relação à meta (%) corretamente
            const pct = numericGoal > 0 ? (monthlyRevenue / numericGoal) * 100 : 0;

            // Atualiza elementos no front-end
            el('kpi-goal').innerHTML = `
              ${pct.toFixed(1)}%<br>
              <small style="color:#9aa0aa;font-size:12px;">
                R$ ${n(monthlyRevenue,2)} / R$ ${n(numericGoal,2)}
              </small>
            `;
            el('goal-progress').style.width = Math.min(100, pct) + '%';
          }
      
          
      
          // UI
          document.querySelectorAll('.tab').forEach(t=>{
            t.addEventListener('click', ()=>{
              document.querySelectorAll('.tab').forEach(x=>x.classList.remove('active'));
              t.classList.add('active'); area = t.dataset.area; loadAll();
            });
          });
          flatpickr("#date-range", {
            mode: "range",
            dateFormat: "Y-m-d",
            locale: "pt",
            onClose: async function(selectedDates) {
        if (selectedDates.length === 2) {
          const [start, end] = selectedDates;
          const startStr = start.toISOString().slice(0,10);
          const endStr = end.toISOString().slice(0,10);
      
          // Atualiza GA4 com base nas novas datas
          await fetch(`api/refresh_ga4.php?start=${startStr}&end=${endStr}`);
      
          window.customDateRange = { start: startStr, end: endStr };
          loadAll();
        }
      }
          });
      
          document.getElementById('refresh').addEventListener('click', ()=>{
            const fp = document.querySelector('#date-range')._flatpickr;
            if (fp.selectedDates.length === 2) {
              const [start, end] = fp.selectedDates;
              window.customDateRange = {
                start: start.toISOString().slice(0,10),
                end: end.toISOString().slice(0,10)
              };
            } else {
              window.customDateRange = null;
            }
            loadAll();
          });
      
          loadAll();
      
          function renderChannels(channels){
            const ctx = document.getElementById('chart-channels').getContext('2d');
            if(window.chartChannels) window.chartChannels.destroy();
            const labels = channels.map(c=>c.channel);
            const revenue = channels.map(c=>c.revenue);
            const transactions = channels.map(c=>c.transactions);
            window.chartChannels = new Chart(ctx, {
              type:'bar',
              data:{ labels, datasets:[{ label:'Receita (R$)', data:revenue, backgroundColor:'#26d7a0' }] },
              options:{ responsive:true, plugins:{ tooltip:{ callbacks:{ afterBody:(ctx)=>`Transações: ${transactions[ctx[0].dataIndex]}` } } } }
            });
          }
        </script>
      </body>
      </html>