// Caririaçu Guincho — Vistoria Digital Screen
const { useState: useStateV, useEffect: useEffectV } = React;
const CHECKLIST_ITEMS = [
'CD/Mp3','Auto-Falantes','Calotas','Faróis','Alarme','Estepe',
'Macaco','Extintor','Documentos','Tapetes','Chave de Roda','Manual'
];
const FOTOS_CATS = ['Frente','Lateral Esq.','Lateral Dir.','Traseira','Painel','Motor','Outros'];
const VehicleView = ({ label, marks, onMark }) => {
const [hov, setHov] = useStateV(false);
return (
{label}
{
const rect = e.currentTarget.getBoundingClientRect();
const x = Math.round(((e.clientX - rect.left) / rect.width) * 100);
const y = Math.round(((e.clientY - rect.top) / rect.height) * 100);
onMark(x, y, label);
}}
onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}
style={{ position: 'relative', cursor: 'crosshair', border: `2px solid ${hov ? '#3B82F6' : '#E2E8F0'}`, borderRadius: 10, background: '#F8FAFC', width: 140, height: 90, overflow: 'hidden', transition: 'border 0.15s' }}>
{marks.map((m, i) => (
{i+1}
))}
{hov &&
}
);
};
const VistoriaScreen = () => {
const [vistorias, setVistorias] = useStateV([]);
const [loading, setLoading] = useStateV(true);
const [tab, setTab] = useStateV(0);
const [showForm, setShowForm] = useStateV(false);
const [motoristas, setMotoristas] = useStateV([]);
// Form state
const [tipoVistoria, setTipoVistoria] = useStateV('Entrada');
const [tipoVeiculo, setTipoVeiculo] = useStateV('Carro');
const [placa, setPlaca] = useStateV('');
const [marca, setMarca] = useStateV('');
const [modelo, setModelo] = useStateV('');
const [cor, setCor] = useStateV('');
const [ano, setAno] = useStateV('');
const [km, setKm] = useStateV('');
const [motoristaId, setMotoristaId] = useStateV('');
const [checklist, setChecklist] = useStateV({});
const [combustivel, setCombustivel] = useStateV(50);
const [damages, setDamages] = useStateV({ 'Frente': [], 'Traseira': [], 'Lat. Esq.': [], 'Lat. Dir.': [] });
const [signGuin, setSignGuin] = useStateV(null);
const [signCli, setSignCli] = useStateV(null);
const [nomeGuin, setNomeGuin] = useStateV('');
const [nomeCli, setNomeCli] = useStateV('');
const [formStep, setFormStep] = useStateV(0);
const TABS = ['Identificação','Checklist','Mapa de Avarias','Fotos','Assinaturas','Finalizar'];
const VEICS = ['Carro','Moto','Caminhão','Utilitário'];
const refresh = async () => {
setLoading(true);
setVistorias(await db.list('vistorias', { order: 'created_at' }));
setLoading(false);
};
useEffectV(() => { refresh(); db.list('motoristas', { order: 'nome', asc: true }).then(setMotoristas); }, []);
const resetForm = () => {
setTipoVistoria('Entrada'); setTipoVeiculo('Carro'); setPlaca(''); setMarca(''); setModelo('');
setCor(''); setAno(''); setKm(''); setMotoristaId(''); setChecklist({}); setCombustivel(50);
setDamages({ 'Frente': [], 'Traseira': [], 'Lat. Esq.': [], 'Lat. Dir.': [] });
setSignGuin(null); setSignCli(null); setNomeGuin(''); setNomeCli(''); setFormStep(0);
};
const addDamage = (x, y, view) => setDamages(d => ({ ...d, [view]: [...(d[view]||[]), { x, y, comment: '' }] }));
const totalDamages = Object.values(damages).reduce((s, a) => s + a.length, 0);
const salvarVistoria = async () => {
if (!placa) { showToast('Informe a placa', 'warning'); return; }
const motoristaSel = motoristas.find(m => m.id === parseInt(motoristaId));
const created = await db.insert('vistorias', {
tipo: tipoVistoria, tipo_veiculo: tipoVeiculo, placa, marca, modelo, cor, ano, km,
veiculo: `${marca} ${modelo} ${cor}`.trim(),
motorista_id: motoristaSel?.id || null,
motorista_nome: motoristaSel?.nome || null,
checklist, combustivel, damages,
assinatura_guincheiro: signGuin, assinatura_cliente: signCli,
nome_guincheiro: nomeGuin, nome_cliente: nomeCli,
status: (signGuin && signCli) ? 'concluida' : 'pendente',
});
if (created) {
showToast(`Vistoria ${created.numero} salva!`, 'success');
resetForm();
setShowForm(false);
refresh();
}
};
const filtradas = vistorias.filter(v => tab === 0 || (tab === 1 && v.status === 'pendente') || (tab === 2 && v.status === 'concluida'));
if (showForm) return (
{ resetForm(); setShowForm(false); }} style={{ transform: 'rotate(180deg)' }}>
Nova Vistoria
{TABS.map((t, i) => (
))}
{formStep === 0 && (
{VEICS.map(v => (
))}
setPlaca(e.target.value)} />
setMarca(e.target.value)} />
setModelo(e.target.value)} />
setCor(e.target.value)} />
setAno(e.target.value)} />
setKm(e.target.value)} />
)}
{formStep === 1 && (
{CHECKLIST_ITEMS.map(item => {
const checked = checklist[item];
return (
);
})}
50 ? '#10B981' : combustivel > 25 ? '#F59E0B' : '#EF4444', transition: 'width 0.3s, background 0.3s' }} />
setCombustivel(+e.target.value)} style={{ width: '100%', marginTop: 8, accentColor: '#F59E0B' }} />
E1/41/23/4F
)}
{formStep === 2 && (
Mapa de Avarias
Clique nas vistas para marcar avarias
{totalDamages > 0 &&
{totalDamages} avaria{totalDamages > 1 ? 's' : ''}}
{Object.keys(damages).map(view => (
addDamage(x, y, view)} />
))}
{totalDamages > 0 && (
Avarias Registradas
{Object.entries(damages).map(([view, marks]) => marks.map((m, i) => (
{i+1}
{view} — posição ({m.x}%, {m.y}%)
)))}
)}
)}
{formStep === 3 && (
Fotos do Veículo
Upload de fotos requer Supabase Storage configurado — em breve. Por enquanto, anexe externamente ao laudo PDF.
{FOTOS_CATS.map(cat => (
))}
)}
{formStep === 4 && (
Assinaturas Digitais
Data/Hora automática: {new Date().toLocaleString('pt-BR')}
)}
{formStep === 5 && (
Vistoria Pronta!
Revise o resumo e salve a vistoria
{[
['Tipo', tipoVistoria], ['Placa', placa || '—'],
['Veículo', `${marca} ${modelo}`.trim() || '—'], ['Combustível', `${combustivel}%`],
['Avarias', `${totalDamages} marcadas`], ['Motorista', motoristas.find(m => m.id === parseInt(motoristaId))?.nome || '—'],
['Assinatura Guincheiro', signGuin ? '✓ Assinado' : '— Pendente'],
['Assinatura Cliente', signCli ? '✓ Assinado' : '— Pendente'],
].map(([k,v]) => (
))}
Salvar Vistoria
)}
{formStep < 5 && (
setFormStep(s => Math.max(0, s-1))} disabled={formStep === 0}>← Anterior
setFormStep(s => Math.min(5, s+1))}>Próximo →
)}
);
return (
Vistoria Digital
Laudos e checklists dos veículos
{ resetForm(); setShowForm(true); }}>Nova Vistoria
{['Todas','Pendentes','Concluídas'].map((t, i) => (
))}
{loading ? (
Carregando vistorias...
) : filtradas.length === 0 ? (
{vistorias.length === 0 ? 'Nenhuma vistoria registrada' : 'Nenhuma vistoria nesta categoria'}
{vistorias.length === 0 ? 'Clique em "Nova Vistoria" para registrar a primeira' : 'Tente outra aba'}
{vistorias.length === 0 &&
{ resetForm(); setShowForm(true); }} style={{ marginTop: 16 }}>Nova Vistoria}
) : (
{['Nº','Placa','Veículo','Data/Hora','Tipo','Motorista','Status'].map(h => (
| {h} |
))}
{filtradas.map((v, i) => (
e.currentTarget.style.background = '#EFF6FF'}
onMouseLeave={e => e.currentTarget.style.background = i % 2 ? '#F8FAFC' : '#fff'}>
| {v.numero} |
{v.placa || '—'}
|
{v.veiculo || '—'} |
{new Date(v.created_at).toLocaleString('pt-BR')} |
{v.tipo}
|
{v.motorista_nome || '—'} |
|
))}
)}
);
};
Object.assign(window, { VistoriaScreen });