// Caririaçu Guincho — Tab Lembrete const { useState: useStateTL, useEffect: useEffectTL, useMemo: useMemoTL } = React; const CORES_LEMBRETE = { ativo: { bg: '#ECFDF5', border: '#86EFAC', text: '#065F46', icon: '🟢' }, proximo: { bg: '#FEF3C7', border: '#FCD34D', text: '#92400E', icon: '🟡' }, vencido: { bg: '#FEE2E2', border: '#FCA5A5', text: '#991B1B', icon: '🔴' }, }; const TabLembrete = ({ guincho, onLembreteChange }) => { const [items, setItems] = useStateTL([]); const [edit, setEdit] = useStateTL(null); const [tick, setTick] = useStateTL(0); useEffectTL(() => { db.list('lembretes', { eq: { guincho_id: guincho.id, status: 'ativo' }, order: 'created_at', asc: false }) .then(setItems); }, [guincho.id, tick]); const itemsComStatus = useMemoTL(() => { const order = { vencido: 0, proximo: 1, ativo: 2 }; return items.map(x => ({ ...x, st: window.statusLembrete(x, guincho.km_atual) })).sort((a, b) => order[a.st.estado] - order[b.st.estado]); }, [items, guincho.km_atual]); const metricas = useMemoTL(() => { const ativos = itemsComStatus.filter(x => x.st.estado === 'ativo').length; const proximos = itemsComStatus.filter(x => x.st.estado === 'proximo').length; const vencidos = itemsComStatus.filter(x => x.st.estado === 'vencido').length; return { ativos, proximos, vencidos }; }, [itemsComStatus]); const novo = () => setEdit({ descricao: '', gatilho: 'km', proximo_km: (guincho.km_atual || 0) + 5000, proxima_data: null, antecedencia_km: 500, antecedencia_dias: 15, recorrente: false, intervalo_km: 10000, intervalo_dias: 90, }); const salvar = async () => { if (!edit.descricao?.trim()) { showToast('Descreva o lembrete', 'warning'); return; } if (edit.gatilho === 'km' && !edit.proximo_km) { showToast('Informe próximo km', 'warning'); return; } if (edit.gatilho === 'data' && !edit.proxima_data) { showToast('Informe próxima data', 'warning'); return; } const payload = { ...edit, guincho_id: guincho.id, proximo_km: edit.gatilho !== 'data' ? parseInt(edit.proximo_km) : null, proxima_data: edit.gatilho !== 'km' ? edit.proxima_data : null, antecedencia_km: parseInt(edit.antecedencia_km || 0), antecedencia_dias: parseInt(edit.antecedencia_dias || 0), intervalo_km: edit.recorrente && edit.gatilho !== 'data' ? parseInt(edit.intervalo_km || 0) || null : null, intervalo_dias: edit.recorrente && edit.gatilho !== 'km' ? parseInt(edit.intervalo_dias || 0) || null : null, }; const result = edit.id ? await db.update('lembretes', edit.id, payload) : await db.insert('lembretes', payload); if (!result) return; setEdit(null); setTick(t => t + 1); onLembreteChange?.(); showToast('Lembrete salvo', 'success'); }; const concluir = async (item) => { if (!confirm(`Marcar "${item.descricao}" como concluído?`)) return; const { error } = await sb.rpc('concluir_lembrete', { p_lembrete_id: item.id }); if (error) { showToast('Erro: ' + error.message, 'error'); return; } setTick(t => t + 1); onLembreteChange?.(); showToast(item.recorrente ? 'Concluído (próximo lembrete criado)' : 'Concluído', 'success'); }; const excluir = async () => { if (!confirm('Excluir este lembrete?')) return; const ok = await db.remove('lembretes', edit.id); if (!ok) return; setEdit(null); setTick(t => t + 1); onLembreteChange?.(); }; return ( <>
Ativos
{metricas.ativos}
Próximos
{metricas.proximos}
Vencidos
{metricas.vencidos}
{itemsComStatus.length === 0 ? (
Nenhum lembrete ativo. Clique no + pra criar.
) : itemsComStatus.map(x => { const c = CORES_LEMBRETE[x.st.estado]; return (
{ const { st, ...rest } = x; setEdit(rest); }}>
{c.icon} {x.descricao}
{window.formatarFalta(x.st)} {x.recorrente && ' · recorrente'}
); })}
{edit && ( setEdit(null)} title={edit.id ? 'Editar lembrete' : 'Novo lembrete'} width={500}>
setEdit({ ...edit, descricao: e.target.value })} /> setEdit({ ...edit, proximo_km: e.target.value })} /> setEdit({ ...edit, antecedencia_km: e.target.value })} />
)} {edit.gatilho !== 'km' && (
setEdit({ ...edit, proxima_data: e.target.value })} /> setEdit({ ...edit, antecedencia_dias: e.target.value })} />
)} {edit.recorrente && (
{edit.gatilho !== 'data' && ( setEdit({ ...edit, intervalo_km: e.target.value })} /> )} {edit.gatilho !== 'km' && ( setEdit({ ...edit, intervalo_dias: e.target.value })} /> )}
)}
{edit.id ? Excluir :
}
setEdit(null)}>Cancelar Salvar
)} ); }; // Expor CORES_LEMBRETE no window pra reuso na tela consolidada (Task 18) window.CORES_LEMBRETE = CORES_LEMBRETE; Object.assign(window, { TabLembrete });