// Admin panel — pedidos, produtos, pagamentos, galeria, perfis, agenda. Login com usuário e senha. const { useState: adUseState, useRef: adUseRef } = React; const ADMIN_SESSION = 'aa-admin-session'; function Admin({open, onClose, showToast}) { const store = useStore(); const acc = store.settings.account; const [authed, setAuthed] = adUseState(() => { try { return sessionStorage.getItem(ADMIN_SESSION) === '1'; } catch(e){ return false; } }); const [user, setUser] = adUseState(''); const [pass, setPass] = adUseState(''); const [err, setErr] = adUseState(false); const [tab, setTab] = adUseState('painel'); if (!open) return null; const login = (e) => { e.preventDefault(); const ok = user.trim().toLowerCase() === (acc.user||'').toLowerCase() && pass === acc.pass; if (ok) { setAuthed(true); setErr(false); try { sessionStorage.setItem(ADMIN_SESSION,'1'); } catch(x){} } else { setErr(true); setPass(''); } }; const logout = () => { setAuthed(false); try { sessionStorage.removeItem(ADMIN_SESSION); } catch(x){} onClose(); }; if (!authed) { return (
Área restrita

Painel do Studio

Entre com seu usuário e senha.

{setUser(e.target.value);setErr(false)}} autoComplete="username" placeholder="angella" autoFocus/> {setPass(e.target.value);setErr(false)}} autoComplete="current-password" placeholder="••••••••"/> {err &&
Usuário ou senha incorretos.
}
Acesso inicial: {acc.user} / {acc.pass} — troque em Pagamentos → Acesso.
); } const TABS = [ {id:'painel', label:'Painel'}, {id:'pedidos', label:'Pedidos'}, {id:'produtos', label:'Produtos'}, {id:'pagamentos',label:'Pagamentos'}, {id:'galeria', label:'Galeria'}, {id:'cinema', label:'Abertura'}, {id:'instagram', label:'Instagram'}, {id:'perfis', label:'Perfis'}, {id:'wellness', label:'Massoterapia'}, {id:'agenda', label:'Agendamentos'}, ]; return (
{tab==='painel' && } {tab==='pedidos' && } {tab==='produtos' && } {tab==='pagamentos' && } {tab==='galeria' && } {tab==='cinema' && } {tab==='instagram'&& } {tab==='perfis' && } {tab==='wellness' && } {tab==='agenda' && }
); } /* ——————————————————————— Painel ——————————————————————— */ function Painel({store, go}) { const receita = store.bookings.filter(b=>b.status!=='cancelado').reduce((a,b)=>a+(b.price||0),0); const orders = store.orders || []; const vendas = orders.filter(o=>o.status!=='cancelado').reduce((a,o)=>a+(o.total||0),0); const cards = [ {k:'Agendamentos ativos', v:store.bookings.filter(b=>b.status!=='cancelado').length, s:'esta semana'}, {k:'Receita prevista', v:brl(receita), s:'serviços reservados'}, {k:'Vendas na loja', v:brl(vendas), s:`${orders.length} pedido${orders.length===1?'':'s'}`}, {k:'Produtos na loja', v:store.products.length, s:`${store.products.reduce((a,p)=>a+(+p.stock||0),0)} itens em estoque`}, ]; const viaIA = store.bookings.filter(b=>b.via==='ia').length; return ( <>
{cards.map(c => (
{c.k}
{c.v}
{c.s}
))}

Próximos atendimentos

{store.bookings.slice(0,4).map(b => (
{b.client}
{b.service} · {b.pro}
{fmtDate(b.date)} · {b.time}
{b.status}
))}
Concierge IA

{viaIA} de {store.bookings.length} reservas chegaram pela Bela. Ela recomenda serviços, sugere produtos e agenda sozinha — 24h por dia.

); } /* ——————————————————————— Pedidos ——————————————————————— */ const ORDER_STATUS = ['aguardando','pago','enviado','entregue','cancelado']; function Pedidos({store, showToast}) { const orders = store.orders || []; const [f, setF] = adUseState('todos'); const rows = orders.filter(o => f==='todos' || o.status===f); const setStatus = (id, status) => { setStore(s => ({orders: s.orders.map(o => o.id===id ? {...o, status} : o)})); showToast('Pedido atualizado'); }; const wa = (o) => `https://wa.me/55${o.customer.phone.replace(/^55/,'')}?text=${encodeURIComponent(`Olá ${o.customer.name.split(' ')[0]}! Sobre seu pedido ${o.code} no Angella Barros Studio:`)}`; return ( <>
{['todos',...ORDER_STATUS].map(s => )}
{rows.map(o => ( ))} {!rows.length && }
PedidoClienteItensValorPagamentoEntregaStatus
{o.code}
{fmtDate(o.createdAt.slice(0,10))}
{o.customer.name}
{o.customer.phone}
{o.items.map(i=>`${i.qty}× ${i.name}`).join(', ')} {brlc(o.total)} {o.method==='pix'?'Pix':`Cartão ${o.installments}x`} {o.delivery==='retirada' ? 'Retirada' : `${o.address?.street||''}, ${o.address?.num||''} · ${o.address?.bairro||''}`}
Nenhum pedido {f==='todos'?'ainda':`com status "${f}"`}.
); } /* ——————————————————————— Pagamentos ——————————————————————— */ function Pagamentos({store, showToast}) { const cfg = store.settings; const upd = (group, patch) => setStore(s => ({settings: {...s.settings, [group]: {...s.settings[group], ...patch}}})); const [pass2, setPass2] = adUseState(''); const pixOk = !!cfg.pix.key; return ( <>

Pix

{pixOk?'ativo':'sem chave'}

O QR Code do checkout é gerado a partir desta chave — o dinheiro cai direto na sua conta.

upd('pix',{key:e.target.value})} placeholder="+5592999990000"/> upd('pix',{merchant:e.target.value})}/> upd('pix',{city:e.target.value})}/>
Aceitar Pix no checkout

