setHistory(null)}
/>
)}
>
);
}
function DistribuidoresList({ ctx, onEdit, onHistory }) {
const [search, setSearch] = useState("");
const list = ctx.state.distributors.filter(d => d.name.toLowerCase().includes(search.toLowerCase()) || d.city.toLowerCase().includes(search.toLowerCase()) || d.doc.includes(search));
const onDelete = (d) => ctx.askConfirm({
title: "Excluir distribuidor?",
message: `Excluir "${d.name}"?`,
detail: "O histórico de vendas deste cliente permanecerá nos relatórios.",
danger: true,
onConfirm: () => ctx.actions.deleteDistributor(d.id),
});
return (
<>
} label="Distribuidores ativos" value={ctx.state.distributors.length + ""} sub="Pessoa jurídica" />
} label="Compras (mês)" value={ctx.state.distributors.reduce((s,d)=>s+d.totals.compras,0)+""} sub="Vendas registradas" />
} label="Volume (mês)" value={fmtBRL(ctx.state.distributors.reduce((s,d)=>s+d.totals.valor,0))} trend="+12,8%" trendDir="up" />
} label="Ticket médio" value={fmtBRL(ctx.state.distributors.length ? ctx.state.distributors.reduce((s,d)=>s+d.totals.valor,0) / Math.max(1, ctx.state.distributors.reduce((s,d)=>s+d.totals.compras,0)) : 0)} sub="Por compra" />
setSearch(e.target.value)} />
}
>
{list.length === 0 ? (
{search ? "Nenhum resultado" : "Nenhum distribuidor"}
{search ? "Tente outro termo de busca." : "Clique em \"Novo distribuidor\" para cadastrar o primeiro."}
) : (
| Razão social |
CNPJ |
Cidade |
Pagamento |
Compras |
Volume |
|
{list.map(d => (
onHistory(d)}>
{d.name.split(" ").map(w => w[0]).slice(0,2).join("")}
|
{d.doc} |
{d.city} |
{d.payment} |
{d.totals.compras} |
{fmtBRL(d.totals.valor)} |
e.stopPropagation()}>
|
))}
)}
>
);
}
function PicolezeirosList({ ctx, onEdit, onHistory }) {
const onDelete = (p) => ctx.askConfirm({
title: "Excluir picolezeiro?",
message: `Excluir "${p.name}"?`,
detail: "O histórico de saídas permanecerá nos relatórios.",
danger: true,
onConfirm: () => ctx.actions.deletePicolezeiro(p.id),
});
if (ctx.state.picolezeiros.length === 0) {
return (
Nenhum picolezeiro cadastrado
Cadastre picolezeiros para começar a registrar saídas e consignações.
);
}
return (
{ctx.state.picolezeiros.map(p => {
const beaches = p.beaches.map(bid => ctx.state.beaches.find(b => b.id === bid)?.name).filter(Boolean);
return (
{p.photo}
{p.name}
{p.phone}
{p.since ? "Desde " + fmtDate(p.since) : ""}
{p.commission}%
Praias atendidas
{beaches.length > 0 ? beaches.map((b,i) => {b}) : Nenhuma praia atribuída}
{p.totals.saidas}Saídas
{fmtBRL(p.totals.vendido)}Faturado
{fmtBRL(p.totals.comissao)}Comissão
);
})}
);
}
function DistribuidorModal({ initial, onClose, onSave }) {
const [type, setType] = useState(initial?.type || "PJ");
const [name, setName] = useState(initial?.name || "");
const [doc, setDoc] = useState(initial?.doc || "");
const [ie, setIe] = useState(initial?.ie || "");
const [phone, setPhone] = useState(initial?.phone || "");
const [email, setEmail] = useState(initial?.email || "");
const [cep, setCep] = useState(initial?.cep || "");
const [city, setCity] = useState(initial?.city || "");
const [address, setAddress] = useState(initial?.address || "");
const [payment, setPayment] = useState(initial?.payment || "PIX");
const [err, setErr] = useState({});
const submit = () => {
const e = {};
if (!name.trim()) e.name = "Informe a razão social";
if (!doc.trim()) e.doc = "Informe o documento";
if (!phone.trim()) e.phone = "Informe o WhatsApp";
setErr(e);
if (Object.keys(e).length) return;
onSave({ type, name: name.trim(), doc, ie, phone, email, cep, city, address, payment });
};
return (
>}
>
setName(e.target.value)} placeholder={type === "PJ" ? "Empresa LTDA" : "Nome completo"} autoFocus />
setDoc(type === "PJ" ? maskCNPJ(e.target.value) : maskCPF(e.target.value))} placeholder={type === "PJ" ? "00.000.000/0000-00" : "000.000.000-00"} />
{type === "PJ" && setIe(e.target.value)} placeholder="000.000.000.000" />}
{type === "PF" && }
setPhone(maskPhone(e.target.value))} placeholder="(00) 00000-0000" />
setEmail(e.target.value)} placeholder="contato@..." />
setCep(maskCEP(e.target.value))} placeholder="00000-000" />
setCity(e.target.value)} placeholder="Salvador / BA" />
setAddress(e.target.value)} placeholder="Rua, número, bairro" />
);
}
function PicolezeiroModal({ initial, beaches, defaultCommission, onClose, onSave }) {
const [name, setName] = useState(initial?.name || "");
const [cpf, setCpf] = useState(initial?.cpf || "");
const [rg, setRg] = useState(initial?.rg || "");
const [phone, setPhone] = useState(initial?.phone || "");
const [address, setAddress] = useState(initial?.address || "");
const [commission, setCommission] = useState(initial?.commission ?? defaultCommission);
const [selBeaches, setSelBeaches] = useState(initial?.beaches || []);
const [since, setSince] = useState(initial?.since || "");
const [err, setErr] = useState({});
const submit = () => {
const e = {};
if (!name.trim()) e.name = "Informe o nome";
if (!cpf.trim()) e.cpf = "Informe o CPF";
if (!phone.trim()) e.phone = "Informe o WhatsApp";
if (selBeaches.length === 0) e.beaches = "Selecione ao menos uma praia";
setErr(e);
if (Object.keys(e).length) return;
const photo = name.trim().split(" ").map(w => w[0]).slice(0,2).join("").toUpperCase();
onSave({
name: name.trim(), cpf, rg, phone, address,
commission: parseInt(commission || 0, 10), beaches: selBeaches, photo, since,
});
};
return (
>}
>
setName(e.target.value)} placeholder="Nome do picolezeiro" autoFocus />
setCpf(maskCPF(e.target.value))} placeholder="000.000.000-00" />
setRg(e.target.value)} placeholder="00.000.000-0" />
setPhone(maskPhone(e.target.value))} placeholder="(00) 00000-0000" />
setSince(e.target.value)} placeholder="AAAA-MM-DD" />
setAddress(e.target.value)} placeholder="Rua, número, bairro, cidade" />
setCommission(e.target.value.replace(/\D/g,""))} />
{beaches.length === 0 ? (
Cadastre praias em Configurações antes.
) : beaches.map(b => {
const on = selBeaches.includes(b.id);
return (
);
})}
);
}
window.Clientes = Clientes;