// 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 });