// mobile.jsx — Comissa no celular + frame com identidade do app const { useState: _ms, useEffect: _me, useRef: _ur } = React; // ───────────────────────────────────────────────────────────── // Ícones da tab bar (estilo SF Symbols line, 22px) // ───────────────────────────────────────────────────────────── const TabIcon = ({ kind, active = false, size = 22 }) => { const sw = active ? 2 : 1.7; const props = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: sw, strokeLinecap: 'round', strokeLinejoin: 'round', }; if (kind === 'home') return ( ); if (kind === 'vendas') return ( ); if (kind === 'metas') return ( ); if (kind === 'perfil') return ( ); return null; }; // ───────────────────────────────────────────────────────────── // MobileApp — Comissa no iPhone (tabs Início / Vendas / Metas / Perfil) // ───────────────────────────────────────────────────────────── const M_FONT = "'Geist', -apple-system, system-ui, sans-serif"; const M_MONO = "'Geist Mono', ui-monospace, monospace"; const M_VENDAS = [ { t: 'Rolex Submariner', c: 'Pedro Marques', v: 78000, com: 6260, status: '#059669', day: 'Hoje · 14:32' }, { t: 'Curso Trading Pro', c: 'Ana Souza', v: 1997, com: 599, status: '#F59E0B', day: 'Hoje · 11:08' }, { t: 'Honda Civic 2024', c: 'Vista Verde', v: 142000, com: 7100, status: '#94A3B4', day: 'Ontem' }, { t: 'iPhone 16 Pro', c: 'Mariana R.', v: 9499, com: 475, status: '#059669', day: 'Ontem' }, { t: 'Apto 2Q Pinheiros', c: 'Família Lima', v: 580000, com: 17400, status: '#F59E0B', day: '2 dias' }, { t: 'Curso Inglês', c: '24 alunos', v: 47928, com: 9586, status: '#059669', day: '3 dias' }, ]; function MobileHomeView() { // valores ao vivo (driftam devagar, tween suave na exibição) const comissao = window.useDrift({ start: 12840, min: 12700, max: 13620, stepMin: 8, stepMax: 38, upBias: 0.74, intervalMs: 2200, }); const vendas = window.useDrift({ start: 12, min: 12, max: 18, stepMin: 0.15, stepMax: 0.55, upBias: 0.62, intervalMs: 2800, }); const ticket = window.useDrift({ start: 6500, min: 6280, max: 6920, stepMin: 6, stepMax: 22, upBias: 0.55, intervalMs: 2600, }); const conv = window.useDrift({ start: 34, min: 32, max: 36, stepMin: 0.05, stepMax: 0.22, upBias: 0.5, intervalMs: 3000, }); const META = 16000; const pct = Math.min(100, (comissao / META) * 100); const falta = Math.max(0, META - comissao); const TN = window.TweenedNumber; const brl = (n) => 'R$ ' + Math.round(n).toLocaleString('pt-BR'); const brlk = (n) => 'R$ ' + (n / 1000).toFixed(1).replace('.', ',') + 'k'; return ( {/* header */}
Olá, Marina
Hoje é dia de fechar
MS
{/* card comissão */}
COMISSÃO · MAIO
↑ +24% vs abril
Meta de R$ 16.000 Math.round(n) + '%'} />
Faltam . Você consegue.
{/* mini KPIs */}
{[ { l: 'Vendas', v: vendas, fmt: (n) => Math.round(n).toString(), d: '+3 hoje' }, { l: 'Ticket', v: ticket, fmt: brlk, d: '+R$ 380' }, { l: 'Conv.', v: conv, fmt: (n) => Math.round(n) + '%', d: '−2pp' }, ].map((k) => (
{k.l}
{k.d}
))}
{/* vendas recentes */}
Vendas recentes
Ver tudo
{M_VENDAS.slice(0, 4).map((v, i) => (
{v.t}
{v.c}
R$ {v.v.toLocaleString('pt-BR')}
+R$ {v.com.toLocaleString('pt-BR')}
))}
); } function MobileVendasView() { const [filter, setFilter] = _ms('todas'); const filtros = [ { k: 'todas', l: 'Todas' }, { k: 'abertas', l: 'Abertas' }, { k: 'pagas', l: 'Pagas' }, ]; const filtered = filter === 'pagas' ? M_VENDAS.filter((v) => v.status === '#059669') : filter === 'abertas' ? M_VENDAS.filter((v) => v.status !== '#059669') : M_VENDAS; return (
Suas vendas
12 abertas · 8 pagas
{/* busca */}
{/* filtros */}
{filtros.map((f) => ( ))}
{/* lista */}
{filtered.map((v, i) => (
{v.t}
{v.c} · {v.day}
R$ {v.v.toLocaleString('pt-BR')}
+R$ {v.com.toLocaleString('pt-BR')}
))}
); } function MobileMetasView() { return (
Metas
Maio · faltam 11 dias
{[ { l: 'Comissão pessoal', cur: 12840, max: 16000, color: '#2563EB', sub: 'Faltam R$ 3.160' }, { l: 'Vendas no mês', cur: 12, max: 18, color: '#38BDF8', sub: '+3 hoje', fmt: 'count' }, { l: 'Ticket médio', cur: 6500, max: 7500, color: 'var(--green-text)', sub: '+R$ 380 vs abr' }, ].map((m) => { const pct = Math.min(100, Math.round((m.cur / m.max) * 100)); return (
{m.l}
{pct}%
{m.fmt === 'count' ? m.cur : `R$ ${m.cur.toLocaleString('pt-BR')}`} / {m.fmt === 'count' ? m.max : `R$ ${m.max.toLocaleString('pt-BR')}`}
{m.sub}
); })}
Ranking do time · maio
{[ { p: 1, n: 'Beatriz F.', v: 18420, you: false, hue: 30 }, { p: 2, n: 'Você', v: 12840, you: true, hue: 14 }, { p: 3, n: 'Carlos O.', v: 11280, you: false, hue: 30 }, { p: 4, n: 'Felipe C.', v: 9460, you: false, hue: 42 }, ].map((r, i) => (
{r.p} {r.n.split(' ').map((s) => s[0]).join('').slice(0, 2)}
{r.n}
R$ {r.v.toLocaleString('pt-BR')}
))}
); } function MobilePerfilView() { return (
MS
Marina Souza
Vendedora · time Velocittà
{[ { l: 'Vendas', v: '147' }, { l: 'Clientes', v: '62' }, { l: 'Streak', v: '9 d' }, ].map((s) => (
{s.v}
{s.l}
))}
{[ { i: IconGrid, l: 'Configurações da conta', s: 'Nome, email, senha, Face ID' }, { i: IconBolt, l: 'Notificações', s: 'Nova venda, meta, follow-up' }, { i: IconPix, l: 'Pagamento e Pix', s: 'Como sua comissão cai' }, { i: IconUsers, l: 'Time Velocittà', s: '6 vendedores · você é o #2' }, { i: IconCircle, l: 'Ajuda & contato', s: 'WhatsApp do suporte' }, { i: IconArrow, l: 'Sair', s: 'Encerrar sessão neste celular', danger: true }, ].map((row, i) => (
{row.l}
{row.s}
))}
Comissa · versão 2.4.1
); } function NovaVendaSheet({ onClose }) { return (
e.stopPropagation()} style={{ width: '100%', background: '#F0F4F8', borderRadius: '24px 24px 0 0', padding: '14px 18px 34px', animation: 'cd-modal-pop .35s cubic-bezier(.2,.8,.2,1)', }}>
Nova ação
O que você quer registrar?
{[ { i: IconMoney, l: 'Nova venda', s: 'Adicionar venda com comissão' }, { i: IconUsers, l: 'Novo cliente', s: 'Cadastrar lead ou cliente' }, { i: IconClock, l: 'Novo lembrete', s: 'Follow-up, ligação, reunião' }, { i: IconTarget, l: 'Atualizar meta', s: 'Mudar meta de maio' }, ].map((x) => ( ))}
); } const PHONE_NOTIF_PRODUCTS = [ { p: 'Rolex Submariner', c: 'Pedro M.' }, { p: 'Honda Civic 2024', c: 'Vista Verde' }, { p: 'Apto 2Q Pinheiros', c: 'Família Lima' }, { p: 'Curso Trading Pro', c: 'Ana S.' }, { p: 'iPhone 16 Pro Max', c: 'Mariana R.' }, { p: 'MacBook Air M3', c: 'Lucas O.' }, { p: 'Yamaha MT-07', c: 'Caio B.' }, { p: 'Toyota Corolla XEi', c: 'Renata F.' }, ]; function PhoneNotifCard({ note, onDismiss }) { const [drag, setDrag] = _ms({ dragging: false, dy: 0 }); const startY = _ur(0); const onPointerDown = (e) => { startY.current = e.clientY; setDrag({ dragging: true, dy: 0 }); try { e.currentTarget.setPointerCapture(e.pointerId); } catch (_) {} }; const onPointerMove = (e) => { if (!drag.dragging) return; const d = e.clientY - startY.current; setDrag({ dragging: true, dy: Math.min(0, d) }); }; const endDrag = () => { if (drag.dy < -28) { onDismiss(); } else { setDrag({ dragging: false, dy: 0 }); } }; return (
C
COMISSA
agora
Vendido! +R$ {note.valor.toLocaleString('pt-BR')}
{note.produto} · {note.cliente}
); } function PhoneNotifications({ startDelayMs = 1500, intervalMs = 35000, lifetimeMs = 20000, fadeMs = 600 }) { const [notes, setNotes] = _ms([]); _me(() => { let mounted = true; let id = 0; let interval = null; const timers = new Set(); const addTimer = (fn, ms) => { const t = setTimeout(() => { timers.delete(t); fn(); }, ms); timers.add(t); }; const spawn = () => { if (!mounted) return; id += 1; const myId = id; const it = PHONE_NOTIF_PRODUCTS[Math.floor(Math.random() * PHONE_NOTIF_PRODUCTS.length)]; const valor = Math.floor(2 + Math.random() * 78) * 1000 + Math.floor(Math.random() * 999); setNotes(n => [{ id: myId, valor, produto: it.p, cliente: it.c, fading: false }, ...n].slice(0, 2)); // inicia o fade um pouco antes do fim addTimer(() => { if (!mounted) return; setNotes(n => n.map(x => x.id === myId ? { ...x, fading: true } : x)); }, Math.max(0, lifetimeMs - fadeMs)); // remove de fato após o tempo de vida addTimer(() => { if (!mounted) return; setNotes(n => n.filter(x => x.id !== myId)); }, lifetimeMs); }; const startTimer = setTimeout(() => { if (!mounted) return; spawn(); interval = setInterval(spawn, intervalMs); }, startDelayMs); return () => { mounted = false; clearTimeout(startTimer); if (interval) clearInterval(interval); timers.forEach(clearTimeout); }; }, [startDelayMs, intervalMs, lifetimeMs, fadeMs]); const dismiss = (id) => setNotes(n => n.filter(x => x.id !== id)); return (
{notes.map(n => ( dismiss(n.id)} /> ))}
); } function ComissaMobileApp() { const [tab, setTab] = _ms('home'); const [sheet, setSheet] = _ms(null); const view = tab === 'home' ? : tab === 'vendas' ? : tab === 'metas' ? : tab === 'perfil' ? : null; return (
{view}
{[ { k: 'home', l: 'Início' }, { k: 'vendas', l: 'Vendas' }, { k: 'fab', fab: true }, { k: 'metas', l: 'Metas' }, { k: 'perfil', l: 'Perfil' }, ].map((b) => ( b.fab ? ( ) : ( ) ))}
{sheet && setSheet(null)} />}
); } // ───────────────────────────────────────────────────────────── // ComissaNotif — versão alternativa do iPhone: lock screen // com notificação de venda // ───────────────────────────────────────────────────────────── function ComissaLockScreen() { return (
{/* glow */}
{/* hora */}
terça, 13 mai
14:32
{/* notificação */}
COMISSA
agora
Vendido! +R$ 6.260
Pedro Marques fechou o Rolex Submariner. Comissão cai via Pix em 1 dia útil.
{/* notificação 2 */}
Falta R$ 3.160 pra meta de maio
Você bate fácil até sexta.
{/* bottom hint */}
Toque pra abrir o Comissa
); } // ───────────────────────────────────────────────────────────── // AppIdentity — frame "este é o app": ícone, nome, splash // ───────────────────────────────────────────────────────────── function AppIdentityFrame({ width = 720, height = 880 }) { return (
{/* topo */}
identidade · app
{/* centro ícone gigante */}
Comissa
Painel pra quem vende.
Carro, curso, relógio, imóvel. Tudo num lugar só.
{/* badges de loja */}
{[ { l1: 'Disponível na', l2: 'App Store' }, { l1: 'Disponível no', l2: 'Google Play' }, ].map((b) => (
{b.l2 === 'App Store' ? '' : '▶'}
{b.l1.toUpperCase()}
{b.l2}
))}
{/* paleta de ícones em vários tamanhos */}
Variações de ícone
{[120, 84, 60, 44, 32].map((s) => ( ))}
); } // O ícone do app — gradiente roxo→ciano com a marca de onda function AppIcon({ size = 120, dark = false, mono = false }) { const radius = size * 0.22; const bg = mono ? '#FBFCFD' : dark ? 'linear-gradient(135deg, #F4F8FB 0%, #E8F1FE 100%)' : 'linear-gradient(135deg, #2563EB 0%, #38BDF8 100%)'; return (
{/* highlight */}
); } Object.assign(window, { ComissaMobileApp, ComissaLockScreen, AppIdentityFrame, AppIcon, });