45 años de experiencia · Reparaciones en General · Confianza y calidad.
Especialistas en diagnóstico, reparación y mantenimiento de vehículos. Atención personalizada y trabajo garantizado.
📞 Teléfono: 0985 192671
📱 WhatsApp: +595 985 192671
🕒 Horario: Lun-Vie 8:00-18:00 · Sáb 8:00-13:00
📧 Email: nunezemprendimientos@gmail.com
| Nombre | Teléfono | Documento | RUC | Dirección | Acciones |
|---|
Te enviaremos un enlace a tu correo para que puedas crear una nueva contraseña.
⏳ Cargando historial...
'; document.getElementById('modalHistorial').style.display = 'flex'; try { const vehSnap = await getDoc(doc(db, 'vehiculos', vehiculoId)); if (!vehSnap.exists()) { container.innerHTML = '❌ Vehículo no encontrado.
'; return; } const vehData = vehSnap.data(); let nombreCliente = 'Cliente eliminado'; if (vehData.clienteId) { const cliSnap = await getDoc(doc(db, 'clientes', vehData.clienteId)); if (cliSnap.exists()) nombreCliente = cliSnap.data().nombre; } const q = query(collection(db, 'reparaciones'), where('vehiculoId', '==', vehiculoId)); const snapshot = await getDocs(q); const reparaciones = []; snapshot.forEach(doc => reparaciones.push({ id: doc.id, ...doc.data() })); reparaciones.sort((a, b) => (a.fechaIngreso ? a.fechaIngreso.seconds : 0) - (b.fechaIngreso ? b.fechaIngreso.seconds : 0)); let html = `Cliente: ${nombreCliente}
Matrícula: ${vehData.matricula || ''}
Marca: ${vehData.marca || ''} | Modelo: ${vehData.modelo || ''}
Año: ${vehData.anio || 'No especificado'} | Chasis: ${vehData.chasis || 'No especificado'}
Kilometraje: ${vehData.kilometraje ? Number(vehData.kilometraje).toLocaleString() : 'No registrado'} km
Próximo Mantenimiento: ${vehData.proximoMantenimiento ? Number(vehData.proximoMantenimiento).toLocaleString() : 'No programado'} km
| Fecha | Descripción | Tipo | Estado | Costo (Gs) |
|---|---|---|---|---|
| No hay reparaciones registradas para este vehículo. | ||||
| ${fecha} | ${data.descripcion || ''} | ${tipo} | ${data.estado || 'Pendiente'} | ${data.costo ? Number(data.costo).toLocaleString() : ''} |
❌ Error al cargar historial: ${e.message}
`; console.error('Error al cargar historial:', e); } }; window.cerrarHistorial = function() { document.getElementById('modalHistorial').style.display = 'none'; vehiculoHistorialActual = null; }; window.exportarHistorialPDF = function() { const container = document.getElementById('historialContainer'); if (!container) return; const btn = document.querySelector('#modalHistorial .btn-success'); if (btn) btn.textContent = '⏳ Generando PDF...'; const opt = { margin: 10, filename: `historial-vehiculo-${Date.now()}.pdf`, html2canvas: { scale: 2, useCORS: true }, jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' } }; html2pdf().set(opt).from(container).save().then(() => { if (btn) btn.textContent = '📄 Exportar a PDF'; }).catch((e) => { alert('Error al generar PDF: ' + e.message); if (btn) btn.textContent = '📄 Exportar a PDF'; }); }; // ELIMINAR window.solicitarPassword = function(tipo, id) { elementoAEliminar = id; tipoEliminar = tipo; document.getElementById('passwordInput').value = ''; document.getElementById('passwordError').textContent = ''; document.getElementById('modalPassword').style.display = 'flex'; }; window.confirmarEliminacion = async function() { const password = document.getElementById('passwordInput').value; const errorDiv = document.getElementById('passwordError'); const adminPassword = await obtenerPasswordAdmin(); if (password === adminPassword) { try { if (tipoEliminar === 'cliente') { await deleteDoc(doc(db, 'clientes', elementoAEliminar)); cargarClientes(); } else if (tipoEliminar === 'vehiculo') { await deleteDoc(doc(db, 'vehiculos', elementoAEliminar)); cargarVehiculos(); } else if (tipoEliminar === 'reparacion') { await deleteDoc(doc(db, 'reparaciones', elementoAEliminar)); cargarReparaciones(); } else if (tipoEliminar === 'empleado') { await deleteDoc(doc(db, 'empleados', elementoAEliminar)); cargarEmpleados(); } cerrarPasswordModal(); alert('✅ Registro eliminado correctamente.'); } catch (e) { errorDiv.textContent = '❌ Error al eliminar: ' + e.message; } } else { errorDiv.textContent = '❌ Contraseña incorrecta.'; document.getElementById('passwordInput').value = ''; document.getElementById('passwordInput').focus(); } }; window.cerrarPasswordModal = function() { document.getElementById('modalPassword').style.display = 'none'; elementoAEliminar = null; tipoEliminar = ''; document.getElementById('passwordInput').value = ''; document.getElementById('passwordError').textContent = ''; }; // EDITAR REPARACIÓN window.abrirEditar = function(id) { editandoId = id; getDoc(doc(db, 'reparaciones', id)).then((snap) => { if (snap.exists()) { const data = snap.data(); document.getElementById('editEstado').value = data.estado || 'Pendiente'; document.getElementById('editDescripcion').value = data.descripcion || ''; document.getElementById('editCosto').value = data.costo || 0; document.getElementById('modalEditar').style.display = 'flex'; document.getElementById('editError').textContent = ''; } }).catch((e) => { alert('Error al cargar reparación: ' + e.message); }); }; window.cerrarEditar = function() { document.getElementById('modalEditar').style.display = 'none'; editandoId = null; }; document.getElementById('formEditar').addEventListener('submit', async (e) => { e.preventDefault(); if (!editandoId) return; const estado = document.getElementById('editEstado').value; const descripcion = document.getElementById('editDescripcion').value.trim(); const costo = parseFloat(document.getElementById('editCosto').value) || 0; if (!descripcion) { document.getElementById('editError').textContent = '❌ La descripción es obligatoria.'; return; } try { await updateDoc(doc(db, 'reparaciones', editandoId), { estado, descripcion, costo }); cerrarEditar(); cargarReparaciones(); alert('✅ Reparación actualizada.'); } catch (error) { document.getElementById('editError').textContent = '❌ ' + error.message; } }); // QR window.generarQR = function(id) { const url = `https://tallerctam.pages.dev/seguimiento.html?id=${id}`; qrUrlActual = url; const container = document.getElementById('qrContainer'); container.innerHTML = ''; try { const qr = new QRCode(container, { text: url, width: 220, height: 220, colorDark: '#585162', colorLight: '#ffffff', correctLevel: QRCode.CorrectLevel.H }); setTimeout(() => { const canvas = container.querySelector('canvas'); if (canvas) qrCanvas = canvas; }, 100); } catch (e) { container.innerHTML = `❌ Error al generar QR
`; qrCanvas = null; } document.getElementById('qrUrlText').textContent = '🔗 ' + url; document.getElementById('modalQR').style.display = 'flex'; }; window.cerrarQR = function() { document.getElementById('modalQR').style.display = 'none'; document.getElementById('qrContainer').innerHTML = ''; qrCanvas = null; }; window.descargarQR = function() { if (qrCanvas) { const link = document.createElement('a'); link.download = 'qr-reparacion.png'; link.href = qrCanvas.toDataURL('image/png'); link.click(); } else { alert('❌ QR no generado'); } }; window.copiarURL = function() { if (qrUrlActual) { navigator.clipboard.writeText(qrUrlActual).then(() => alert('✅ Enlace copiado')).catch(() => { const input = document.createElement('input'); input.value = qrUrlActual; document.body.appendChild(input); input.select(); document.execCommand('copy'); document.body.removeChild(input); alert('✅ Enlace copiado'); }); } }; document.getElementById('modalQR').addEventListener('click', function(e) { if (e.target === this) cerrarQR(); }); document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { cerrarPasswordModal(); cerrarQR(); cerrarEditar(); cerrarModalVehiculo(); cerrarHistorial(); cerrarModalConfirmacion(); cerrarModalEditarCliente(); } }); // CONFIGURACIÓN document.getElementById('formCambiarPassword').addEventListener('submit', async (e) => { e.preventDefault(); const errorDiv = document.getElementById('configError'); const successDiv = document.getElementById('configSuccess'); errorDiv.textContent = ''; successDiv.textContent = ''; const actual = document.getElementById('passActual').value; const nueva = document.getElementById('passNueva').value; const confirmar = document.getElementById('passConfirmar').value; const adminPassword = await obtenerPasswordAdmin(); if (actual !== adminPassword) { errorDiv.textContent = '❌ Contraseña actual incorrecta.'; return; } if (nueva !== confirmar) { errorDiv.textContent = '❌ Las contraseñas no coinciden.'; return; } if (nueva.length < 6) { errorDiv.textContent = '❌ Mínimo 6 caracteres.'; return; } try { await setDoc(doc(db, 'config', 'admin'), { password: nueva }); successDiv.textContent = '✅ Contraseña actualizada correctamente.'; document.getElementById('passActual').value = ''; document.getElementById('passNueva').value = ''; document.getElementById('passConfirmar').value = ''; } catch (err) { errorDiv.textContent = '❌ ' + err.message; } }); // EMPLEADOS async function cargarEmpleados() { if (!auth.currentUser) return; try { const q = query(collection(db, 'empleados'), orderBy('nombre')); const snapshot = await getDocs(q); todosLosEmpleados = []; snapshot.forEach(docSnap => { todosLosEmpleados.push({ id: docSnap.id, ...docSnap.data() }); }); mostrarEmpleados(); } catch (e) { console.error('Error cargando empleados:', e); } } function mostrarEmpleados() { let html = '', encontrados = 0; todosLosEmpleados.forEach(emp => { encontrados++; const nombreCompleto = `${emp.nombre || ''} ${emp.apellido || ''}`.trim(); const estadoBadge = emp.aprobado ? 'badge-completado' : 'badge-pendiente'; const estadoText = emp.aprobado ? 'Aprobado' : 'Pendiente'; html += `