Cartão de crédito

{cfg.card.publicKey?'conectado':'demonstração'}

Escolha a maquininha digital e cole a chave pública. A chave secreta fica só no servidor, nunca aqui.

upd('card',{publicKey:e.target.value})} placeholder="APP_USR-…"/> upd('card',{maxInstallments:e.target.value})}/> upd('card',{minInstallment:e.target.value})}/> upd('card',{feeNote:e.target.value})}/>
Aceitar cartão no checkout

Entrega e contato

upd('delivery',{localFee:e.target.value})}/> upd('delivery',{freeAbove:e.target.value})}/> upd('delivery',{address:e.target.value})}/> upd('contact',{whatsapp:e.target.value})} placeholder="5592999990000"/> upd('contact',{email:e.target.value})}/>
Permitir retirada no studio
Entregar em Parintins

Acesso ao painel

Troque a senha inicial antes de publicar o site.

upd('account',{name:e.target.value})}/> upd('account',{user:e.target.value})}/> upd('account',{pass:e.target.value})}/> setPass2(e.target.value)}/>
); } /* ——————————————————————— Produtos ——————————————————————— */ const EMPTY_PRODUCT = {name:'', cat:'', priceN:'', tag:'', tagClass:'', tone:'wine', style:0, img:null, stock:'', desc:''}; function Produtos({store, showToast}) { const [editing, setEditing] = adUseState(null); const fileRef = adUseRef(null); const save = () => { if (!editing.name.trim()) { showToast('Dê um nome ao produto'); return; } const p = {...editing, priceN: Number(editing.priceN)||0, stock: Number(editing.stock)||0}; setStore(s => editing.id ? ({products: s.products.map(x => x.id===editing.id ? p : x)}) : ({products: [...s.products, {...p, id: uid('p')}]})); showToast(editing.id ? 'Produto atualizado' : 'Produto adicionado à loja'); setEditing(null); }; const remove = (id) => { if (!confirm('Remover este produto da loja?')) return; setStore(s => ({products: s.products.filter(p => p.id !== id)})); showToast('Produto removido'); }; const pickImage = async (e) => { const f = e.target.files?.[0]; if (!f) return; const url = await fileToDataURL(f, 800); setEditing(x => ({...x, img:url})); }; return ( <>
{store.products.map(p => (
{p.img ? {p.name}/ :
}
{p.cat || '—'}
{p.name}
{brl(p.priceN)} {p.stock||0} un.
))}
{editing && ( setEditing(null)} onSave={save}>
setEditing({...editing, name:e.target.value})} placeholder="Ex.: Máscara Reconstrução"/> setEditing({...editing, cat:e.target.value})} placeholder="Tratamento Capilar"/> setEditing({...editing, priceN:e.target.value})} placeholder="198"/> setEditing({...editing, stock:e.target.value})} placeholder="12"/> setEditing({...editing, tag:e.target.value})} placeholder="Novo, Best-seller…"/>