// Caririaçu Guincho — CRUDs auxiliares de Configurações // Renderizados como abas dentro de ConfigScreen (screen-outros.jsx) // Cada componente retorna um com header interno (padrão da aba Usuários). const { useState: useStateCA, useEffect: useEffectCA } = React; // ── Estilos compartilhados (padrão tabela Usuários) ─────────────────────── const headerStyleCA = { padding: '16px 20px', borderBottom: '1px solid #F1F5F9', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }; const titleStyleCA = { margin: 0, fontSize: 17, fontWeight: 700, color: '#0F172A', fontFamily: 'DM Sans, sans-serif' }; const thStyleCA = { padding: '10px 16px', textAlign: 'left', fontSize: 12, fontWeight: 600, color: '#64748B', borderBottom: '1px solid #E2E8F0' }; const tdStyleCA = { padding: '10px 16px', borderBottom: '1px solid #E2E8F0' }; const emptyStyleCA = { padding: 40, textAlign: 'center', color: '#94A3B8', fontSize: 13 }; const iconBtnCA = (color = '#64748B') => ({ background: 'none', border: 'none', cursor: 'pointer', color, padding: 4, borderRadius: 6 }); // ── CrudSimplesScreen ───────────────────────────────────────────────────── // Helper para CRUDs com nome (obrigatório) + ativo (boolean). // Usado por Combustíveis e Tipos de despesa. const CrudSimplesScreen = ({ titulo, tituloSingular, tabela, campos = ['nome'] }) => { const [items, setItems] = useStateCA([]); const [modal, setModal] = useStateCA(false); const [editing, setEditing] = useStateCA(null); const [form, setForm] = useStateCA({ nome: '', ativo: true }); const carregar = async () => { const data = await db.list(tabela, { order: 'nome', asc: true }); setItems(data); }; useEffectCA(() => { carregar(); }, []); const openNew = () => { setEditing(null); setForm({ nome: '', ativo: true }); setModal(true); }; const openEdit = (row) => { setEditing(row); setForm({ nome: row.nome || '', ativo: row.ativo !== false }); setModal(true); }; const salvar = async () => { if (!form.nome.trim()) { showToast('Informe o nome', 'warning'); return; } const payload = { nome: form.nome.trim(), ativo: !!form.ativo }; if (editing) { const upd = await db.update(tabela, editing.id, payload); if (upd) { showToast(`${upd.nome} atualizado`, 'success'); await carregar(); setModal(false); } } else { const created = await db.insert(tabela, payload); if (created) { showToast(`${created.nome} adicionado`, 'success'); await carregar(); setModal(false); } } }; const excluir = async (row) => { if (!confirm(`Excluir "${row.nome}"?`)) return; const ok = await db.remove(tabela, row.id); if (ok) { showToast(`"${row.nome}" excluído`, 'warning'); await carregar(); } }; const mostraAtivo = campos.includes('ativo'); return (

{titulo}

Adicionar
{items.length === 0 ? (
Nenhum item cadastrado.
) : ( {mostraAtivo && } {items.map((row, i) => ( {mostraAtivo && ( )} ))}
NomeStatusAções
{row.nome}
)} setModal(false)} title={editing ? `Editar ${editing.nome}` : `Novo ${tituloSingular || titulo.toLowerCase()}`} width={460}>
setForm(f => ({ ...f, nome: e.target.value }))} /> {mostraAtivo && ( )}
setModal(false)}>Cancelar {editing ? 'Salvar' : 'Adicionar'}
); }; // ── ConfigCombustiveisScreen ────────────────────────────────────────────── const ConfigCombustiveisScreen = () => ; // ── ConfigTiposDespesaScreen ────────────────────────────────────────────── const ConfigTiposDespesaScreen = () => ; // ── ConfigPostosScreen ──────────────────────────────────────────────────── const ConfigPostosScreen = () => { const [items, setItems] = useStateCA([]); const [modal, setModal] = useStateCA(false); const [editing, setEditing] = useStateCA(null); const [form, setForm] = useStateCA({ nome: '', endereco: '', ativo: true }); const carregar = async () => { setItems(await db.list('postos', { order: 'nome', asc: true })); }; useEffectCA(() => { carregar(); }, []); const openNew = () => { setEditing(null); setForm({ nome: '', endereco: '', ativo: true }); setModal(true); }; const openEdit = (row) => { setEditing(row); setForm({ nome: row.nome || '', endereco: row.endereco || '', ativo: row.ativo !== false }); setModal(true); }; const salvar = async () => { if (!form.nome.trim()) { showToast('Informe o nome do posto', 'warning'); return; } const payload = { nome: form.nome.trim(), endereco: form.endereco.trim() || null, ativo: !!form.ativo, }; if (editing) { const upd = await db.update('postos', editing.id, payload); if (upd) { showToast(`${upd.nome} atualizado`, 'success'); await carregar(); setModal(false); } } else { const created = await db.insert('postos', payload); if (created) { showToast(`${created.nome} adicionado`, 'success'); await carregar(); setModal(false); } } }; const excluir = async (row) => { if (!confirm(`Excluir posto "${row.nome}"?`)) return; const ok = await db.remove('postos', row.id); if (ok) { showToast(`"${row.nome}" excluído`, 'warning'); await carregar(); } }; return (

