/* ===========================================================
Forja 3D — Catálogo de produtos & Histórico de impressões
=========================================================== */
function ProductForm({ initial, filaments, onSave, onClose }) {
const [p, setP] = useState(initial || { name: '', filamentId: filaments[0]?.id || null, weightG: 50, printTimeMin: 180, price: '' });
const set = (k, v) => setP((x) => ({ ...x, [k]: v }));
const h = Math.floor((p.printTimeMin || 0) / 60), m = (p.printTimeMin || 0) % 60;
return (
<>
set('name', v)} placeholder="Ex: Vaso geométrico G" />
set('weightG', v)} suffix="g" />
set('printTimeMin', (Number(v) || 0) * 60 + m)} suffix="h" />
set('printTimeMin', h * 60 + (Number(v) || 0))} suffix="min" />
set('price', v)} prefix="R$" />
>
);
}
function Catalog({ go }) {
const s = useStore();
const [modal, setModal] = useState(null);
const st = s.settings;
function costOf(p) {
const fil = s.filaments.find((f) => f.id === p.filamentId);
const c = computeCost({ grams: p.weightG, costPerKg: fil?.costPerKg || 100, printTimeMin: p.printTimeMin, packaging: st.defaultPackaging, margin: 0, failRate: st.failRate, marketplaceFee: 0, extraCost: 0 }, st);
return c.withFail;
}
return (
setModal({ mode: 'add' })}>Novo produto} />
{s.products.length === 0 ? go('calculadora')}>Ir para calculadora}>Crie produtos pela calculadora ou adicione manualmente.
: (
{s.products.map((p) => {
const fil = s.filaments.find((f) => f.id === p.filamentId);
const cost = costOf(p);
const lucro = p.price - cost;
const margin = cost > 0 ? (lucro / cost) * 100 : 0;
return (
{/* thumb placeholder w/ filament color tint */}
foto do produto
{p.name}
setModal({ mode: 'edit', data: p })} />
confirmDel(`Excluir "${p.name}"?`) && store.removeProduct(p.id)} />
{fil?.name || 'sem filamento'} · {fmt.num(p.weightG)}g · {fmt.hm(p.printTimeMin)}
Custo {fmt.brl0(cost)}
{fmt.brl(p.price)}
= 80 ? 'g' : margin >= 30 ? 'o' : 'r'}>{lucro >= 0 ? '+' : ''}{fmt.pct(margin)}
);
})}
)}
setModal(null)} title={modal?.mode === 'edit' ? 'Editar produto' : 'Novo produto'}>
{modal && modal.mode === 'edit' ? store.updateProduct(modal.data.id, p) : store.addProduct(p)} onClose={() => setModal(null)} />}
);
}
window.Catalog = Catalog;
/* ===========================================================
Histórico de impressões
=========================================================== */
function PrintForm({ filaments, products, onClose }) {
const [p, setP] = useState({ date: today(), product: '', filamentId: filaments[0]?.id || null, grams: '', timeMin: '', status: 'sucesso' });
const set = (k, v) => setP((x) => ({ ...x, [k]: v }));
const h = Math.floor((Number(p.timeMin) || 0) / 60), m = (Number(p.timeMin) || 0) % 60;
return (
<>
set('product', e.target.value)} placeholder="Ex: Vaso geométrico G" />
set('grams', v)} suffix="g" />
set('timeMin', (Number(v) || 0) * 60 + m)} suffix="h" />
set('timeMin', h * 60 + (Number(v) || 0))} suffix="min" />
set('date', e.target.value)} />
Registrar consome a gramagem do estoque do filamento.
>
);
}
function History() {
const s = useStore();
const [modal, setModal] = useState(false);
const [filt, setFilt] = useState('todas');
const list = s.prints.filter((p) => filt === 'todas' || p.status === filt).sort((a, b) => (b.date > a.date ? 1 : -1));
const totG = s.prints.reduce((a, p) => a + p.grams, 0);
const totH = s.prints.reduce((a, p) => a + p.timeMin, 0) / 60;
const ok = s.prints.filter((p) => p.status === 'sucesso').length;
const taxa = s.prints.length ? (ok / s.prints.length) * 100 : 0;
return (
setModal(true)}>Registrar impressão} />
{(totG / 1000).toFixed(2).replace('.', ',')} kg} />
{totH.toFixed(0)} h} />
= 90 ? 'g' : 'r'} value={fmt.pct(taxa)} />
{[['todas', 'Todas'], ['sucesso', 'Sucessos'], ['falha', 'Falhas']].map(([v, l]) => )}
{list.length === 0 ?
setModal(true)}>Registrar impressão}>Acompanhe cada job da sua impressora. : (
| Peça | Filamento | Data | Material | Tempo | Resultado | |
{list.map((p) => {
const fil = s.filaments.find((f) => f.id === p.filamentId);
return (
| {p.product} |
{fil?.name || '—'}
|
{fmt.dateFull(p.date)} |
{fmt.num(p.grams)}g |
{fmt.hm(p.timeMin)} |
{p.status === 'sucesso' ? Sucesso : Falha} |
store.removePrint(p.id)} /> |
);
})}
)}
setModal(false)} title="Registrar impressão">
setModal(false)} />
);
}
window.History = History;