// Caririaçu Guincho — Tab Checklist const { useState: useStateTC, useEffect: useEffectTC, useMemo: useMemoTC } = React; const TabChecklist = ({ guincho, periodo }) => { const [items, setItems] = useStateTC([]); const [templates, setTemplates] = useStateTC([]); const [view, setView] = useStateTC(null); const [novoStep, setNovoStep] = useStateTC(null); const [tick, setTick] = useStateTC(0); useEffectTC(() => { sb.from('checklist_execucoes').select('*') .eq('guincho_id', guincho.id) .gte('data', periodo.inicio) .lte('data', periodo.fim + 'T23:59:59') .order('data', { ascending: false }) .then(r => setItems(r.data || [])); }, [guincho.id, periodo.inicio, periodo.fim, tick]); useEffectTC(() => { db.list('checklist_templates', { eq: { ativo: true }, order: 'nome', asc: true }).then(setTemplates); }, []); const metricas = useMemoTC(() => { const ultima = items[0]; let pctOk = null; if (ultima) { const r = ultima.respostas || []; const ok = r.filter(x => x.ok).length; pctOk = r.length ? Math.round((ok / r.length) * 100) : null; } return { count: items.length, ultima, pctOk }; }, [items]); const iniciar = (tpl) => { setNovoStep({ template_id: tpl.id, template_nome: tpl.nome, data: new Date().toISOString().slice(0, 16), km: guincho.km_atual, responsavel: '', respostas: (tpl.itens || []).map(it => ({ item_id: it.id, label: it.label, obrigatorio: it.obrigatorio, ok: false, obs: '' })), }); }; const salvar = async () => { const obrigSemOk = novoStep.respostas.filter(r => r.obrigatorio && !r.ok); let statusFinal = 'concluido'; if (obrigSemOk.length) { if (!confirm(`${obrigSemOk.length} item(ns) obrigatório(s) não marcados como OK. Salvar como pendente?`)) return; statusFinal = 'pendente'; } const result = await db.insert('checklist_execucoes', { guincho_id: guincho.id, template_id: novoStep.template_id, template_nome: novoStep.template_nome, data: novoStep.data, km: parseInt(novoStep.km) || null, responsavel: novoStep.responsavel, respostas: novoStep.respostas.map(r => ({ item_id: r.item_id, label: r.label, ok: r.ok, obs: r.obs })), status: statusFinal, }); if (!result) return; setNovoStep(null); setTick(t => t + 1); showToast('Checklist salvo', 'success'); }; const excluir = async (item) => { if (!confirm('Excluir este checklist?')) return; const ok = await db.remove('checklist_execucoes', item.id); if (!ok) return; setTick(t => t + 1); showToast('Checklist excluído', 'info'); }; return ( <>
Total no período
{metricas.count}
Último
{metricas.ultima ? new Date(metricas.ultima.data).toLocaleDateString('pt-BR') : '—'}
% OK último
= 90 ? '#10B981' : '#F59E0B', marginTop: 4 }}> {metricas.pctOk != null ? metricas.pctOk + '%' : '—'}
{items.length === 0 ? (
Nenhum checklist no período. Clique no + pra preencher um.
) : items.map(x => { const ok = (x.respostas || []).filter(r => r.ok).length; const total = (x.respostas || []).length; return (
setView(x)}>
{x.template_nome || 'Checklist'}
{new Date(x.data).toLocaleDateString('pt-BR')} {x.km && ` · ${fmtKM(x.km)}`} {x.responsavel && ` · ${x.responsavel}`} · {ok}/{total} OK {x.status === 'pendente' && · pendente}
); })}
{ if (!templates.length) { showToast('Cadastre um template em Configurações primeiro', 'warning'); return; } if (templates.length === 1) iniciar(templates[0]); else setNovoStep('select-template'); }} /> {/* Seletor de template */} {novoStep === 'select-template' && ( setNovoStep(null)} title="Escolher template" width={400}>
{templates.map(t => ( ))}
)} {/* Form de preenchimento */} {novoStep && typeof novoStep === 'object' && ( setNovoStep(null)} title={`Preencher: ${novoStep.template_nome}`} width={560}>
setNovoStep({ ...novoStep, data: e.target.value })} /> setNovoStep({ ...novoStep, km: e.target.value })} />
setNovoStep({ ...novoStep, responsavel: e.target.value })} />
Itens
{novoStep.respostas.map((r, i) => (
{ const arr = [...novoStep.respostas]; arr[i] = { ...r, obs: e.target.value }; setNovoStep({ ...novoStep, respostas: arr }); }} style={{ border: '1px solid #E2E8F0', borderRadius: 4, padding: '4px 8px', fontSize: 11, background: '#fff' }} />
))}
setNovoStep(null)}>Cancelar Salvar
)} {/* Visualização read-only */} {view && ( setView(null)} title={`${view.template_nome}`} width={500}>
{new Date(view.data).toLocaleString('pt-BR')} {view.km && ` · ${fmtKM(view.km)}`} {view.responsavel && ` · ${view.responsavel}`}
{(view.respostas || []).map(r => (
{r.ok ? '✓' : '✗'} {r.label}
{r.obs &&
{r.obs}
}
))}
setView(null)}>Fechar
)} ); }; Object.assign(window, { TabChecklist });