Postos

Adicionar
{items.length === 0 ? (
Nenhum posto cadastrado.
) : ( {items.map((row, i) => ( ))}
Nome Endereço Status Ações
{row.nome} {row.endereco || '—'}
)} setModal(false)} title={editing ? `Editar ${editing.nome}` : 'Novo posto'} width={500}>
setForm(f => ({ ...f, nome: e.target.value }))} /> setForm(f => ({ ...f, endereco: e.target.value }))} />
setModal(false)}>Cancelar {editing ? 'Salvar' : 'Adicionar'}
); }; // ── ConfigChecklistsScreen ──────────────────────────────────────────────── // Lista templates de checklist (checklist_templates) com itens editáveis em JSON. const tempItemId = () => `tmp-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; const ConfigChecklistsScreen = () => { const [items, setItems] = useStateCA([]); const [modal, setModal] = useStateCA(false); const [editing, setEditing] = useStateCA(null); const [nome, setNome] = useStateCA(''); const [ativo, setAtivo] = useStateCA(true); const [itens, setItens] = useStateCA([]); // [{id, label, obrigatorio}] const carregar = async () => { setItems(await db.list('checklist_templates', { order: 'nome', asc: true })); }; useEffectCA(() => { carregar(); }, []); const normalizaItens = (raw) => { if (!raw) return []; let arr = raw; if (typeof raw === 'string') { try { arr = JSON.parse(raw); } catch { return []; } } if (!Array.isArray(arr)) return []; return arr.map((it) => ({ id: it.id ?? tempItemId(), label: it.label || '', obrigatorio: it.obrigatorio !== false, })); }; const openNew = () => { setEditing(null); setNome(''); setAtivo(true); setItens([]); setModal(true); }; const openEdit = (row) => { setEditing(row); setNome(row.nome || ''); setAtivo(row.ativo !== false); setItens(normalizaItens(row.itens)); setModal(true); }; const addItem = () => { setItens(arr => [...arr, { id: tempItemId(), label: '', obrigatorio: true }]); }; const updateItem = (id, patch) => { setItens(arr => arr.map(it => it.id === id ? { ...it, ...patch } : it)); }; const removeItem = (id) => { setItens(arr => arr.filter(it => it.id !== id)); }; const salvar = async () => { if (!nome.trim()) { showToast('Informe o nome do template', 'warning'); return; } if (itens.length === 0) { showToast('Adicione ao menos um item', 'warning'); return; } if (itens.some(it => !it.label.trim())) { showToast('Preencha o texto de todos os itens', 'warning'); return; } const itensFinal = itens.map((it, idx) => ({ id: idx + 1, label: it.label.trim(), obrigatorio: !!it.obrigatorio, })); const payload = { nome: nome.trim(), ativo: !!ativo, itens: itensFinal, }; if (editing) { const upd = await db.update('checklist_templates', editing.id, payload); if (upd) { showToast(`Template "${upd.nome}" atualizado`, 'success'); await carregar(); setModal(false); } } else { const created = await db.insert('checklist_templates', payload); if (created) { showToast(`Template "${created.nome}" criado`, 'success'); await carregar(); setModal(false); } } }; const excluir = async (row) => { if (!confirm(`Excluir template "${row.nome}"?`)) return; const ok = await db.remove('checklist_templates', row.id); if (ok) { showToast(`"${row.nome}" excluído`, 'warning'); await carregar(); } }; const countItens = (raw) => normalizaItens(raw).length; return (

Templates de checklist

Adicionar
{items.length === 0 ? (
Nenhum template cadastrado.
) : ( {items.map((row, i) => ( ))}
Nome Itens Status Ações
{row.nome} {countItens(row.itens)} {countItens(row.itens) === 1 ? 'item' : 'itens'}
)} setModal(false)} title={editing ? `Editar template — ${editing.nome}` : 'Novo template de checklist'} width={620}>
setNome(e.target.value)} />
Adicionar item
{itens.length === 0 ? (
Nenhum item ainda. Clique em "Adicionar item" para começar.
) : (
{itens.map((it, idx) => (
{idx + 1}. updateItem(it.id, { label: e.target.value })} placeholder="Descrição do item" style={{ flex: 1, padding: '7px 10px', borderRadius: 6, fontSize: 13, border: '1px solid #E2E8F0', outline: 'none', background: '#fff', fontFamily: 'Inter, sans-serif' }} />
))}
)}
setModal(false)}>Cancelar {editing ? 'Salvar' : 'Criar template'}
); }; Object.assign(window, { CrudSimplesScreen, ConfigCombustiveisScreen, ConfigTiposDespesaScreen, ConfigPostosScreen, ConfigChecklistsScreen, });