function ScreenNewQuotation({ setRoute }) {
const { CATEGORIES, BRL } = window.SCP;
const [step, setStep] = useState(1);
const [title, setTitle] = useState('Cotação Semanal · ' + new Date().toLocaleDateString('pt-BR', { day: '2-digit', month: 'short' }));
const [items, setItems] = useState([]); // [{ id (uuid), qty }]
const [selectedSups, setSelectedSups] = useState([]); // [uuid]
const [deadline, setDeadline] = useState(new Date(Date.now() + 2 * 86400000).toISOString().slice(0, 16));
const [obs, setObs] = useState('Pagamento 28 dias · Entrega na Rua Carlos Gomes, 1244 · Receber entre 8h e 11h');
const [channels, setChannels] = useState({ email: true, whats: true, link: true });
const [aiSuggest, setAiSuggest] = useState(true);
const [showProductPicker, setShowProductPicker] = useState(false);
const [allProducts, setAllProducts] = useState([]);
const [allSuppliers, setAllSuppliers] = useState([]);
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
// Aliases p/ produto vindo do banco (compatibilidade com a JSX existente)
const adaptProduct = (p) => ({
...p,
lastPrice: Number(p.last_price ?? p.lastPrice ?? 0),
min: p.min_stock ?? p.min ?? 0,
photo: p.photo_url ?? p.photo ?? null,
supplier: p.preferred_supplier?.name || p.supplier || '—',
});
useEffect(() => {
let mounted = true;
(async () => {
try {
const [prods, sups] = await Promise.all([
window.scpDb.products.list(),
window.scpDb.suppliers.list(),
]);
if (!mounted) return;
setAllProducts(prods.map(adaptProduct));
setAllSuppliers(sups);
} catch (e) {
if (mounted) window.scpToast('Falha ao carregar dados', { kind: 'coral', sub: e.message });
} finally {
if (mounted) setLoading(false);
}
})();
return () => { mounted = false; };
}, []);
const PRODUCTS = allProducts;
const SUPPLIERS = allSuppliers;
const itemsFull = items.map(it => {
const p = PRODUCTS.find(p => p.id === it.id);
return p ? { ...p, qty: it.qty } : null;
}).filter(Boolean);
const handleSubmit = async () => {
if (!title.trim()) { window.scpToast('Defina um título', { kind: 'amber' }); setStep(1); return; }
if (items.length === 0) { window.scpToast('Adicione produtos', { kind: 'amber' }); setStep(2); return; }
if (selectedSups.length === 0) { window.scpToast('Selecione fornecedores', { kind: 'amber' }); setStep(3); return; }
setSubmitting(true);
try {
const created = await window.scpDb.quotations.create({
title: title.trim(),
deadline: deadline ? new Date(deadline).toISOString() : null,
obs: obs.trim() || null,
items: itemsFull.map(p => ({
product_id: p.id,
name: p.name,
unit: p.unit,
qty: p.qty,
last_price: p.lastPrice,
})),
supplier_ids: selectedSups,
});
window.scpToast('Cotação enviada', { kind: 'emerald', sub: `${created.code} · ${selectedSups.length} fornecedores` });
setTimeout(() => setRoute('quotations'), 600);
} catch (e) {
window.scpToast('Erro ao criar cotação', { kind: 'coral', sub: e.message });
} finally {
setSubmitting(false);
}
};
return (
} onClick={() => setRoute('quotations')}>Cancelar
{[
{ n: 1, t: 'Informações', i:
},
{ n: 2, t: 'Produtos', i: },
{ n: 3, t: 'Fornecedores', i: },
{ n: 4, t: 'Envio & revisão', i: },
].map(s => (
s.n && 'done')}>
{step > s.n ? : s.n}
{s.t}
))}
Resumo
Itens{items.length}
Fornecedores{selectedSups.length}
Estimativa{BRL(itemsFull.reduce((s, i) => s + i.lastPrice * i.qty, 0))}
Prazo{deadline.split('T')[0].split('-').reverse().join('/')}
{step === 1 && (
)}
{step === 2 && (
Produtos da cotação
} onClick={() => setShowProductPicker(true)}>Adicionar produto
| Produto |
Categoria |
Estoque |
Mín. |
Quantidade |
Últ. preço |
|
{itemsFull.map(p => {
const cat = CATEGORIES.find(c => c.key === p.cat);
return (
|
{p.name}
{p.code} · {p.unit}
|
{cat.label} |
{p.stock} |
{p.min} |
setItems(items.map(i => i.id === p.id ? { ...i, qty: parseInt(e.target.value) || 1 } : i))} />
|
{BRL(p.lastPrice)} |
} size="sm" onClick={() => setItems(items.filter(i => i.id !== p.id))}/> |
);
})}
{showProductPicker && (
setShowProductPicker(false)}
onAdd={(ids) => {
setItems([...items, ...ids.filter(id => !items.find(i => i.id === id)).map(id => ({ id, qty: 10 }))]);
setShowProductPicker(false);
}}
excluded={items.map(i => i.id)}
products={PRODUCTS}
/>
)}
}>Importar Excel
}>Importar XML de NFe
}>Ler código de barras
}>Usar template salvo
)}
{step === 3 && (
Selecionar fornecedores
{selectedSups.length} selecionados
{CATEGORIES.slice(0, 6).map(c => (
))}
{SUPPLIERS.filter(s => s.status !== 'bloqueado' && s.status !== 'Bloqueado').map(s => {
const checked = selectedSups.includes(s.id);
return (
);
})}
)}
{step === 4 && (
Revisão e envio
Confira tudo antes de disparar. Os fornecedores receberão um link único, sem necessidade de cadastro.
Informações
Título{title}
Prazo limite{deadline.replace('T', ' às ').split('-').reverse().join('/')}
Comprador{window.SCP.COMPANY.owner}
{obs}
Itens ({items.length})
{itemsFull.slice(0, 6).map(p => (
{p.name}
{p.qty} {p.unit}
))}
{itemsFull.length > 6 &&
+ {itemsFull.length - 6} itens}
Fornecedores ({selectedSups.length})
{selectedSups.slice(0, 6).map(sid => {
const s = SUPPLIERS.find(x => x.id === sid);
return
;
})}
{selectedSups.length > 6 &&
+ {selectedSups.length - 6}}
Canais de envio
)}
setStep(Math.max(1, step - 1))} disabled={step === 1 || submitting}>Voltar
{step < 4 ? (
} onClick={() => setStep(step + 1)} disabled={loading}>Continuar
) : (
} onClick={handleSubmit} disabled={submitting || selectedSups.length === 0 || items.length === 0}>
{submitting ? 'Enviando…' : `Enviar para ${selectedSups.length} fornecedor${selectedSups.length === 1 ? '' : 'es'}`}
)}
);
}
function ProductPicker({ onClose, onAdd, excluded = [], products = [] }) {
const { CATEGORIES } = window.SCP;
const [q, setQ] = useState('');
const [sel, setSel] = useState([]);
const [cat, setCat] = useState('todas');
const list = products.filter(p =>
!excluded.includes(p.id) &&
(cat === 'todas' || p.cat === cat) &&
(!q || p.name.toLowerCase().includes(q.toLowerCase()))
);
return (
e.stopPropagation()}>
Adicionar produtos
} size="sm" onClick={onClose}/>
} placeholder="Buscar por nome ou código…" value={q} onChange={e => setQ(e.target.value)} />
{list.map(p => {
const c = CATEGORIES.find(c => c.key === p.cat);
return (
);
})}
{sel.length} selecionados
Cancelar
onAdd(sel)}>Adicionar {sel.length || ''}
);
}
(function() {
if (document.getElementById('scp-wizard-css')) return;
const s = document.createElement('style'); s.id = 'scp-wizard-css';
s.textContent = `
.scp-wizard { display: grid; grid-template-columns: 260px 1fr; min-height: 640px; padding: 0; overflow: hidden; }
.scp-wizard-rail { padding: 20px 18px; border-right: 1px solid var(--border-soft); background: oklch(0.17 0.012 250); display: flex; flex-direction: column; gap: 22px; }
.scp-wizard-steps { display: flex; flex-direction: column; gap: 4px; position: relative; }
.scp-wizard-steps::before {
content: ""; position: absolute; left: 16px; top: 14px; bottom: 14px;
width: 1px; background: var(--border);
}
.wstep { display: flex; align-items: center; gap: 12px; padding: 6px 0; position: relative; z-index: 1; }
.wstep .num { width: 26px; height: 26px; border-radius: 999px; display: grid; place-items: center; background: var(--surface-3); border: 1px solid var(--border); color: var(--fg-3); font-size: 12px; font-weight: 700; font-family: 'JetBrains Mono', monospace; }
.wstep.current .num { background: oklch(0.40 0.10 160); border-color: oklch(0.55 0.12 160); color: white; box-shadow: 0 0 0 4px oklch(0.40 0.10 160 / .2); }
.wstep.done .num { background: oklch(0.30 0.05 160); border-color: oklch(0.50 0.10 160); color: oklch(0.92 0.14 160); }
.wstep .t { font-size: 13px; color: var(--fg-3); font-weight: 500; }
.wstep.current .t, .wstep.done .t { color: var(--fg); font-weight: 600; }
.scp-wizard-summary { margin-top: auto; padding: 14px; border-radius: 12px; background: var(--surface); border: 1px solid var(--border-soft); display: flex; flex-direction: column; gap: 6px; }
.scp-wizard-summary h4 { margin: 0 0 4px; font-size: 11px; color: var(--fg-3); text-transform: uppercase; letter-spacing: .1em; font-weight: 700; }
.scp-wizard-summary .row { display: flex; justify-content: space-between; font-size: 12.5px; padding: 3px 0; }
.scp-wizard-summary .row span { color: var(--fg-3); }
.scp-wizard-summary .row strong { color: var(--fg); font-weight: 600; }
.scp-wizard-summary .row strong.num { font-family: 'JetBrains Mono', monospace; }
.scp-wizard-body { padding: 28px 28px 100px; min-width: 0; position: relative; }
.scp-wizard-body h3 { margin: 0 0 6px; font-size: 22px; font-weight: 600; letter-spacing: -0.02em; }
.scp-wizard-body > p { margin: 0 0 22px; color: var(--fg-3); font-size: 13.5px; }
.scp-step-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 18px; }
.scp-step-sub { font-size: 12px; color: var(--fg-3); }
.scp-wfield-list { display: flex; flex-direction: column; gap: 14px; max-width: 720px; }
.scp-field { display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 0; }
.scp-field > span { font-size: 12px; color: var(--fg-3); font-weight: 600; }
.scp-field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.scp-select { background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px; padding: 9px 12px; color: var(--fg); font-size: 13.5px; outline: none; }
.scp-textarea { background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; color: var(--fg); font: inherit; font-size: 13.5px; outline: none; resize: vertical; }
.scp-textarea:focus, .scp-select:focus { border-color: oklch(0.55 0.10 160); }
.scp-suggest { display: grid; grid-template-columns: 32px 1fr auto; gap: 12px; align-items: center; padding: 14px; border-radius: 12px;
background: oklch(0.22 0.04 160 / .35); border: 1px solid oklch(0.34 0.06 160 / .55); }
.scp-suggest-glyph { width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center; background: oklch(0.42 0.10 160); color: white; }
.scp-suggest strong { color: var(--fg); display: block; font-size: 13px; font-weight: 600; }
.scp-suggest p { margin: 2px 0 0; color: var(--fg-2); font-size: 12.5px; line-height: 1.4; }
.scp-qty { display: inline-flex; align-items: center; background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.scp-qty button { width: 26px; height: 28px; color: var(--fg-2); font-weight: 600; }
.scp-qty button:hover { background: var(--surface-3); color: var(--fg); }
.scp-qty input { width: 48px; height: 28px; text-align: center; background: transparent; border: 0; outline: 0; color: var(--fg); font-family: 'JetBrains Mono', monospace; font-size: 12.5px; font-weight: 600; }
.scp-bulk-actions { display: flex; gap: 6px; margin-top: 14px; flex-wrap: wrap; }
.scp-cat-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.scp-cat-chips button { cursor: pointer; }
.scp-cat-chips button:hover { background: var(--surface-3); }
.scp-sup-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.scp-sup-card {
display: grid; grid-template-columns: 24px 36px 1fr auto; gap: 12px; padding: 12px 14px;
border-radius: 12px; background: var(--surface); border: 1px solid var(--border-soft);
text-align: left; align-items: center; transition: background .12s, border-color .12s;
cursor: pointer; position: relative;
}
.scp-sup-card:hover { background: var(--surface-2); border-color: var(--border); }
.scp-sup-card.checked { background: oklch(0.22 0.05 160 / .55); border-color: oklch(0.40 0.08 160); }
.scp-sup-card .check { width: 18px; height: 18px; border-radius: 5px; border: 1.5px solid var(--border-strong); display: grid; place-items: center; color: white; }
.scp-sup-card.checked .check { background: oklch(0.55 0.13 160); border-color: oklch(0.55 0.13 160); }
.scp-sup-card .info { min-width: 0; }
.scp-sup-card .info .n { display: block; font-weight: 600; font-size: 13px; color: var(--fg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.scp-sup-card .info .c { display: block; font-size: 11px; color: var(--fg-3); margin-top: 2px; }
.scp-sup-card .score { display: flex; flex-direction: column; align-items: flex-end; gap: 0; line-height: 1.1; }
.scp-sup-card .score .sc { font-size: 16px; font-weight: 700; color: oklch(0.92 0.14 160); font-family: 'JetBrains Mono', monospace; }
.scp-sup-card .score .sl { font-size: 10px; color: var(--fg-4); text-transform: uppercase; letter-spacing: .1em; }
.scp-sup-card .cats { grid-column: 2 / -1; display: flex; gap: 4px; flex-wrap: wrap; margin-top: 6px; }
.scp-review-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 16px; }
.scp-review-title { margin: 0 0 10px; font-size: 11px; text-transform: uppercase; letter-spacing: .1em; color: var(--fg-3); font-weight: 700; }
.scp-review-list > div { display: flex; justify-content: space-between; padding: 5px 0; font-size: 12.5px; border-bottom: 1px dashed var(--border-soft); }
.scp-review-list > div:last-child { border-bottom: 0; }
.scp-review-list span { color: var(--fg-3); }
.scp-review-list strong { color: var(--fg); font-weight: 600; }
.scp-review-grid .obs { font-size: 12px; color: var(--fg-3); margin: 10px 0 0; padding: 8px; background: var(--surface-2); border-radius: 8px; }
.scp-review-items > div { display: flex; justify-content: space-between; padding: 5px 0; font-size: 12.5px; }
.scp-review-items .n { color: var(--fg-2); }
.scp-review-items .q { color: var(--fg-3); font-family: 'JetBrains Mono', monospace; }
.scp-review-items .more, .scp-review-sups .more { font-size: 11.5px; color: var(--fg-3); padding-top: 6px; display: inline-block; }
.scp-review-sups > div { display: flex; align-items: center; gap: 8px; padding: 5px 0; font-size: 12.5px; }
.scp-channels { display: flex; flex-direction: column; gap: 6px; }
.scp-channels label { display: flex; align-items: center; gap: 10px; font-size: 12.5px; color: var(--fg-2); cursor: pointer; }
.scp-ai-toggle { display: flex; align-items: center; gap: 12px; }
.scp-ai-toggle div { display: flex; flex-direction: column; }
.scp-ai-toggle span { font-size: 12.5px; color: var(--fg); }
.scp-ai-toggle small { font-size: 11px; color: var(--fg-3); margin-top: 2px; }
.scp-wizard-foot {
position: absolute; bottom: 0; left: 0; right: 0;
display: flex; justify-content: space-between; align-items: center;
padding: 14px 28px; background: oklch(0.17 0.012 250 / .95); border-top: 1px solid var(--border-soft);
backdrop-filter: blur(6px);
}
/* Modal */
.scp-modal-mask { position: fixed; inset: 0; background: oklch(0.05 0.005 250 / .75); z-index: 50; display: grid; place-items: center; backdrop-filter: blur(4px); animation: fadein .2s ease both; }
.scp-modal { width: 640px; max-width: 92vw; max-height: 80vh; display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--border); border-radius: 16px; box-shadow: var(--shadow-pop); overflow: hidden; animation: scaleIn .2s ease both; }
.scp-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--border-soft); }
.scp-modal-head h3 { margin: 0; font-size: 16px; font-weight: 600; }
.scp-modal-toolbar { display: flex; gap: 8px; padding: 12px 18px; border-bottom: 1px solid var(--border-soft); }
.scp-modal-toolbar .scp-input { flex: 1; }
.scp-modal-body { padding: 6px 0; overflow: auto; flex: 1; }
.scp-pick-row { display: grid; grid-template-columns: 20px 1fr auto auto; gap: 12px; align-items: center; padding: 10px 18px; cursor: pointer; transition: background .12s; width: 100%; text-align: left; border-bottom: 1px solid var(--border-soft); }
.scp-pick-row:hover { background: var(--surface-2); }
.scp-pick-row.sel { background: oklch(0.22 0.04 160 / .35); }
.scp-pick-row .check { width: 18px; height: 18px; border-radius: 5px; border: 1.5px solid var(--border-strong); display: grid; place-items: center; }
.scp-pick-row.sel .check { background: oklch(0.55 0.13 160); border-color: oklch(0.55 0.13 160); color: white; }
.scp-pick-row .n { display: flex; flex-direction: column; min-width: 0; }
.scp-pick-row .n .t { font-size: 13px; color: var(--fg); font-weight: 500; }
.scp-pick-row .n .m { font-size: 11px; color: var(--fg-3); }
.scp-pick-row .lp { font-size: 12.5px; color: var(--fg-3); font-weight: 600; }
.scp-modal-foot { display: flex; justify-content: space-between; align-items: center; padding: 14px 18px; border-top: 1px solid var(--border-soft); font-size: 12px; color: var(--fg-3); }
`;
document.head.appendChild(s);
})();
window.ScreenNewQuotation = ScreenNewQuotation;