// ============================================================
// JuFlow — Serviços + Pacotes
// ============================================================
const { useState: useStSV } = React;
const USV = window.JFUtils;
const SVC_COLORS = ['#FF6B35','#7C3AED','#0891B2','#059669','#D97706','#DB2777','#3B82F6','#14B8A6','#EF4444','#A855F7'];
const EMPTY_SVC = { id:null, name:'', category:'', price:100, duration:60, description:'', professionals:[], color:'#FF6B35' };
const Servicos = ({ addToast }) => {
const [tab, setTab] = useStSV('servicos');
const [search, setSearch] = useStSV('');
const [catFilter, setCatFilter] = useStSV('all');
const [extraSvcs, setExtraSvcs] = useStSV([]);
const [removed, setRemoved] = useStSV([]);
const [modal, setModal] = useStSV({ open:false, mode:'create', form: EMPTY_SVC });
const [confirm, setConfirm] = useStSV({ open:false, svc:null });
const allServices = [...window.JF.SERVICES, ...extraSvcs].filter(s => !removed.includes(s.id));
const categories = [...new Set(allServices.map(s => s.category))];
const filtered = allServices.filter(s => {
const matchSearch = !search || s.name.toLowerCase().includes(search.toLowerCase());
const matchCat = catFilter === 'all' || s.category === catFilter;
return matchSearch && matchCat;
});
const openCreate = () => setModal({ open:true, mode:'create', form: { ...EMPTY_SVC, color: SVC_COLORS[Math.floor(Math.random()*SVC_COLORS.length)] } });
const openEdit = (svc) => setModal({ open:true, mode:'edit', form: { ...EMPTY_SVC, ...svc, professionals: [...(svc.professionals || [])] } });
const closeModal = () => setModal(m => ({ ...m, open:false }));
const setForm = (k, v) => setModal(m => ({ ...m, form: { ...m.form, [k]: v } }));
const toggleProf = (id) => setForm('professionals', modal.form.professionals.includes(id) ? modal.form.professionals.filter(p => p !== id) : [...modal.form.professionals, id]);
const saveSvc = () => {
const f = modal.form;
if (!f.name.trim() || !f.category.trim()) { addToast('Preencha nome e categoria.', 'warning'); return; }
if (!f.price || f.price <= 0) { addToast('Informe um valor válido.', 'warning'); return; }
if (!f.duration || f.duration <= 0) { addToast('Informe a duração.', 'warning'); return; }
if (modal.mode === 'create') {
const id = Math.max(0, ...allServices.map(s=>s.id)) + 1;
setExtraSvcs(arr => [...arr, { ...f, id }]);
addToast(`Serviço "${f.name}" criado!`, 'success');
} else {
const isFromBase = window.JF.SERVICES.some(s => s.id === f.id);
if (isFromBase) {
Object.assign(window.JF.SERVICES.find(s=>s.id===f.id), f);
setExtraSvcs(arr => [...arr]);
} else {
setExtraSvcs(arr => arr.map(s => s.id === f.id ? { ...s, ...f } : s));
}
addToast('Serviço atualizado!', 'success');
}
closeModal();
};
const duplicateSvc = (svc) => {
const id = Math.max(0, ...allServices.map(s=>s.id)) + 1;
setExtraSvcs(arr => [...arr, { ...svc, id, name: svc.name + ' (cópia)' }]);
addToast(`Cópia de "${svc.name}" criada.`, 'success');
};
const askDelete = (svc) => setConfirm({ open:true, svc });
const confirmDelete = () => {
setRemoved(arr => [...arr, confirm.svc.id]);
addToast(`Serviço "${confirm.svc.name}" excluído.`, 'success');
};
return (
s+sv.price,0)/allServices.length : 0)} icon="DollarSign" color="#059669" subtitle="por serviço" />
{tab === 'servicos' && (
<>
setCatFilter('all')} style={{ padding:'5px 12px', borderRadius:20, fontSize:12, fontWeight:600, cursor:'pointer', background:catFilter==='all'?'#0A1F44':'#F3F4F6', color:catFilter==='all'?'#fff':'#6B7280', border:'none' }}>Todos
{categories.map(c => (
setCatFilter(c)} style={{ padding:'5px 12px', borderRadius:20, fontSize:12, fontWeight:600, cursor:'pointer', background:catFilter===c?'#0A1F44':'#F3F4F6', color:catFilter===c?'#fff':'#6B7280', border:'none' }}>{c}
))}
Novo Serviço
{filtered.map(svc => {
const profs = (svc.professionals || []).map(id => USV.getProfessional(id)).filter(Boolean);
return (
{ e.currentTarget.style.boxShadow='0 8px 24px rgba(10,31,68,.12)'; e.currentTarget.style.transform='translateY(-2px)'; }}
onMouseLeave={e=>{ e.currentTarget.style.boxShadow='0 2px 8px rgba(10,31,68,.06)'; e.currentTarget.style.transform='none'; }}
>
}
items={[
{label:'Editar',icon:'Edit2',onClick:()=>openEdit(svc)},
{label:'Duplicar',icon:'Copy',onClick:()=>duplicateSvc(svc)},
'divider',
{label:'Excluir',icon:'Trash2',onClick:()=>askDelete(svc),danger:true},
]}
/>
{svc.description}
{USV.formatCurrency(svc.price)}
valor
{svc.duration}min
duração
Profissionais
{profs.length === 0 ? (
Nenhum profissional associado
) : profs.map(p => (
))}
);
})}
>
)}
{tab === 'pacotes' && (
{window.JF.PACKAGES.map(pkg => {
const svc = USV.getService(pkg.serviceId);
const discount = Math.round((1 - pkg.price/pkg.originalPrice)*100);
return (
{pkg.description}
{pkg.totalSessions}
sessões
{USV.formatCurrency(pkg.price)}
{USV.formatCurrency(pkg.originalPrice)}
{svc &&
Serviço: {svc.name}
}
addToast('Edição de pacote em breve!','info')}>Editar
addToast('Pacote vendido com sucesso!','success')}>Vender
);
})}
{ e.currentTarget.style.borderColor='#FF6B35'; e.currentTarget.style.background='#FFF0EA'; }}
onMouseLeave={e=>{ e.currentTarget.style.borderColor='#D1D5DB'; e.currentTarget.style.background='transparent'; }}
onClick={()=>addToast('Modal de novo pacote em breve!','info')}
>
Novo Pacote
)}
{/* Service form modal */}
Cancelar
Salvar
>}
>
{/* Color + Name + Category */}
Cor
{SVC_COLORS.slice(0,5).map(c => (
setForm('color',c)} style={{ width:28, height:28, borderRadius:7, background:c, border: modal.form.color === c ? '3px solid #0A1F44' : '3px solid transparent', cursor:'pointer', boxShadow:'0 1px 3px rgba(0,0,0,.15)' }} />
))}
{SVC_COLORS.slice(5).map(c => (
setForm('color',c)} style={{ width:28, height:28, borderRadius:7, background:c, border: modal.form.color === c ? '3px solid #0A1F44' : '3px solid transparent', cursor:'pointer', boxShadow:'0 1px 3px rgba(0,0,0,.15)' }} />
))}
setForm('name',v)} icon="Scissors" required autoFocus style={{ flex:1 }} />
{/* Category (with suggestions) */}
setForm('category',v)} icon="Tag" required placeholder="Ex: Massagem, Estética..." />
{categories.length > 0 && (
Sugestões:
{categories.map(c => (
setForm('category',c)} style={{
padding:'2px 9px', borderRadius:20, fontSize:11, fontWeight:600, cursor:'pointer',
background: modal.form.category === c ? '#0A1F44' : '#F3F4F6',
color: modal.form.category === c ? '#fff' : '#6B7280',
border:'none',
}}>{c}
))}
)}
{/* Price + Duration */}
Valor (R$) *
setForm('price', Number(e.target.value))}
style={{ padding:'9px 12px', borderRadius:8, fontSize:13, border:'1.5px solid #E2E8F0', outline:'none', color:'#0A1F44', background:'#fff' }} />
Duração (minutos) *
setForm('duration', Number(e.target.value))}
style={{ padding:'9px 12px', borderRadius:8, fontSize:13, border:'1.5px solid #E2E8F0', outline:'none', color:'#0A1F44', background:'#fff' }} />
{/* Description */}
{/* Delete confirm */}
setConfirm(c => ({ ...c, open:false }))}
onConfirm={confirmDelete}
title="Excluir serviço"
message={confirm.svc ? `Tem certeza que deseja excluir o serviço "${confirm.svc.name}"? Atendimentos passados serão preservados, mas o serviço não estará mais disponível para novos agendamentos.` : ''}
confirmLabel="Excluir"
danger
/>
);
};
Object.assign(window, { Servicos });