
// Client Portal Shell — Shell/navegação do lado cliente (Portal do Cliente MVP)
// Renderizado por App.jsx quando o usuario logado é um client_user (não é da equipe).
// Fase 3: conteúdo real de Dashboard, Minhas Ações (+ detalhe), Arquivos, Relatórios,
// Aprovações, e wire-up do registerBudgetView.

function ClientPortalShell({ session, clientUser, clientRow }) {
  const [page, setPage] = React.useState(() => localStorage.getItem('portal_page') || 'dashboard');
  const [sidebarOpen, setSidebarOpen] = React.useState(false);
  const [isMobile, setIsMobile] = React.useState(window.innerWidth < 768);
  const [userMenuOpen, setUserMenuOpen] = React.useState(false);
  const [passwordModal, setPasswordModal] = React.useState(false);
  const [passwordForm, setPasswordForm] = React.useState({ next: '', confirm: '' });
  const [passwordSaving, setPasswordSaving] = React.useState(false);
  const [passwordMsg, setPasswordMsg] = React.useState(null);
  // Quando o cliente clica numa ação/orçamento pra ver detalhes
  const [selectedEventId, setSelectedEventId] = React.useState(null);
  const [selectedBudgetId, setSelectedBudgetId] = React.useState(null);
  const [toast, setToast] = React.useState(null);
  const showToast = (msg, type = 'success') => { setToast({ msg, type }); setTimeout(() => setToast(null), 3000); };

  React.useEffect(() => {
    const onResize = () => setIsMobile(window.innerWidth < 768);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  React.useEffect(() => {
    if (!userMenuOpen) return;
    const onClickOutside = (e) => {
      if (e.target.closest('[data-portal-user-menu]')) return;
      setUserMenuOpen(false);
    };
    document.addEventListener('mousedown', onClickOutside);
    return () => document.removeEventListener('mousedown', onClickOutside);
  }, [userMenuOpen]);

  const navigate = (p) => {
    setPage(p);
    setSelectedEventId(null);
    setSelectedBudgetId(null);
    localStorage.setItem('portal_page', p);
    if (isMobile) setSidebarOpen(false);
  };

  const handleLogout = async () => { await sb.auth.signOut(); };

  const handleChangePassword = async () => {
    setPasswordMsg(null);
    if (!passwordForm.next || passwordForm.next.length < 6) {
      setPasswordMsg({ type: 'error', text: 'A senha deve ter ao menos 6 caracteres.' }); return;
    }
    if (passwordForm.next !== passwordForm.confirm) {
      setPasswordMsg({ type: 'error', text: 'As senhas não conferem.' }); return;
    }
    setPasswordSaving(true);
    const { error } = await sb.auth.updateUser({ password: passwordForm.next });
    setPasswordSaving(false);
    if (error) {
      setPasswordMsg({ type: 'error', text: error.message || 'Erro ao trocar senha.' }); return;
    }
    setPasswordMsg({ type: 'success', text: 'Senha atualizada com sucesso!' });
    setPasswordForm({ next: '', confirm: '' });
    setTimeout(() => { setPasswordModal(false); setPasswordMsg(null); }, 1500);
  };

  const NAV = [
    { id: 'dashboard',   label: 'Dashboard',           icon: 'dashboard' },
    { id: 'acoes',       label: 'Minhas Ações',        icon: 'calendar'  },
    { id: 'orcamentos',  label: 'Orçamentos',          icon: 'budget'    },
    { id: 'aprovacoes',  label: 'Aprovações',          icon: 'check'     },
    { id: 'arquivos',    label: 'Arquivos',            icon: 'download'  },
    { id: 'relatorios',  label: 'Relatórios',          icon: 'reports'   },
  ];

  const clientName = clientRow?.name || 'Cliente';
  const displayName = (clientUser.name || '').charAt(0).toUpperCase() + (clientUser.name || '').slice(1);

  const sidebarWidth = 240;
  const OG = DS.colors.primary;

  const openEventDetail = (eventId) => { setSelectedEventId(eventId); setPage('acoes'); };
  const openBudgetDetail = (budgetId) => { setSelectedBudgetId(budgetId); setPage('orcamentos'); };

  return (
    <div style={{ display: 'flex', minHeight: '100vh', background: '#F1F5F9', fontFamily: "'Inter', sans-serif" }}>

      {/* ── Sidebar ── */}
      <aside style={{
        width: isMobile ? '80vw' : sidebarWidth,
        maxWidth: 300,
        background: '#12085C',
        color: '#fff',
        display: 'flex',
        flexDirection: 'column',
        padding: '20px 16px',
        position: isMobile ? 'fixed' : 'sticky',
        top: 0,
        height: '100vh',
        left: isMobile ? (sidebarOpen ? 0 : '-100%') : 0,
        transition: 'left 0.25s',
        zIndex: 100,
        overflowY: 'auto',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 26, padding: '4px 8px' }}>
          <img src="logo-15anos-white.jpg?v=1" alt="Promoup" style={{ height: 44, objectFit: 'contain', mixBlendMode: 'screen' }} />
        </div>

        <div style={{ padding: '10px 12px', background: 'rgba(255,255,255,0.06)', borderRadius: 10, marginBottom: 20, border: '1px solid rgba(255,255,255,0.08)' }}>
          <div style={{ fontSize: 10, fontWeight: 700, color: 'rgba(255,255,255,0.55)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 4 }}>Cliente</div>
          <div style={{ fontSize: 13, fontWeight: 700, color: '#fff', lineHeight: 1.3 }}>{clientName}</div>
        </div>

        <nav style={{ display: 'flex', flexDirection: 'column', gap: 3, flex: 1 }}>
          {NAV.map(item => {
            const active = page === item.id;
            return (
              <button key={item.id} onClick={() => navigate(item.id)}
                style={{
                  display: 'flex', alignItems: 'center', gap: 12,
                  padding: '10px 12px', borderRadius: 8,
                  border: 'none',
                  background: active ? OG : 'transparent',
                  color: active ? '#fff' : 'rgba(255,255,255,0.72)',
                  fontSize: 13.5, fontWeight: active ? 700 : 500,
                  cursor: 'pointer', textAlign: 'left',
                  fontFamily: 'inherit',
                  transition: 'background 0.15s',
                }}
                onMouseEnter={e => { if (!active) e.currentTarget.style.background = 'rgba(255,255,255,0.06)'; }}
                onMouseLeave={e => { if (!active) e.currentTarget.style.background = 'transparent'; }}>
                <Icon name={item.icon} size={16} color={active ? '#fff' : 'rgba(255,255,255,0.7)'} />
                <span>{item.label}</span>
              </button>
            );
          })}
        </nav>

        <div style={{ paddingTop: 18, borderTop: '1px solid rgba(255,255,255,0.08)', fontSize: 10.5, color: 'rgba(255,255,255,0.45)', textAlign: 'center', letterSpacing: '0.03em' }}>
          Promoup 360° · Portal do Cliente
        </div>
      </aside>

      {isMobile && sidebarOpen && (
        <div onClick={() => setSidebarOpen(false)}
          style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.4)', zIndex: 99 }} />
      )}

      {/* ── Main ── */}
      <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column' }}>

        {/* Topbar */}
        <div style={{
          display: 'flex', alignItems: 'center', gap: 12,
          padding: isMobile ? '12px 16px' : '14px 28px',
          background: '#fff',
          borderBottom: `1px solid ${DS.colors.border}`,
          position: 'sticky', top: 0, zIndex: 50,
        }}>
          {isMobile && (
            <button onClick={() => setSidebarOpen(o => !o)}
              style={{ width: 36, height: 36, border: `1px solid ${DS.colors.border}`, borderRadius: DS.radius.sm, background: '#fff', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <Icon name="list" size={16} color={DS.colors.textSec} />
            </button>
          )}

          <div style={{ flex: 1, fontSize: 15, fontWeight: 700, color: DS.colors.text }}>
            {NAV.find(n => n.id === page)?.label || 'Portal do Cliente'}
          </div>

          <div data-portal-user-menu style={{ position: 'relative' }}>
            <div onClick={() => setUserMenuOpen(o => !o)}
              style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '6px 10px', borderRadius: DS.radius.sm, border: `1px solid ${DS.colors.border}`, cursor: 'pointer', background: userMenuOpen ? '#F8FAFC' : '#fff' }}>
              <Avatar name={displayName || 'C'} size={26} color={OG} />
              {!isMobile && <span style={{ fontSize: 13, fontWeight: 600, color: DS.colors.text }}>{displayName || clientUser.email}</span>}
              <Icon name="chevronDown" size={13} color={DS.colors.textMuted} />
            </div>
            {userMenuOpen && (
              <div style={{ position: 'absolute', right: 0, top: 44, width: 240, background: '#fff', borderRadius: DS.radius.md, boxShadow: DS.shadow.lg, border: `1px solid ${DS.colors.border}`, zIndex: 200, overflow: 'hidden' }}>
                <div style={{ padding: '14px 16px', borderBottom: `1px solid ${DS.colors.border}`, background: '#F8FAFC' }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: DS.colors.text }}>{displayName}</div>
                  <div style={{ fontSize: 11, color: DS.colors.textMuted, marginTop: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{clientUser.email}</div>
                </div>
                {[
                  { icon: 'edit', label: 'Trocar senha', onClick: () => { setUserMenuOpen(false); setPasswordForm({ next: '', confirm: '' }); setPasswordMsg(null); setPasswordModal(true); } },
                  { icon: 'logout', label: 'Sair', onClick: () => { setUserMenuOpen(false); handleLogout(); }, danger: true },
                ].map(item => (
                  <div key={item.label} onClick={item.onClick}
                    style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '11px 16px', cursor: 'pointer', fontSize: 13, fontWeight: 600, color: item.danger ? DS.colors.danger : DS.colors.text }}
                    onMouseEnter={e => e.currentTarget.style.background = item.danger ? '#FEF2F2' : '#F8FAFC'}
                    onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                    <Icon name={item.icon} size={14} color={item.danger ? DS.colors.danger : DS.colors.textSec} />
                    <span>{item.label}</span>
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>

        {/* Page content */}
        <div style={{
          flex: 1,
          padding: isMobile ? '16px 14px' : '28px 28px',
          overflowY: 'auto',
        }}>
          {page === 'dashboard'   && <PortalDashboard clientRow={clientRow} onOpenEvent={openEventDetail} onOpenBudget={openBudgetDetail} />}
          {page === 'acoes'       && !selectedEventId  && <PortalMinhasAcoes clientRow={clientRow} onOpen={setSelectedEventId} />}
          {page === 'acoes'       && selectedEventId   && <PortalEventDetail eventId={selectedEventId} clientRow={clientRow} onBack={() => setSelectedEventId(null)} clientUser={clientUser} showToast={showToast} />}
          {page === 'orcamentos'  && !selectedBudgetId && <PortalOrcamentos clientRow={clientRow} onOpen={setSelectedBudgetId} />}
          {page === 'orcamentos'  && selectedBudgetId  && <PortalBudgetDetail budgetId={selectedBudgetId} clientUser={clientUser} clientRow={clientRow} onBack={() => setSelectedBudgetId(null)} />}
          {page === 'aprovacoes'  && <PortalAprovacoes clientRow={clientRow} clientUser={clientUser} showToast={showToast} />}
          {page === 'arquivos'    && <PortalArquivos clientRow={clientRow} />}
          {page === 'relatorios'  && <PortalRelatorios clientRow={clientRow} />}
        </div>
      </div>

      {/* Modal Trocar Senha */}
      <Modal open={passwordModal} onClose={() => { setPasswordModal(false); setPasswordMsg(null); }} title="Trocar senha" width={380}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <Input label="Nova senha" type="password" value={passwordForm.next} onChange={v => setPasswordForm(f => ({ ...f, next: v }))} placeholder="Mínimo 6 caracteres" />
          <Input label="Confirme a nova senha" type="password" value={passwordForm.confirm} onChange={v => setPasswordForm(f => ({ ...f, confirm: v }))} />
          {passwordMsg && (
            <div style={{ padding: '10px 14px', borderRadius: DS.radius.sm, background: passwordMsg.type === 'error' ? '#FEF2F2' : '#DCFCE7', color: passwordMsg.type === 'error' ? DS.colors.danger : DS.colors.success, fontSize: 12, fontWeight: 600 }}>
              {passwordMsg.text}
            </div>
          )}
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 6 }}>
            <Btn variant="secondary" onClick={() => setPasswordModal(false)}>Cancelar</Btn>
            <Btn onClick={handleChangePassword} disabled={passwordSaving}>{passwordSaving ? 'Salvando...' : 'Salvar'}</Btn>
          </div>
        </div>
      </Modal>

      {toast && <Toast message={toast.msg} onClose={() => setToast(null)} />}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// DASHBOARD — visão geral com stats e listas
// ═══════════════════════════════════════════════════════════════════
function PortalDashboard({ clientRow, onOpenEvent, onOpenBudget }) {
  const [events, setEvents] = React.useState([]);
  const [budgets, setBudgets] = React.useState([]);
  const [approvals, setApprovals] = React.useState([]);
  const [reports, setReports] = React.useState([]);
  const [totalEvents, setTotalEvents] = React.useState(0);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    if (!clientRow?.name || !clientRow?.id) { setLoading(false); return; }
    Promise.all([
      // count: 'exact' traz o total de ações do cliente mesmo com o limit(12),
      // que existe só pra montar as listas logo abaixo.
      // Ações vêm pelo vínculo de ID (events.client_id). Antes era pelo nome
      // digitado no evento, que quase nunca batia com a razão social do CRM —
      // o portal listava zero ações e levava junto arquivos e relatórios.
      sb.from('events').select('id, name, event_date, status, location', { count: 'exact' }).eq('client_id', clientRow.id).order('event_date', { ascending: false }).limit(12),
      sb.from('budgets').select('id, code, event_name, value, status, issued_date').eq('client_name', clientRow.name).order('created_at', { ascending: false }).limit(10),
      sb.from('approvals').select('id, title, status').eq('client_id', clientRow.id).eq('status', 'pendente'),
      sb.from('event_reports').select('id, event_id, type').eq('type', 'RF').order('updated_at', { ascending: false }).limit(5),
    ]).then(([evRes, budRes, apprRes, rfRes]) => {
      setEvents(evRes.data || []);
      setTotalEvents(evRes.count ?? (evRes.data || []).length);
      setBudgets(budRes.data || []);
      setApprovals(apprRes.data || []);
      setReports(rfRes.data || []);
      setLoading(false);
    });
  }, [clientRow?.id, clientRow?.name]);

  if (loading) return <div style={{ padding: 60, textAlign: 'center' }}><Spinner /></div>;

  const today = new Date(); today.setHours(0,0,0,0);
  const proximas = events.filter(e => e.event_date && new Date(e.event_date + 'T00:00:00') >= today);
  const emAndamento = events.filter(e => ['Confirmado','Em andamento'].includes(e.status));
  const orcPendentes = budgets.filter(b => ['Enviado','Visualizado','Em análise','Solicitou ajustes'].includes(b.status));

  const stats = [
    { label: 'Total de ações',       value: totalEvents,         icon: 'grid',     color: '#0EA5E9' },
    { label: 'Próximas ações',       value: proximas.length,     icon: 'calendar', color: DS.colors.primary },
    { label: 'Em andamento',         value: emAndamento.length,  icon: 'zap',      color: DS.colors.success },
    { label: 'Orçamentos abertos',   value: orcPendentes.length, icon: 'budget',   color: DS.colors.warning },
    { label: 'Aprovações pendentes', value: approvals.length,    icon: 'check',    color: '#8B5CF6' },
  ];

  return (
    <div>
      <h1 style={{ fontSize: 24, fontWeight: 800, color: DS.colors.text, margin: '0 0 4px', letterSpacing: '-0.02em' }}>Olá, {clientRow?.name || 'Cliente'} 👋</h1>
      <p style={{ fontSize: 13, color: DS.colors.textSec, margin: '0 0 24px' }}>Aqui você acompanha tudo que a Promoup está fazendo pra sua marca em tempo real.</p>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: 14, marginBottom: 24 }}>
        {stats.map(s => (
          <Card key={s.label} style={{ padding: '18px 22px' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
              <div>
                <div style={{ fontSize: 11, fontWeight: 700, color: DS.colors.textMuted, textTransform: 'uppercase', letterSpacing: '0.06em' }}>{s.label}</div>
                <div style={{ fontSize: 28, fontWeight: 800, color: DS.colors.text, marginTop: 4 }}>{s.value}</div>
              </div>
              <div style={{ width: 40, height: 40, borderRadius: DS.radius.sm, background: s.color + '18', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Icon name={s.icon} size={18} color={s.color} />
              </div>
            </div>
          </Card>
        ))}
      </div>

      <Card style={{ padding: 0, marginBottom: 20 }}>
        <div style={{ padding: '16px 22px', borderBottom: `1px solid ${DS.colors.border}`, fontSize: 14, fontWeight: 700, color: DS.colors.text }}>
          Próximas ações
        </div>
        {proximas.length === 0 ? (
          <div style={{ padding: 40, textAlign: 'center', fontSize: 13, color: DS.colors.textMuted }}>Nenhuma ação futura programada.</div>
        ) : (
          <div>
            {proximas.slice(0, 5).map((ev, i) => (
              <div key={ev.id} onClick={() => onOpenEvent(ev.id)}
                style={{ display: 'grid', gridTemplateColumns: '1.5fr 1fr 1fr 100px', gap: 12, padding: '13px 22px', borderBottom: i < Math.min(proximas.length, 5) - 1 ? `1px solid ${DS.colors.border}` : 'none', alignItems: 'center', cursor: 'pointer', transition: 'background 0.15s' }}
                onMouseEnter={e => e.currentTarget.style.background = '#F8FAFC'}
                onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                <div style={{ fontSize: 13, fontWeight: 600, color: DS.colors.text }}>{ev.name}</div>
                <div style={{ fontSize: 12, color: DS.colors.textSec }}>{ev.event_date ? new Date(ev.event_date + 'T00:00:00').toLocaleDateString('pt-BR', { day: '2-digit', month: 'long', year: 'numeric' }) : '—'}</div>
                <div style={{ fontSize: 12, color: DS.colors.textMuted }}>{ev.location || '—'}</div>
                <Badge variant="primary">{ev.status || '—'}</Badge>
              </div>
            ))}
          </div>
        )}
      </Card>

      {budgets.length > 0 && (
        <Card style={{ padding: 0 }}>
          <div style={{ padding: '16px 22px', borderBottom: `1px solid ${DS.colors.border}`, fontSize: 14, fontWeight: 700, color: DS.colors.text }}>
            Últimos orçamentos
          </div>
          <div>
            {budgets.slice(0, 5).map((b, i) => {
              const variant = (window.BUDGET_STATUS_VARIANT || {})[b.status] || 'default';
              return (
                <div key={b.id} onClick={() => onOpenBudget(b.id)}
                  style={{ display: 'grid', gridTemplateColumns: '80px 1.5fr 1fr 130px 120px', gap: 12, padding: '13px 22px', borderBottom: i < Math.min(budgets.length, 5) - 1 ? `1px solid ${DS.colors.border}` : 'none', alignItems: 'center', cursor: 'pointer', transition: 'background 0.15s' }}
                  onMouseEnter={e => e.currentTarget.style.background = '#F8FAFC'}
                  onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                  <div style={{ fontSize: 12, fontWeight: 800, color: DS.colors.primary }}>{b.code}</div>
                  <div style={{ fontSize: 13, fontWeight: 600, color: DS.colors.text }}>{b.event_name || '—'}</div>
                  <div style={{ fontSize: 12, color: DS.colors.textSec }}>{b.issued_date || '—'}</div>
                  <div style={{ fontSize: 13, fontWeight: 700, color: DS.colors.text }}>R$ {Number(b.value || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2 })}</div>
                  <Badge variant={variant}>{b.status}</Badge>
                </div>
              );
            })}
          </div>
        </Card>
      )}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// MINHAS AÇÕES — lista todos os eventos do cliente
// ═══════════════════════════════════════════════════════════════════
function PortalMinhasAcoes({ clientRow, onOpen }) {
  const [events, setEvents] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [tab, setTab] = React.useState('todos');

  React.useEffect(() => {
    if (!clientRow?.id) { setLoading(false); return; }
    sb.from('events').select('*').eq('client_id', clientRow.id).order('event_date', { ascending: false }).then(({ data }) => {
      setEvents(data || []);
      setLoading(false);
    });
  }, [clientRow?.id]);

  if (loading) return <div style={{ padding: 60, textAlign: 'center' }}><Spinner /></div>;

  const today = new Date(); today.setHours(0,0,0,0);
  const futuros = events.filter(e => e.event_date && new Date(e.event_date + 'T00:00:00') >= today);
  const passados = events.filter(e => e.event_date && new Date(e.event_date + 'T00:00:00') < today);
  const filtered = tab === 'futuros' ? futuros : tab === 'passados' ? passados : events;

  return (
    <div>
      <h1 style={{ fontSize: 24, fontWeight: 800, color: DS.colors.text, margin: '0 0 4px' }}>Minhas Ações</h1>
      <p style={{ fontSize: 13, color: DS.colors.textSec, margin: '0 0 20px' }}>Todas as ações da sua marca — clique em qualquer uma pra ver detalhes.</p>

      <div style={{ display: 'flex', gap: 4, marginBottom: 16, background: '#F1F5F9', padding: 4, borderRadius: DS.radius.sm, width: 'fit-content' }}>
        {[['todos', `Todas (${events.length})`], ['futuros', `Futuras (${futuros.length})`], ['passados', `Concluídas (${passados.length})`]].map(([id, label]) => (
          <button key={id} onClick={() => setTab(id)} style={{
            padding: '7px 14px', borderRadius: 6, border: 'none', fontSize: 12, fontWeight: 600, cursor: 'pointer',
            background: tab === id ? '#fff' : 'transparent',
            color: tab === id ? DS.colors.text : DS.colors.textSec,
            boxShadow: tab === id ? DS.shadow.sm : 'none',
            fontFamily: 'inherit',
          }}>{label}</button>
        ))}
      </div>

      {filtered.length === 0 ? (
        <Card style={{ padding: 60, textAlign: 'center' }}>
          <Icon name="calendar" size={36} color={DS.colors.border} />
          <div style={{ fontSize: 14, fontWeight: 600, color: DS.colors.textSec, marginTop: 12 }}>Nenhuma ação neste filtro</div>
        </Card>
      ) : (
        <Card style={{ padding: 0 }}>
          <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 1.4fr 100px', gap: 12, padding: '10px 20px', background: '#F8FAFC', fontSize: 11, fontWeight: 700, color: DS.colors.textMuted, textTransform: 'uppercase', letterSpacing: '0.06em', borderBottom: `1px solid ${DS.colors.border}` }}>
            <div>Ação</div><div>Data</div><div>Local</div><div>Status</div>
          </div>
          {filtered.map((ev, i) => (
            <div key={ev.id} onClick={() => onOpen(ev.id)}
              style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 1.4fr 100px', gap: 12, padding: '14px 20px', borderBottom: i < filtered.length - 1 ? `1px solid ${DS.colors.border}` : 'none', alignItems: 'center', cursor: 'pointer', transition: 'background 0.15s' }}
              onMouseEnter={e => e.currentTarget.style.background = '#F8FAFC'}
              onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
              <div>
                <div style={{ fontSize: 13, fontWeight: 700, color: DS.colors.text }}>{ev.name}</div>
                {ev.type && <div style={{ fontSize: 11, color: DS.colors.textMuted, marginTop: 2 }}>{ev.type}</div>}
              </div>
              <div style={{ fontSize: 12, color: DS.colors.textSec }}>{ev.event_date ? new Date(ev.event_date + 'T00:00:00').toLocaleDateString('pt-BR', { day: '2-digit', month: 'short', year: 'numeric' }) : '—'}</div>
              <div style={{ fontSize: 12, color: DS.colors.textMuted }}>{ev.location || '—'}</div>
              <Badge variant="primary">{ev.status || '—'}</Badge>
            </div>
          ))}
        </Card>
      )}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// EVENT DETAIL — briefing, cronograma, timeline de operação, arquivos
// ═══════════════════════════════════════════════════════════════════
function PortalEventDetail({ eventId, clientRow, onBack, clientUser, showToast }) {
  const [event, setEvent] = React.useState(null);
  const [tasks, setTasks] = React.useState([]);
  const [updates, setUpdates] = React.useState([]);
  const [files, setFiles] = React.useState([]);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    Promise.all([
      sb.from('events').select('*').eq('id', eventId).maybeSingle(),
      sb.from('operation_updates').select('*').eq('event_id', eventId).order('created_at', { ascending: false }),
      sb.from('project_files').select('*').eq('event_id', eventId).eq('visible_to_client', true).order('created_at', { ascending: false }),
    ]).then(async ([evRes, updRes, filesRes]) => {
      setEvent(evRes.data || null);
      setUpdates(updRes.data || []);
      setFiles(filesRes.data || []);
      // tarefas via event_name (schema legado)
      if (evRes.data?.name) {
        const { data: tks } = await sb.from('tasks').select('*').eq('event_name', evRes.data.name).order('created_at');
        setTasks(tks || []);
      }
      setLoading(false);
    });
  }, [eventId]);

  const handleDownload = async (file) => {
    const { data, error } = await sb.storage.from('project-files').createSignedUrl(file.storage_path, 3600);
    if (error) { showToast('Erro ao gerar link: ' + error.message, 'error'); return; }
    window.open(data.signedUrl, '_blank');
  };

  if (loading) return <div style={{ padding: 60, textAlign: 'center' }}><Spinner /></div>;
  if (!event) return <Card style={{ padding: 40, textAlign: 'center' }}>Ação não encontrada.</Card>;

  const eventDate = event.event_date ? new Date(event.event_date + 'T00:00:00') : null;
  const daysUntil = eventDate ? Math.round((eventDate - new Date().setHours(0,0,0,0)) / 86400000) : null;

  const timelineIcon = { chegada: 'zap', inicio: 'zap', foto: 'photo', ocorrencia: 'alert', encerramento: 'check', nota: 'edit' };
  const timelineColor = { chegada: '#3B82F6', inicio: '#8B5CF6', foto: '#10B981', ocorrencia: '#F59E0B', encerramento: '#059669', nota: '#64748B' };
  const timelineLabel = { chegada: 'Chegada da equipe', inicio: 'Ação iniciada', foto: 'Foto da ativação', ocorrencia: 'Ocorrência', encerramento: 'Ação encerrada', nota: 'Atualização' };

  return (
    <div>
      <button onClick={onBack} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '6px 12px', background: '#F1F5F9', border: 'none', borderRadius: DS.radius.sm, cursor: 'pointer', fontSize: 12, fontWeight: 600, color: DS.colors.textSec, marginBottom: 16, fontFamily: 'inherit' }}>
        <Icon name="chevronLeft" size={12} color={DS.colors.textSec} /> Voltar para Minhas Ações
      </button>

      <Card style={{ padding: 28, marginBottom: 20, background: `linear-gradient(135deg, ${DS.colors.primary} 0%, #C45508 100%)`, border: 'none' }}>
        <Badge variant="default" style={{ background: 'rgba(255,255,255,0.2)', color: '#fff', marginBottom: 10 }}>{event.status}</Badge>
        <div style={{ fontSize: 26, fontWeight: 800, color: '#fff', letterSpacing: '-0.02em', marginBottom: 4 }}>{event.name}</div>
        <div style={{ display: 'flex', gap: 18, marginTop: 12, flexWrap: 'wrap' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13, color: 'rgba(255,255,255,0.9)' }}>
            <Icon name="calendar" size={14} color="rgba(255,255,255,0.9)" />
            {eventDate ? eventDate.toLocaleDateString('pt-BR', { day: '2-digit', month: 'long', year: 'numeric' }) : 'Sem data'}
            {daysUntil !== null && daysUntil > 0 && <span style={{ opacity: 0.7 }}>· em {daysUntil} dia{daysUntil !== 1 ? 's' : ''}</span>}
            {daysUntil === 0 && <Badge variant="warning" style={{ marginLeft: 4 }}>HOJE</Badge>}
          </div>
          {event.location && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13, color: 'rgba(255,255,255,0.9)' }}>
              <Icon name="map" size={14} color="rgba(255,255,255,0.9)" />{event.location}
            </div>
          )}
        </div>
      </Card>

      {event.description && (
        <Card style={{ padding: 22, marginBottom: 20 }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: DS.colors.textMuted, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 8 }}>Briefing</div>
          <div style={{ fontSize: 13, color: DS.colors.text, lineHeight: 1.6, whiteSpace: 'pre-wrap' }}>{event.description}</div>
        </Card>
      )}

      {/* Atualizações da Operação */}
      <Card style={{ padding: 0, marginBottom: 20 }}>
        <div style={{ padding: '16px 22px', borderBottom: `1px solid ${DS.colors.border}`, fontSize: 14, fontWeight: 700, color: DS.colors.text, display: 'flex', alignItems: 'center', gap: 8 }}>
          <Icon name="zap" size={14} color={DS.colors.primary} /> Atualizações da Operação
        </div>
        {updates.length === 0 ? (
          <div style={{ padding: 32, textAlign: 'center', fontSize: 13, color: DS.colors.textMuted }}>Sem atualizações da equipe ainda.</div>
        ) : (
          <div style={{ padding: 22 }}>
            {updates.map((u, i) => (
              <div key={u.id} style={{ display: 'flex', gap: 14, paddingBottom: i < updates.length - 1 ? 18 : 0, position: 'relative' }}>
                <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', flexShrink: 0 }}>
                  <div style={{ width: 32, height: 32, borderRadius: '50%', background: (timelineColor[u.type] || '#64748B') + '20', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                    <Icon name={timelineIcon[u.type] || 'bell'} size={14} color={timelineColor[u.type] || '#64748B'} />
                  </div>
                  {i < updates.length - 1 && <div style={{ width: 2, flex: 1, background: DS.colors.border, marginTop: 4 }} />}
                </div>
                <div style={{ flex: 1, minWidth: 0, paddingBottom: 4 }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: DS.colors.text }}>{timelineLabel[u.type] || u.type}</div>
                  <div style={{ fontSize: 11, color: DS.colors.textMuted, marginTop: 2 }}>
                    {new Date(u.created_at).toLocaleString('pt-BR', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' })}
                    {u.created_by_name && ` · por ${u.created_by_name}`}
                  </div>
                  {u.text && <div style={{ fontSize: 13, color: DS.colors.text, marginTop: 6, lineHeight: 1.5, whiteSpace: 'pre-wrap' }}>{u.text}</div>}
                  {u.image_url && <img src={u.image_url} alt="Foto da operação" style={{ maxWidth: '100%', maxHeight: 280, marginTop: 8, borderRadius: DS.radius.sm, border: `1px solid ${DS.colors.border}` }} />}
                </div>
              </div>
            ))}
          </div>
        )}
      </Card>

      {/* Cronograma via tasks */}
      {tasks.length > 0 && (
        <Card style={{ padding: 0, marginBottom: 20 }}>
          <div style={{ padding: '16px 22px', borderBottom: `1px solid ${DS.colors.border}`, fontSize: 14, fontWeight: 700, color: DS.colors.text }}>Cronograma</div>
          <div>
            {tasks.slice(0, 12).map((t, i) => (
              <div key={t.id} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 22px', borderBottom: i < Math.min(tasks.length, 12) - 1 ? `1px solid ${DS.colors.border}` : 'none' }}>
                <div style={{ width: 18, height: 18, borderRadius: 4, background: t.column_key === 'done' ? DS.colors.success : '#E2E8F0', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  {t.column_key === 'done' && <Icon name="check" size={11} color="#fff" />}
                </div>
                <div style={{ fontSize: 13, color: t.column_key === 'done' ? DS.colors.textSec : DS.colors.text, textDecoration: t.column_key === 'done' ? 'line-through' : 'none' }}>{t.title}</div>
              </div>
            ))}
          </div>
        </Card>
      )}

      {/* Arquivos do projeto */}
      {files.length > 0 && (
        <Card style={{ padding: 0 }}>
          <div style={{ padding: '16px 22px', borderBottom: `1px solid ${DS.colors.border}`, fontSize: 14, fontWeight: 700, color: DS.colors.text }}>Arquivos deste projeto ({files.length})</div>
          <div>
            {files.map((f, i) => (
              <div key={f.id} style={{ display: 'grid', gridTemplateColumns: '32px 1fr 100px 100px 90px', gap: 12, padding: '12px 22px', borderBottom: i < files.length - 1 ? `1px solid ${DS.colors.border}` : 'none', alignItems: 'center' }}>
                <Icon name="download" size={16} color={DS.colors.textMuted} />
                <div>
                  <div style={{ fontSize: 13, fontWeight: 600, color: DS.colors.text }}>{f.filename}</div>
                  <div style={{ fontSize: 11, color: DS.colors.textMuted, marginTop: 2 }}>{fileCategoryLabel(f.category)}</div>
                </div>
                <div style={{ fontSize: 11, color: DS.colors.textSec }}>{formatFileSize(f.size_bytes)}</div>
                <div style={{ fontSize: 11, color: DS.colors.textSec }}>{new Date(f.created_at).toLocaleDateString('pt-BR')}</div>
                <Btn size="sm" onClick={() => handleDownload(f)}>Baixar</Btn>
              </div>
            ))}
          </div>
        </Card>
      )}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// ORÇAMENTOS (listagem) e DETAIL (com wire-up de view tracking)
// ═══════════════════════════════════════════════════════════════════
function PortalOrcamentos({ clientRow, onOpen }) {
  const [budgets, setBudgets] = React.useState([]);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    if (!clientRow?.name) { setLoading(false); return; }
    sb.from('budgets').select('*').eq('client_name', clientRow.name).order('created_at', { ascending: false }).then(({ data }) => {
      setBudgets(data || []);
      setLoading(false);
    });
  }, [clientRow?.name]);

  if (loading) return <div style={{ padding: 60, textAlign: 'center' }}><Spinner /></div>;

  return (
    <div>
      <h1 style={{ fontSize: 24, fontWeight: 800, color: DS.colors.text, margin: '0 0 4px' }}>Orçamentos</h1>
      <p style={{ fontSize: 13, color: DS.colors.textSec, margin: '0 0 20px' }}>Todos os orçamentos que a Promoup enviou pra você.</p>

      {budgets.length === 0 ? (
        <Card style={{ padding: 60, textAlign: 'center' }}>
          <Icon name="budget" size={36} color={DS.colors.border} />
          <div style={{ fontSize: 14, fontWeight: 600, color: DS.colors.textSec, marginTop: 12 }}>Nenhum orçamento por aqui ainda.</div>
        </Card>
      ) : (
        <Card style={{ padding: 0 }}>
          <div style={{ display: 'grid', gridTemplateColumns: '90px 2fr 130px 130px 130px', gap: 12, padding: '10px 20px', background: '#F8FAFC', fontSize: 11, fontWeight: 700, color: DS.colors.textMuted, textTransform: 'uppercase', letterSpacing: '0.06em', borderBottom: `1px solid ${DS.colors.border}` }}>
            <div>Código</div><div>Evento</div><div>Emissão</div><div>Valor</div><div>Status</div>
          </div>
          {budgets.map((b, i) => {
            const variant = (window.BUDGET_STATUS_VARIANT || {})[b.status] || 'default';
            return (
              <div key={b.id} onClick={() => onOpen(b.id)}
                style={{ display: 'grid', gridTemplateColumns: '90px 2fr 130px 130px 130px', gap: 12, padding: '14px 20px', borderBottom: i < budgets.length - 1 ? `1px solid ${DS.colors.border}` : 'none', alignItems: 'center', cursor: 'pointer', transition: 'background 0.15s' }}
                onMouseEnter={e => e.currentTarget.style.background = '#F8FAFC'}
                onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                <div style={{ fontSize: 13, fontWeight: 800, color: DS.colors.primary }}>{b.code}</div>
                <div style={{ fontSize: 13, fontWeight: 600, color: DS.colors.text }}>{b.event_name || '—'}</div>
                <div style={{ fontSize: 12, color: DS.colors.textSec }}>{b.issued_date || '—'}</div>
                <div style={{ fontSize: 13, fontWeight: 700, color: DS.colors.text }}>R$ {Number(b.value || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2 })}</div>
                <Badge variant={variant}>{b.status}</Badge>
              </div>
            );
          })}
        </Card>
      )}
    </div>
  );
}

function PortalBudgetDetail({ budgetId, clientUser, clientRow, onBack }) {
  const [budget, setBudget] = React.useState(null);
  const [items, setItems] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const trackedRef = React.useRef(false);

  React.useEffect(() => {
    Promise.all([
      sb.from('budgets').select('*').eq('id', budgetId).maybeSingle(),
      sb.from('budget_items').select('*').eq('budget_id', budgetId),
    ]).then(([bRes, iRes]) => {
      setBudget(bRes.data || null);
      setItems(iRes.data || []);
      setLoading(false);

      // Wire-up: registra visualização (uma vez por sessão de tela)
      if (bRes.data && !trackedRef.current && window.registerBudgetView) {
        trackedRef.current = true;
        window.registerBudgetView({
          budget_id: budgetId,
          viewer_name: clientUser?.name || null,
          viewer_email: clientUser?.email || null,
        });
      }
    });
  }, [budgetId]);

  if (loading) return <div style={{ padding: 60, textAlign: 'center' }}><Spinner /></div>;
  if (!budget) return <Card style={{ padding: 40, textAlign: 'center' }}>Orçamento não encontrado.</Card>;

  const subtotal = items.reduce((s, i) => s + (parseFloat(i.total) || 0), 0);
  const discountPct = parseFloat(budget.discount) || 0;
  const feePct = parseFloat(budget.fee) || 0;
  const discountAmt = subtotal * discountPct / 100;
  const feeAmt = subtotal * feePct / 100;
  const total = subtotal - discountAmt + feeAmt;
  const variant = (window.BUDGET_STATUS_VARIANT || {})[budget.status] || 'default';
  const fmt = v => 'R$ ' + Number(v || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });

  return (
    <div>
      <button onClick={onBack} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '6px 12px', background: '#F1F5F9', border: 'none', borderRadius: DS.radius.sm, cursor: 'pointer', fontSize: 12, fontWeight: 600, color: DS.colors.textSec, marginBottom: 16, fontFamily: 'inherit' }}>
        <Icon name="chevronLeft" size={12} color={DS.colors.textSec} /> Voltar para Orçamentos
      </button>

      <Card style={{ padding: 32 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 24, flexWrap: 'wrap', gap: 12 }}>
          <div>
            <div style={{ fontSize: 12, fontWeight: 700, color: DS.colors.textMuted, textTransform: 'uppercase', letterSpacing: '0.06em' }}>Orçamento</div>
            <div style={{ fontSize: 28, fontWeight: 800, color: DS.colors.primary, letterSpacing: '-0.02em', marginTop: 4 }}>{budget.code}</div>
            <div style={{ fontSize: 14, color: DS.colors.text, marginTop: 8, fontWeight: 600 }}>{budget.event_name || '—'}</div>
            <div style={{ fontSize: 12, color: DS.colors.textMuted, marginTop: 2 }}>Emitido em {budget.issued_date || '—'}</div>
          </div>
          <Badge variant={variant} style={{ fontSize: 12, padding: '6px 14px' }}>{budget.status}</Badge>
        </div>

        <table style={{ width: '100%', borderCollapse: 'collapse', marginBottom: 20 }}>
          <thead>
            <tr style={{ background: DS.colors.text }}>
              {['Descrição', 'Qtd', 'Período', 'Valor Unit.', 'Total'].map((h, i) => (
                <th key={h} style={{ padding: '10px 14px', textAlign: i === 0 ? 'left' : (i === 1 || i === 2) ? 'center' : 'right', fontSize: 11, fontWeight: 700, color: '#fff', textTransform: 'uppercase', letterSpacing: '0.06em' }}>{h}</th>
              ))}
            </tr>
          </thead>
          <tbody>
            {items.length === 0 ? (
              <tr><td colSpan={5} style={{ padding: '20px 14px', textAlign: 'center', fontSize: 13, color: DS.colors.textMuted }}>Nenhum item</td></tr>
            ) : items.map((it, i) => (
              <tr key={it.id} style={{ background: i % 2 === 0 ? '#fff' : '#F8FAFC' }}>
                <td style={{ padding: '12px 14px', fontSize: 13, color: DS.colors.text }}>
                  <div style={{ fontWeight: 600 }}>{it.description}</div>
                  {it.service_description && <div style={{ fontSize: 11, color: DS.colors.textSec, marginTop: 4 }}>{it.service_description}</div>}
                </td>
                <td style={{ padding: '12px 14px', fontSize: 13, color: DS.colors.textSec, textAlign: 'center' }}>{it.qty}</td>
                <td style={{ padding: '12px 14px', fontSize: 13, color: DS.colors.textSec, textAlign: 'center' }}>{it.periodo && Number(it.periodo) !== 1 ? it.periodo : '—'}</td>
                <td style={{ padding: '12px 14px', fontSize: 13, color: DS.colors.textSec, textAlign: 'right' }}>{fmt(it.unit_price)}</td>
                <td style={{ padding: '12px 14px', fontSize: 13, fontWeight: 600, color: DS.colors.text, textAlign: 'right' }}>{fmt(it.total)}</td>
              </tr>
            ))}
          </tbody>
        </table>

        <div style={{ marginLeft: 'auto', width: 280 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', padding: '6px 0', fontSize: 13, color: DS.colors.textSec, borderBottom: `1px solid ${DS.colors.border}` }}>
            <span>Subtotal</span><span style={{ color: DS.colors.text }}>{fmt(subtotal)}</span>
          </div>
          {discountPct > 0 && (
            <div style={{ display: 'flex', justifyContent: 'space-between', padding: '6px 0', fontSize: 13, color: DS.colors.textSec, borderBottom: `1px solid ${DS.colors.border}` }}>
              <span>Desconto ({discountPct}%)</span><span style={{ color: DS.colors.success }}>-{fmt(discountAmt)}</span>
            </div>
          )}
          {feePct > 0 && (
            <div style={{ display: 'flex', justifyContent: 'space-between', padding: '6px 0', fontSize: 13, color: DS.colors.textSec, borderBottom: `1px solid ${DS.colors.border}` }}>
              <span>Taxa ({feePct}%)</span><span style={{ color: DS.colors.warning }}>{fmt(feeAmt)}</span>
            </div>
          )}
          <div style={{ display: 'flex', justifyContent: 'space-between', padding: '14px 0 6px', fontSize: 18, fontWeight: 800, color: DS.colors.text }}>
            <span>Total</span><span style={{ color: DS.colors.primary }}>{fmt(total)}</span>
          </div>
        </div>

        {budget.complementares && (
          <div style={{ marginTop: 20, padding: '14px 18px', background: '#FFFBEB', border: '1px solid #FDE68A', borderRadius: DS.radius.sm }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: '#92400E', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 6 }}>Informações Complementares</div>
            <div style={{ fontSize: 13, color: DS.colors.text, lineHeight: 1.6, whiteSpace: 'pre-wrap' }}>{budget.complementares}</div>
          </div>
        )}
      </Card>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// APROVAÇÕES — cliente aprova ou solicita ajuste
// ═══════════════════════════════════════════════════════════════════
function PortalAprovacoes({ clientRow, clientUser, showToast }) {
  const [approvals, setApprovals] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [ajusteModal, setAjusteModal] = React.useState(null);
  const [ajusteReason, setAjusteReason] = React.useState('');
  const [saving, setSaving] = React.useState(false);
  const [tab, setTab] = React.useState('pendentes');

  const fetchApprovals = () => {
    if (!clientRow?.id) { setLoading(false); return; }
    sb.from('approvals').select('*').eq('client_id', clientRow.id).order('created_at', { ascending: false }).then(({ data }) => {
      setApprovals(data || []);
      setLoading(false);
    });
  };

  React.useEffect(() => { fetchApprovals(); }, [clientRow?.id]);

  const handleAprovar = async (appr) => {
    if (!window.confirm(`Aprovar "${appr.title}"?`)) return;
    setSaving(true);
    await sb.from('approvals').update({
      status: 'aprovado',
      decided_by_client_user_id: clientUser.id,
      decided_by_name: clientUser.name,
      decided_at: new Date().toISOString(),
    }).eq('id', appr.id);
    // Notifica equipe
    if (window.createNotification) {
      await window.createNotification({
        type: 'budget_approved',
        title: `${clientUser.name || clientRow?.name} aprovou: ${appr.title}`,
        message: `Aprovação registrada no Portal do Cliente`,
        link: appr.resource_type === 'orcamento' ? 'budgets' : 'events',
        icon: 'check',
      });
    }
    setSaving(false);
    showToast('Aprovação registrada!');
    fetchApprovals();
  };

  const handleAjuste = async () => {
    if (!ajusteReason.trim()) { showToast('Descreva o ajuste solicitado.', 'error'); return; }
    setSaving(true);
    await sb.from('approvals').update({
      status: 'ajuste_solicitado',
      ajuste_reason: ajusteReason.trim(),
      decided_by_client_user_id: clientUser.id,
      decided_by_name: clientUser.name,
      decided_at: new Date().toISOString(),
    }).eq('id', ajusteModal.id);
    if (window.createNotification) {
      await window.createNotification({
        type: 'budget_ajuste',
        title: `${clientUser.name || clientRow?.name} solicitou ajuste em: ${ajusteModal.title}`,
        message: ajusteReason.trim().slice(0, 140),
        link: ajusteModal.resource_type === 'orcamento' ? 'budgets' : 'events',
        icon: 'alert',
      });
    }
    setSaving(false);
    setAjusteModal(null);
    setAjusteReason('');
    showToast('Solicitação de ajuste enviada.');
    fetchApprovals();
  };

  if (loading) return <div style={{ padding: 60, textAlign: 'center' }}><Spinner /></div>;

  const pendentes = approvals.filter(a => a.status === 'pendente');
  const decididas = approvals.filter(a => a.status !== 'pendente');
  const list = tab === 'pendentes' ? pendentes : decididas;

  return (
    <div>
      <h1 style={{ fontSize: 24, fontWeight: 800, color: DS.colors.text, margin: '0 0 4px' }}>Aprovações</h1>
      <p style={{ fontSize: 13, color: DS.colors.textSec, margin: '0 0 20px' }}>Aprove ou solicite ajustes — sua decisão é registrada com data e hora.</p>

      <div style={{ display: 'flex', gap: 4, marginBottom: 16, background: '#F1F5F9', padding: 4, borderRadius: DS.radius.sm, width: 'fit-content' }}>
        {[['pendentes', `Pendentes (${pendentes.length})`], ['decididas', `Histórico (${decididas.length})`]].map(([id, label]) => (
          <button key={id} onClick={() => setTab(id)} style={{
            padding: '7px 14px', borderRadius: 6, border: 'none', fontSize: 12, fontWeight: 600, cursor: 'pointer',
            background: tab === id ? '#fff' : 'transparent',
            color: tab === id ? DS.colors.text : DS.colors.textSec,
            boxShadow: tab === id ? DS.shadow.sm : 'none',
            fontFamily: 'inherit',
          }}>{label}</button>
        ))}
      </div>

      {list.length === 0 ? (
        <Card style={{ padding: 60, textAlign: 'center' }}>
          <Icon name="check" size={36} color={DS.colors.border} />
          <div style={{ fontSize: 14, fontWeight: 600, color: DS.colors.textSec, marginTop: 12 }}>
            {tab === 'pendentes' ? 'Tudo em dia! Nenhuma aprovação pendente.' : 'Nenhuma decisão no histórico ainda.'}
          </div>
        </Card>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          {list.map(a => {
            const badgeVariant = a.status === 'pendente' ? 'warning' : a.status === 'aprovado' ? 'success' : 'primary';
            const badgeLabel = { pendente: 'Aguardando você', aprovado: 'Aprovado', ajuste_solicitado: 'Ajuste solicitado' }[a.status];
            return (
              <Card key={a.id} style={{ padding: 20 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12, marginBottom: 10, flexWrap: 'wrap' }}>
                  <div style={{ minWidth: 0, flex: 1 }}>
                    <div style={{ fontSize: 11, fontWeight: 700, color: DS.colors.textMuted, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>{a.resource_type}</div>
                    <div style={{ fontSize: 15, fontWeight: 700, color: DS.colors.text }}>{a.title}</div>
                    {a.description && <div style={{ fontSize: 12, color: DS.colors.textSec, marginTop: 6, lineHeight: 1.5 }}>{a.description}</div>}
                  </div>
                  <Badge variant={badgeVariant}>{badgeLabel}</Badge>
                </div>
                {a.status === 'ajuste_solicitado' && a.ajuste_reason && (
                  <div style={{ padding: '10px 14px', background: '#FEF3C7', border: '1px solid #FDE68A', borderRadius: DS.radius.sm, fontSize: 12, color: '#92400E', marginTop: 8 }}>
                    <strong>Ajuste pedido:</strong> {a.ajuste_reason}
                  </div>
                )}
                {a.decided_at && (
                  <div style={{ fontSize: 11, color: DS.colors.textMuted, marginTop: 10 }}>
                    Decidido por {a.decided_by_name || 'você'} em {new Date(a.decided_at).toLocaleString('pt-BR')}
                  </div>
                )}
                {a.status === 'pendente' && (
                  <div style={{ display: 'flex', gap: 8, marginTop: 14, flexWrap: 'wrap' }}>
                    <Btn onClick={() => handleAprovar(a)} disabled={saving} icon="check">Aprovar</Btn>
                    <Btn variant="secondary" onClick={() => { setAjusteModal(a); setAjusteReason(''); }} disabled={saving} icon="edit">Solicitar ajuste</Btn>
                  </div>
                )}
              </Card>
            );
          })}
        </div>
      )}

      <Modal open={!!ajusteModal} onClose={() => { setAjusteModal(null); setAjusteReason(''); }} title="Solicitar ajuste" width={480}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div style={{ padding: 12, background: '#F8FAFC', borderRadius: DS.radius.sm, borderLeft: `3px solid ${DS.colors.primary}` }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: DS.colors.textMuted, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 2 }}>Item</div>
            <div style={{ fontSize: 13, fontWeight: 700, color: DS.colors.text }}>{ajusteModal?.title}</div>
          </div>
          <div>
            <label style={{ display: 'block', fontSize: 12, fontWeight: 600, color: DS.colors.textSec, marginBottom: 6 }}>O que precisa ajustar?</label>
            <textarea value={ajusteReason} onChange={e => setAjusteReason(e.target.value)}
              placeholder="Descreva o ajuste — a Promoup receberá essa mensagem"
              rows={5}
              style={{ width: '100%', padding: '10px 12px', border: `1px solid ${DS.colors.border}`, borderRadius: DS.radius.sm, fontSize: 13, fontFamily: 'inherit', color: DS.colors.text, resize: 'vertical', boxSizing: 'border-box', background: '#fff' }} />
          </div>
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
            <Btn variant="secondary" onClick={() => setAjusteModal(null)}>Cancelar</Btn>
            <Btn onClick={handleAjuste} disabled={saving}>{saving ? 'Enviando...' : 'Enviar solicitação'}</Btn>
          </div>
        </div>
      </Modal>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// ARQUIVOS — todos os arquivos visíveis, agrupados por evento
// ═══════════════════════════════════════════════════════════════════
function PortalArquivos({ clientRow }) {
  const [files, setFiles] = React.useState([]);
  const [events, setEvents] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [msg, setMsg] = React.useState(null);

  React.useEffect(() => {
    if (!clientRow?.id) { setLoading(false); return; }
    (async () => {
      const { data: evs } = await sb.from('events').select('id, name, event_date').eq('client_id', clientRow.id).order('event_date', { ascending: false });
      setEvents(evs || []);
      const ids = (evs || []).map(e => e.id);
      if (ids.length === 0) { setFiles([]); setLoading(false); return; }
      const { data: fs } = await sb.from('project_files').select('*').in('event_id', ids).eq('visible_to_client', true).order('created_at', { ascending: false });
      setFiles(fs || []);
      setLoading(false);
    })();
  }, [clientRow?.id]);

  const handleDownload = async (file) => {
    setMsg(null);
    const { data, error } = await sb.storage.from('project-files').createSignedUrl(file.storage_path, 3600);
    if (error) { setMsg({ type: 'error', text: 'Erro ao gerar link: ' + error.message }); return; }
    window.open(data.signedUrl, '_blank');
  };

  if (loading) return <div style={{ padding: 60, textAlign: 'center' }}><Spinner /></div>;

  const byEvent = {};
  files.forEach(f => {
    if (!byEvent[f.event_id]) byEvent[f.event_id] = [];
    byEvent[f.event_id].push(f);
  });
  const eventsWithFiles = events.filter(e => byEvent[e.id]);

  return (
    <div>
      <h1 style={{ fontSize: 24, fontWeight: 800, color: DS.colors.text, margin: '0 0 4px' }}>Arquivos</h1>
      <p style={{ fontSize: 13, color: DS.colors.textSec, margin: '0 0 20px' }}>Briefings, artes, contratos, NF e relatórios de cada projeto — clique pra baixar.</p>

      {msg && <div style={{ marginBottom: 14, padding: '10px 14px', borderRadius: DS.radius.sm, background: msg.type === 'error' ? '#FEF2F2' : '#DCFCE7', color: msg.type === 'error' ? DS.colors.danger : DS.colors.success, fontSize: 12, fontWeight: 600 }}>{msg.text}</div>}

      {eventsWithFiles.length === 0 ? (
        <Card style={{ padding: 60, textAlign: 'center' }}>
          <Icon name="download" size={36} color={DS.colors.border} />
          <div style={{ fontSize: 14, fontWeight: 600, color: DS.colors.textSec, marginTop: 12 }}>Nenhum arquivo disponível ainda.</div>
          <div style={{ fontSize: 12, color: DS.colors.textMuted, marginTop: 4 }}>A Promoup ainda não enviou arquivos pra suas ações.</div>
        </Card>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          {eventsWithFiles.map(ev => (
            <Card key={ev.id} style={{ padding: 0 }}>
              <div style={{ padding: '14px 20px', borderBottom: `1px solid ${DS.colors.border}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <div>
                  <div style={{ fontSize: 14, fontWeight: 700, color: DS.colors.text }}>{ev.name}</div>
                  <div style={{ fontSize: 11, color: DS.colors.textMuted, marginTop: 2 }}>{ev.event_date ? new Date(ev.event_date + 'T00:00:00').toLocaleDateString('pt-BR', { day: '2-digit', month: 'long', year: 'numeric' }) : '—'}</div>
                </div>
                <span style={{ fontSize: 11, color: DS.colors.textMuted }}>{byEvent[ev.id].length} arquivo{byEvent[ev.id].length !== 1 ? 's' : ''}</span>
              </div>
              {byEvent[ev.id].map((f, i) => (
                <div key={f.id} style={{ display: 'grid', gridTemplateColumns: '32px 1fr 120px 110px 90px', gap: 12, padding: '12px 20px', borderBottom: i < byEvent[ev.id].length - 1 ? `1px solid ${DS.colors.border}` : 'none', alignItems: 'center' }}>
                  <Icon name="download" size={16} color={DS.colors.textMuted} />
                  <div>
                    <div style={{ fontSize: 13, fontWeight: 600, color: DS.colors.text }}>{f.filename}</div>
                    <div style={{ fontSize: 11, color: DS.colors.textMuted, marginTop: 2 }}>{fileCategoryLabel(f.category)}</div>
                  </div>
                  <div style={{ fontSize: 11, color: DS.colors.textSec }}>{formatFileSize(f.size_bytes)}</div>
                  <div style={{ fontSize: 11, color: DS.colors.textSec }}>{new Date(f.created_at).toLocaleDateString('pt-BR')}</div>
                  <Btn size="sm" onClick={() => handleDownload(f)}>Baixar</Btn>
                </div>
              ))}
            </Card>
          ))}
        </div>
      )}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// RELATÓRIOS FINAIS — RFs finalizados dos eventos do cliente
// ═══════════════════════════════════════════════════════════════════
function PortalRelatorios({ clientRow }) {
  const [reports, setReports] = React.useState([]);
  const [events, setEvents] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [selected, setSelected] = React.useState(null);

  React.useEffect(() => {
    if (!clientRow?.id) { setLoading(false); return; }
    (async () => {
      const { data: evs } = await sb.from('events').select('id, name, event_date').eq('client_id', clientRow.id).order('event_date', { ascending: false });
      setEvents(evs || []);
      const ids = (evs || []).map(e => e.id);
      if (ids.length === 0) { setReports([]); setLoading(false); return; }
      const { data: rfs } = await sb.from('event_reports').select('*').eq('type', 'RF').in('event_id', ids).order('updated_at', { ascending: false });
      setReports(rfs || []);
      setLoading(false);
    })();
  }, [clientRow?.id]);

  if (loading) return <div style={{ padding: 60, textAlign: 'center' }}><Spinner /></div>;

  const eventById = {};
  events.forEach(e => { eventById[e.id] = e; });

  if (selected) {
    return <PortalRelatorioDetail rf={selected} ev={eventById[selected.event_id]} onBack={() => setSelected(null)} />;
  }

  return (
    <div>
      <h1 style={{ fontSize: 24, fontWeight: 800, color: DS.colors.text, margin: '0 0 4px' }}>Relatórios Finais</h1>
      <p style={{ fontSize: 13, color: DS.colors.textSec, margin: '0 0 20px' }}>Resultados de cada ação — métricas, observações e download em PDF.</p>

      {reports.length === 0 ? (
        <Card style={{ padding: 60, textAlign: 'center' }}>
          <Icon name="reports" size={36} color={DS.colors.border} />
          <div style={{ fontSize: 14, fontWeight: 600, color: DS.colors.textSec, marginTop: 12 }}>Nenhum relatório final publicado ainda.</div>
          <div style={{ fontSize: 12, color: DS.colors.textMuted, marginTop: 4 }}>Depois de cada ação, a Promoup publica o RF por aqui.</div>
        </Card>
      ) : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 14 }}>
          {reports.map(rf => {
            const ev = eventById[rf.event_id];
            return (
              <Card key={rf.id} style={{ padding: 20, cursor: 'pointer' }} onClick={() => setSelected(rf)}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
                  <div style={{ width: 36, height: 36, borderRadius: DS.radius.sm, background: DS.colors.primaryLight, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                    <Icon name="reports" size={16} color={DS.colors.primary} />
                  </div>
                  <Badge variant="success">RF</Badge>
                </div>
                <div style={{ fontSize: 14, fontWeight: 700, color: DS.colors.text }}>{ev?.name || '—'}</div>
                <div style={{ fontSize: 11, color: DS.colors.textMuted, marginTop: 4 }}>
                  {ev?.event_date ? new Date(ev.event_date + 'T00:00:00').toLocaleDateString('pt-BR', { day: '2-digit', month: 'short', year: 'numeric' }) : '—'}
                </div>
                <div style={{ fontSize: 11, color: DS.colors.textSec, marginTop: 8 }}>Atualizado em {new Date(rf.updated_at || rf.created_at).toLocaleDateString('pt-BR')}</div>
              </Card>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// PORTAL RF DETAIL — tela do RF com botão Baixar PDF
// ═══════════════════════════════════════════════════════════════════
function PortalRelatorioDetail({ rf, ev, onBack }) {
  const pdfRef = React.useRef(null);
  const [exporting, setExporting] = React.useState(false);
  const [msg, setMsg] = React.useState(null);
  const payload = rf.payload || {};

  const handleExportPDF = async () => {
    if (!window.renderPDF || !window.RFPDFTemplate) {
      setMsg({ type: 'error', text: 'Módulo de PDF não carregou. Recarregue a página.' }); return;
    }
    if (!pdfRef.current) {
      setMsg({ type: 'error', text: 'Template não renderizou. Aguarde 1s e tente de novo.' }); return;
    }
    setExporting(true);
    setMsg(null);
    const safeName = (ev?.name || 'evento').replace(/[^a-zA-Z0-9]/g, '_').slice(0, 40);
    const ok = await window.renderPDF(pdfRef.current, `RF_${safeName}.pdf`);
    setExporting(false);
    if (!ok) setMsg({ type: 'error', text: 'Falha ao gerar PDF. Tente de novo.' });
  };

  return (
    <div>
      <button onClick={onBack} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '6px 12px', background: '#F1F5F9', border: 'none', borderRadius: DS.radius.sm, cursor: 'pointer', fontSize: 12, fontWeight: 600, color: DS.colors.textSec, marginBottom: 16, fontFamily: 'inherit' }}>
        <Icon name="chevronLeft" size={12} color={DS.colors.textSec} /> Voltar
      </button>

      <Card style={{ padding: 32 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 22, flexWrap: 'wrap', gap: 10 }}>
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ fontSize: 12, fontWeight: 700, color: DS.colors.textMuted, textTransform: 'uppercase', letterSpacing: '0.06em' }}>Relatório Final</div>
            <div style={{ fontSize: 22, fontWeight: 800, color: DS.colors.text, marginTop: 4 }}>{ev?.name || '—'}</div>
            {ev?.event_date && <div style={{ fontSize: 12, color: DS.colors.textMuted, marginTop: 4 }}>Ação em {new Date(ev.event_date + 'T00:00:00').toLocaleDateString('pt-BR', { day: '2-digit', month: 'long', year: 'numeric' })}</div>}
          </div>
          <Btn icon="download" onClick={handleExportPDF} disabled={exporting}>{exporting ? 'Gerando PDF...' : 'Baixar PDF'}</Btn>
        </div>

        {msg && (
          <div style={{ marginBottom: 14, padding: '10px 14px', borderRadius: DS.radius.sm, background: msg.type === 'error' ? '#FEF2F2' : '#DCFCE7', color: msg.type === 'error' ? DS.colors.danger : DS.colors.success, fontSize: 12, fontWeight: 600 }}>
            {msg.text}
          </div>
        )}

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))', gap: 12, marginBottom: 22 }}>
          {[
            { label: 'Abordagens', value: payload.abordagens || '—' },
            { label: 'Leads', value: payload.leads || '—' },
            { label: 'Brindes', value: payload.brindes || '—' },
            { label: 'Vendas', value: payload.vendas || '—' },
          ].map(m => (
            <div key={m.label} style={{ padding: '14px 16px', background: '#F8FAFC', borderRadius: DS.radius.sm, border: `1px solid ${DS.colors.border}` }}>
              <div style={{ fontSize: 10, fontWeight: 700, color: DS.colors.textMuted, textTransform: 'uppercase', letterSpacing: '0.06em' }}>{m.label}</div>
              <div style={{ fontSize: 22, fontWeight: 800, color: DS.colors.primary, marginTop: 4 }}>{m.value}</div>
            </div>
          ))}
        </div>

        {payload.observacoes && (
          <div style={{ padding: '14px 18px', background: '#FFFBEB', border: '1px solid #FDE68A', borderRadius: DS.radius.sm, marginBottom: 16 }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: '#92400E', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 6 }}>Observações da equipe</div>
            <div style={{ fontSize: 13, color: DS.colors.text, lineHeight: 1.6, whiteSpace: 'pre-wrap' }}>{payload.observacoes}</div>
          </div>
        )}

        <div style={{ fontSize: 11, color: DS.colors.textMuted, marginTop: 20 }}>
          Último update: {new Date(rf.updated_at || rf.created_at).toLocaleString('pt-BR')}
        </div>
      </Card>

      {/* Template invisível pra html2pdf capturar */}
      {window.RFPDFTemplate && React.createElement(window.RFPDFTemplate, { ref: pdfRef, data: payload })}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// Helpers compartilhados
// ═══════════════════════════════════════════════════════════════════
function fileCategoryLabel(cat) {
  return {
    briefing: 'Briefing',
    arte: 'Arte',
    contrato: 'Contrato',
    nota_fiscal: 'Nota Fiscal',
    relatorio: 'Relatório',
    outro: 'Outro',
  }[cat] || cat;
}

function formatFileSize(bytes) {
  if (!bytes || bytes === 0) return '—';
  const units = ['B', 'KB', 'MB', 'GB'];
  let i = 0;
  let n = bytes;
  while (n >= 1024 && i < units.length - 1) { n /= 1024; i++; }
  return `${n.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
}

Object.assign(window, {
  ClientPortalShell,
  PortalDashboard, PortalMinhasAcoes, PortalEventDetail,
  PortalOrcamentos, PortalBudgetDetail,
  PortalAprovacoes, PortalArquivos, PortalRelatorios, PortalRelatorioDetail,
  fileCategoryLabel, formatFileSize,
});
