Bienvenido a Nuñez Mecánica Automotriz

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.

🔧 Diagnóstico 🛞 Alineación ⚙️ Frenos 🔧 Motor

Nuestros Servicios

🔧 Reparación de Motores ⚙️ Mantenimiento de Caja ✅ Mantenimiento Preventivo ✅ Diagnóstico Electrónico ✅ Alineación y Balanceo ✅ Frenos y Suspensión ✅ Cambio de Aceite ✅ Aire Acondicionado ✅ Escaneo de Fallas ✅ Embrague y Transmisión

Contacto

📞 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

📋 Panel de Administración

📊 Gestión de datos
➕ Acciones rápidas

Clientes registrados

Nombre Teléfono Email Documento RUC Dirección Acciones

🔐 Acceso Empleados

🔐 Restablecer Contraseña

Te enviaremos un enlace a tu correo para que puedas crear una nueva contraseña.

// Import the functions you need from the SDKs you need import { initializeApp } from "firebase/app"; // TODO: Add SDKs for Firebase products that you want to use // https://firebase.google.com/docs/web/setup#available-libraries // Your web app's Firebase configuration const firebaseConfig = { apiKey: "AIzaSyARVLPxAh5YnWJMoUIQFfUxmvJOjCnbc0s", authDomain: "nunez-mecanica.firebaseapp.com", projectId: "nunez-mecanica", storageBucket: "nunez-mecanica.firebasestorage.app", messagingSenderId: "629372748928", appId: "1:629372748928:web:d507496ababa7cd8a15265" }; // Initialize Firebase const app = initializeApp(firebaseConfig); // REFERENCIAS DOM const loginPanel = document.getElementById('loginPanel'); const resetPanel = document.getElementById('resetPanel'); const btnLoginOut = document.getElementById('btnLoginOut'); const btnAdminLink = document.getElementById('btnAdminLink'); const grupoAdmin = document.getElementById('grupoAdmin'); const headerUsuario = document.getElementById('headerUsuario'); const loginError = document.getElementById('loginError'); const subpaneles = { clientes: document.getElementById('sub-clientes'), vehiculos: document.getElementById('sub-vehiculos'), reparaciones: document.getElementById('sub-reparaciones'), nuevoCliente: document.getElementById('sub-nuevoCliente'), nuevaReparacion: document.getElementById('sub-nuevaReparacion'), configuracion: document.getElementById('sub-configuracion'), empleados: document.getElementById('sub-empleados') }; const tbodyClientes = document.getElementById('tbodyClientes'); const tbodyVehiculos = document.getElementById('tbodyVehiculos'); const tbodyReparaciones = document.getElementById('tbodyReparaciones'); const tbodyEmpleados = document.getElementById('tbodyEmpleados'); const selectCliente = document.getElementById('repCliente'); const selectVehiculo = document.getElementById('repVehiculo'); const sinResultados = document.getElementById('sinResultados'); const sinVehiculos = document.getElementById('sinVehiculos'); const sinEmpleados = document.getElementById('sinEmpleados'); // Referencias para el modal de edición de cliente const modalEditarCliente = document.getElementById('modalEditarCliente'); const formEditarCliente = document.getElementById('formEditarCliente'); const editClienteId = document.getElementById('editClienteId'); const editCliNombre = document.getElementById('editCliNombre'); const editCliTelefono = document.getElementById('editCliTelefono'); const editCliEmail = document.getElementById('editCliEmail'); const editCliDireccion = document.getElementById('editCliDireccion'); const editCliDocumento = document.getElementById('editCliDocumento'); const editCliRuc = document.getElementById('editCliRuc'); const editClienteError = document.getElementById('editClienteError'); let qrUrlActual = ''; let qrCanvas = null; let editandoId = null; let elementoAEliminar = null; let tipoEliminar = ''; let todosLosClientes = []; let todosLosVehiculos = []; let todosLosEmpleados = []; let vehiculoHistorialActual = null; let empleadoPendiente = null; // CONTRASEÑA ADMIN async function obtenerPasswordAdmin() { try { const docRef = doc(db, 'config', 'admin'); const docSnap = await getDoc(docRef); if (docSnap.exists()) { return docSnap.data().password; } else { await setDoc(docRef, { password: 'admin123' }); return 'admin123'; } } catch (e) { console.error('Error obteniendo contraseña de admin:', e); return 'admin123'; } } // NAVEGACIÓN window.mostrarSeccion = function(id) { document.querySelectorAll('.section').forEach(el => el.classList.remove('active')); const sec = document.getElementById('seccion-' + id); if (sec) sec.classList.add('active'); if (id === 'admin' && !auth.currentUser) { loginPanel.style.display = 'block'; resetPanel.style.display = 'none'; } loginPanel.style.display = 'none'; resetPanel.style.display = 'none'; }; window.mostrarSubPanel = function(id) { Object.keys(subpaneles).forEach(key => { if (subpaneles[key]) { subpaneles[key].style.display = (key === id) ? 'block' : 'none'; } }); if (id === 'clientes') { cargarClientes(); document.getElementById('buscarCliente').value = ''; sinResultados.style.display = 'none'; } if (id === 'vehiculos') { cargarVehiculos(); document.getElementById('buscarVehiculo').value = ''; sinVehiculos.style.display = 'none'; } if (id === 'reparaciones') cargarReparaciones(); if (id === 'nuevaReparacion') { cargarSelectClientes(); cargarVehiculosPorCliente(document.getElementById('repCliente').value); } if (id === 'empleados') cargarEmpleados(); }; function mostrarLogin(mostrar) { loginPanel.style.display = mostrar ? 'block' : 'none'; resetPanel.style.display = 'none'; } window.mostrarReset = function() { loginPanel.style.display = 'none'; resetPanel.style.display = 'block'; document.getElementById('resetMessage').textContent = ''; document.getElementById('resetSuccess').textContent = ''; document.getElementById('resetEmail').value = ''; }; window.mostrarLoginPanel = function() { resetPanel.style.display = 'none'; loginPanel.style.display = 'block'; document.getElementById('loginError').textContent = ''; }; // AUTENTICACIÓN document.getElementById('formLogin').addEventListener('submit', async (e) => { e.preventDefault(); loginError.textContent = ''; const email = document.getElementById('loginEmail').value; const password = document.getElementById('loginPassword').value; try { await signInWithEmailAndPassword(auth, email, password); document.getElementById('formLogin').reset(); } catch (error) { let msg = '❌ '; switch (error.code) { case 'auth/user-not-found': msg += 'No existe una cuenta con ese correo.'; break; case 'auth/wrong-password': msg += 'Contraseña incorrecta.'; break; case 'auth/invalid-credential': msg += 'Credenciales inválidas.'; break; default: msg += error.message; } loginError.textContent = msg; } }); document.getElementById('formReset').addEventListener('submit', async (e) => { e.preventDefault(); const resetMsg = document.getElementById('resetMessage'); const resetSuccess = document.getElementById('resetSuccess'); resetMsg.textContent = ''; resetSuccess.textContent = ''; const email = document.getElementById('resetEmail').value.trim(); if (!email) { resetMsg.textContent = '❌ Ingresa tu correo.'; return; } try { await sendPasswordResetEmail(auth, email); resetSuccess.textContent = '✅ ¡Correo enviado! Revisa tu bandeja de entrada.'; document.getElementById('resetEmail').value = ''; } catch (error) { resetMsg.textContent = '❌ ' + error.message; } }); // 🔥 onAuthStateChanged onAuthStateChanged(auth, async (user) => { if (user) { let nombreCompleto = ''; let esAdmin = false; try { const empleadosRef = collection(db, 'empleados'); const q = query(empleadosRef, where('email', '==', user.email)); const querySnapshot = await getDocs(q); if (!querySnapshot.empty) { const docSnap = querySnapshot.docs[0]; const data = docSnap.data(); nombreCompleto = `${data.nombre || ''} ${data.apellido || ''}`.trim(); esAdmin = data.rol === 'admin'; } } catch (error) { console.error('❌ Error al buscar empleado:', error); } headerUsuario.textContent = nombreCompleto ? `👤 ${nombreCompleto}` : `👤 ${user.email}`; if (grupoAdmin) grupoAdmin.style.display = esAdmin ? 'flex' : 'none'; btnLoginOut.textContent = 'Cerrar Sesión'; btnAdminLink.style.display = 'inline'; loginPanel.style.display = 'none'; resetPanel.style.display = 'none'; document.querySelectorAll('.section').forEach(el => el.classList.remove('active')); document.getElementById('seccion-inicio').classList.add('active'); cargarClientes(); cargarSelectClientes(); cargarVehiculos(); if (esAdmin) cargarEmpleados(); } else { btnLoginOut.textContent = 'Iniciar Sesión'; btnAdminLink.style.display = 'none'; if (grupoAdmin) grupoAdmin.style.display = 'none'; headerUsuario.textContent = ''; loginPanel.style.display = 'block'; resetPanel.style.display = 'none'; document.getElementById('seccion-admin').classList.remove('active'); document.querySelectorAll('.section').forEach(el => el.classList.remove('active')); document.getElementById('seccion-inicio').classList.add('active'); tbodyClientes.innerHTML = ''; tbodyVehiculos.innerHTML = ''; tbodyReparaciones.innerHTML = ''; tbodyEmpleados.innerHTML = ''; } }); window.manejarLoginLogout = function() { if (auth.currentUser) { signOut(auth); } else { mostrarSeccion('inicio'); loginPanel.style.display = 'block'; } }; // CLIENTES async function cargarClientes() { if (!auth.currentUser) return; try { const q = query(collection(db, 'clientes'), orderBy('nombre')); const snapshot = await getDocs(q); todosLosClientes = []; snapshot.forEach(docSnap => { todosLosClientes.push({ id: docSnap.id, ...docSnap.data() }); }); filtrarClientes(); cargarSelectClientes(); } catch (e) { console.error('Error cargando clientes:', e); } } window.filtrarClientes = function() { const busqueda = document.getElementById('buscarCliente').value.toLowerCase().trim(); let html = '', encontrados = 0; todosLosClientes.forEach(cliente => { const nombre = (cliente.nombre || '').toLowerCase(); const telefono = (cliente.telefono || '').toLowerCase(); const email = (cliente.email || '').toLowerCase(); const documento = (cliente.documento || '').toLowerCase(); const ruc = (cliente.ruc || '').toLowerCase(); if (busqueda === '' || nombre.includes(busqueda) || telefono.includes(busqueda) || email.includes(busqueda) || documento.includes(busqueda) || ruc.includes(busqueda)) { encontrados++; html += ` ${cliente.nombre || ''} ${cliente.telefono || ''} ${cliente.email || ''} ${cliente.documento || ''} ${cliente.ruc || ''} ${cliente.direccion || ''} `; } }); tbodyClientes.innerHTML = html; sinResultados.style.display = encontrados === 0 ? 'block' : 'none'; }; document.getElementById('formCliente').addEventListener('submit', async (e) => { e.preventDefault(); if (!auth.currentUser) return alert('Debes iniciar sesión'); const nombre = document.getElementById('cliNombre').value.trim(); const telefono = document.getElementById('cliTelefono').value.trim(); const email = document.getElementById('cliEmail').value.trim(); const direccion = document.getElementById('cliDireccion').value.trim(); const documento = document.getElementById('cliDocumento').value.trim(); const ruc = document.getElementById('cliRuc').value.trim(); if (!nombre || !telefono || !email) return alert('Nombre, teléfono y email son obligatorios'); try { await addDoc(collection(db, 'clientes'), { nombre, telefono, email, direccion, documento, ruc, fechaRegistro: new Date() }); document.getElementById('formCliente').reset(); cargarClientes(); alert('✅ Cliente agregado correctamente'); } catch (e) { alert('Error: ' + e.message); } }); // EDITAR CLIENTE window.editarCliente = async function(id) { editClienteError.textContent = ''; try { const docSnap = await getDoc(doc(db, 'clientes', id)); if (!docSnap.exists()) { alert('El cliente no existe.'); return; } const data = docSnap.data(); editClienteId.value = id; editCliNombre.value = data.nombre || ''; editCliTelefono.value = data.telefono || ''; editCliEmail.value = data.email || ''; editCliDireccion.value = data.direccion || ''; editCliDocumento.value = data.documento || ''; editCliRuc.value = data.ruc || ''; modalEditarCliente.style.display = 'flex'; } catch (e) { alert('Error al cargar los datos: ' + e.message); } }; window.cerrarModalEditarCliente = function() { modalEditarCliente.style.display = 'none'; editClienteError.textContent = ''; }; formEditarCliente.addEventListener('submit', async (e) => { e.preventDefault(); if (!auth.currentUser) return alert('Debes iniciar sesión'); const id = editClienteId.value; const nombre = editCliNombre.value.trim(); const telefono = editCliTelefono.value.trim(); const email = editCliEmail.value.trim(); const direccion = editCliDireccion.value.trim(); const documento = editCliDocumento.value.trim(); const ruc = editCliRuc.value.trim(); if (!nombre || !telefono || !email) { editClienteError.textContent = '❌ Nombre, teléfono y email son obligatorios.'; return; } try { await updateDoc(doc(db, 'clientes', id), { nombre, telefono, email, direccion, documento, ruc }); cerrarModalEditarCliente(); cargarClientes(); alert('✅ Cliente actualizado correctamente.'); } catch (e) { editClienteError.textContent = '❌ Error al actualizar: ' + e.message; } }); modalEditarCliente.addEventListener('click', function(e) { if (e.target === this) cerrarModalEditarCliente(); }); document.addEventListener('keydown', function(e) { if (e.key === 'Escape') cerrarModalEditarCliente(); }); async function cargarSelectClientes() { if (!auth.currentUser) return; try { const snapshot = await getDocs(collection(db, 'clientes')); const selects = [selectCliente, document.getElementById('vehCliente')]; selects.forEach(sel => { if (!sel) return; const currentVal = sel.value; sel.innerHTML = ''; snapshot.forEach(doc => { const data = doc.data(); const opt = document.createElement('option'); opt.value = doc.id; opt.textContent = data.nombre + ' - ' + data.telefono; sel.appendChild(opt); }); if (currentVal) sel.value = currentVal; }); } catch (e) { console.error('Error cargando clientes para selects:', e); } } // VEHÍCULOS async function cargarVehiculos() { if (!auth.currentUser) return; try { const q = query(collection(db, 'vehiculos'), orderBy('matricula')); const snapshot = await getDocs(q); todosLosVehiculos = []; for (const docSnap of snapshot.docs) { const data = docSnap.data(); let nombreCliente = 'Cliente eliminado'; if (data.clienteId) { try { const clienteSnap = await getDoc(doc(db, 'clientes', data.clienteId)); if (clienteSnap.exists()) nombreCliente = clienteSnap.data().nombre; } catch (e) {} } todosLosVehiculos.push({ id: docSnap.id, clienteNombre: nombreCliente, ...data }); } filtrarVehiculos(); } catch (e) { console.error('Error cargando vehículos:', e); } } window.filtrarVehiculos = function() { const busqueda = document.getElementById('buscarVehiculo').value.toLowerCase().trim(); let html = '', encontrados = 0; todosLosVehiculos.forEach(veh => { const matricula = (veh.matricula || '').toLowerCase(); const marca = (veh.marca || '').toLowerCase(); const modelo = (veh.modelo || '').toLowerCase(); if (busqueda === '' || matricula.includes(busqueda) || marca.includes(busqueda) || modelo.includes(busqueda)) { encontrados++; html += ` ${veh.clienteNombre || 'Sin cliente'} ${veh.matricula || ''} ${veh.marca || ''} ${veh.modelo || ''} ${veh.chasis || ''} ${veh.anio || ''} ${veh.kilometraje ? Number(veh.kilometraje).toLocaleString() : ''} ${veh.proximoMantenimiento ? Number(veh.proximoMantenimiento).toLocaleString() : ''} `; } }); tbodyVehiculos.innerHTML = html; sinVehiculos.style.display = encontrados === 0 ? 'block' : 'none'; }; window.abrirModalVehiculo = function(clienteId = null) { document.getElementById('vehiculoModalTitulo').textContent = '🚗 Agregar Vehículo'; document.getElementById('vehiculoId').value = ''; document.getElementById('vehMatricula').value = ''; document.getElementById('vehMarca').value = ''; document.getElementById('vehModelo').value = ''; document.getElementById('vehAnio').value = ''; document.getElementById('vehChasis').value = ''; document.getElementById('vehKilometraje').value = ''; document.getElementById('vehProximoMantenimiento').value = ''; document.getElementById('vehiculoError').textContent = ''; if (clienteId) document.getElementById('vehCliente').value = clienteId; document.getElementById('modalVehiculo').style.display = 'flex'; }; window.editarVehiculo = async function(id) { try { const docSnap = await getDoc(doc(db, 'vehiculos', id)); if (!docSnap.exists()) return alert('Vehículo no encontrado'); const data = docSnap.data(); document.getElementById('vehiculoModalTitulo').textContent = '✏️ Editar Vehículo'; document.getElementById('vehiculoId').value = id; document.getElementById('vehCliente').value = data.clienteId || ''; document.getElementById('vehMatricula').value = data.matricula || ''; document.getElementById('vehMarca').value = data.marca || ''; document.getElementById('vehModelo').value = data.modelo || ''; document.getElementById('vehAnio').value = data.anio || ''; document.getElementById('vehChasis').value = data.chasis || ''; document.getElementById('vehKilometraje').value = data.kilometraje || ''; document.getElementById('vehProximoMantenimiento').value = data.proximoMantenimiento || ''; document.getElementById('vehiculoError').textContent = ''; document.getElementById('modalVehiculo').style.display = 'flex'; } catch (e) { alert('Error al cargar vehículo: ' + e.message); } }; window.cerrarModalVehiculo = function() { document.getElementById('modalVehiculo').style.display = 'none'; }; document.getElementById('formVehiculo').addEventListener('submit', async (e) => { e.preventDefault(); if (!auth.currentUser) return alert('Debes iniciar sesión'); const id = document.getElementById('vehiculoId').value; const clienteId = document.getElementById('vehCliente').value; const matricula = document.getElementById('vehMatricula').value.trim().toUpperCase(); const marca = document.getElementById('vehMarca').value.trim(); const modelo = document.getElementById('vehModelo').value.trim(); const anio = parseInt(document.getElementById('vehAnio').value) || null; const chasis = document.getElementById('vehChasis').value.trim(); const kilometraje = parseInt(document.getElementById('vehKilometraje').value) || null; const proximoMantenimiento = parseInt(document.getElementById('vehProximoMantenimiento').value) || null; const errorDiv = document.getElementById('vehiculoError'); if (!clienteId) return errorDiv.textContent = '❌ Selecciona un cliente.'; if (!matricula) return errorDiv.textContent = '❌ La matrícula es obligatoria.'; if (!marca) return errorDiv.textContent = '❌ La marca es obligatoria.'; if (!modelo) return errorDiv.textContent = '❌ El modelo es obligatorio.'; try { const data = { clienteId, matricula, marca, modelo, anio, chasis, kilometraje, proximoMantenimiento }; if (id) { await updateDoc(doc(db, 'vehiculos', id), data); } else { await addDoc(collection(db, 'vehiculos'), { ...data, fechaRegistro: new Date() }); } cerrarModalVehiculo(); cargarVehiculos(); if (clienteId) cargarVehiculosPorCliente(clienteId); alert('✅ Vehículo guardado correctamente.'); } catch (e) { errorDiv.textContent = '❌ ' + e.message; } }); window.cargarVehiculosPorCliente = async function(clienteId) { if (!clienteId) { selectVehiculo.innerHTML = ''; return; } try { const q = query(collection(db, 'vehiculos'), where('clienteId', '==', clienteId)); const snapshot = await getDocs(q); selectVehiculo.innerHTML = ''; snapshot.forEach(doc => { const data = doc.data(); const opt = document.createElement('option'); opt.value = doc.id; const km = data.kilometraje ? ` (${Number(data.kilometraje).toLocaleString()} km)` : ''; opt.textContent = `${data.matricula} - ${data.marca} ${data.modelo}${km}`; selectVehiculo.appendChild(opt); }); } catch (e) { console.error('Error cargando vehículos por cliente:', e); } }; // REPARACIONES async function cargarReparaciones() { if (!auth.currentUser) return; try { const q = query(collection(db, 'reparaciones'), orderBy('fechaIngreso', 'desc')); const snapshot = await getDocs(q); tbodyReparaciones.innerHTML = ''; for (const docSnap of snapshot.docs) { const data = docSnap.data(); let nombreCliente = 'Cliente eliminado'; let infoVehiculo = 'Sin vehículo'; if (data.vehiculoId) { try { const vehSnap = await getDoc(doc(db, 'vehiculos', data.vehiculoId)); if (vehSnap.exists()) { const v = vehSnap.data(); infoVehiculo = `${v.matricula} (${v.marca} ${v.modelo})`; } } catch (e) {} } if (data.clienteId) { try { const clienteSnap = await getDoc(doc(db, 'clientes', data.clienteId)); if (clienteSnap.exists()) nombreCliente = clienteSnap.data().nombre; } catch (e) {} } const tr = document.createElement('tr'); const estadoClass = { 'Pendiente': 'badge-pendiente', 'En progreso': 'badge-progreso', 'Completado': 'badge-completado' } [data.estado] || ''; tr.innerHTML = ` ${nombreCliente} ${infoVehiculo} ${data.descripcion || ''} ${data.estado || 'Pendiente'} ${data.costo ? Number(data.costo).toLocaleString() : ''} ${data.fechaIngreso ? new Date(data.fechaIngreso.seconds * 1000).toLocaleDateString() : ''} `; tbodyReparaciones.appendChild(tr); } } catch (e) { console.error('Error cargando reparaciones:', e); } } document.getElementById('formReparacion').addEventListener('submit', async (e) => { e.preventDefault(); if (!auth.currentUser) return alert('Debes iniciar sesión'); const clienteId = document.getElementById('repCliente').value; const vehiculoId = document.getElementById('repVehiculo').value; const descripcion = document.getElementById('repDescripcion').value.trim(); const estado = document.getElementById('repEstado').value; const costo = parseFloat(document.getElementById('repCosto').value) || 0; if (!clienteId) return alert('Selecciona un cliente'); if (!vehiculoId) return alert('Selecciona un vehículo'); if (!descripcion) return alert('Descripción es obligatoria'); try { await addDoc(collection(db, 'reparaciones'), { clienteId, vehiculoId, descripcion, estado, costo, fechaIngreso: new Date() }); document.getElementById('formReparacion').reset(); cargarReparaciones(); alert('✅ Reparación registrada correctamente'); } catch (e) { alert('Error: ' + e.message); } }); // HISTORIAL window.verHistorial = async function(vehiculoId) { vehiculoHistorialActual = vehiculoId; const container = document.getElementById('historialContainer'); container.innerHTML = '

⏳ 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

📋 Registro de intervenciones (${reparaciones.length})

`; if (reparaciones.length === 0) { html += ``; } else { reparaciones.forEach(data => { const fecha = data.fechaIngreso ? new Date(data.fechaIngreso.seconds * 1000).toLocaleDateString('es-ES') : 'Sin fecha'; const estadoClass = { 'Pendiente': 'badge-pendiente', 'En progreso': 'badge-progreso', 'Completado': 'badge-completado' } [data.estado] || ''; const tipo = data.tipo || 'Reparación'; html += ``; }); } html += `
FechaDescripciónTipoEstadoCosto (Gs)
No hay reparaciones registradas para este vehículo.
${fecha}${data.descripcion || ''}${tipo}${data.estado || 'Pendiente'}${data.costo ? Number(data.costo).toLocaleString() : ''}
`; container.innerHTML = html; } catch (e) { container.innerHTML = `

❌ 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 += ` ${nombreCompleto || 'Sin nombre'} ${emp.email || ''} ${emp.rol || 'empleado'} ${estadoText} ${!emp.aprobado ? `` : ''} `; }); tbodyEmpleados.innerHTML = html; sinEmpleados.style.display = encontrados === 0 ? 'block' : 'none'; } window.aprobarEmpleado = async function(uid) { if (!auth.currentUser) return; if (confirm('¿Aprobar a este empleado?')) { try { await updateDoc(doc(db, 'empleados', uid), { aprobado: true }); cargarEmpleados(); alert('✅ Empleado aprobado correctamente.'); } catch (e) { alert('Error al aprobar: ' + e.message); } } }; window.abrirModalEmpleado = async function() { if (!auth.currentUser) { alert('Debes iniciar sesión.'); return; } const nombre = prompt('Ingresa el nombre del empleado:'); if (!nombre) return; const apellido = prompt('Ingresa el apellido del empleado:'); if (!apellido) return; const email = prompt('Ingresa el email del empleado:'); if (!email) return; const password = prompt('Ingresa la contraseña (mínimo 6 caracteres):'); if (!password || password.length < 6) { alert('La contraseña debe tener al menos 6 caracteres.'); return; } const rol = confirm('¿Es administrador?') ? 'admin' : 'empleado'; empleadoPendiente = { nombre, apellido, email, password, rol }; document.getElementById('confNombre').textContent = `${nombre} ${apellido}`; document.getElementById('confEmail').textContent = email; document.getElementById('confRol').textContent = rol === 'admin' ? 'Administrador' : 'Empleado'; document.getElementById('modalConfirmacion').style.display = 'flex'; }; window.confirmarCrearEmpleado = async function() { if (!empleadoPendiente) return; const { nombre, apellido, email, password, rol } = empleadoPendiente; try { const userCredential = await createUserWithEmailAndPassword(auth, email, password); const uid = userCredential.user.uid; await setDoc(doc(db, 'empleados', uid), { nombre, apellido, email, rol, aprobado: true, fechaRegistro: new Date() }); cerrarModalConfirmacion(); cargarEmpleados(); alert('✅ Empleado creado correctamente.'); } catch (e) { alert('❌ Error al crear empleado: ' + e.message); } }; window.cerrarModalConfirmacion = function() { document.getElementById('modalConfirmacion').style.display = 'none'; empleadoPendiente = null; }; // BACKUP window.descargarBackup = async function() { if (!auth.currentUser) { alert('Debes iniciar sesión para realizar un backup.'); return; } if (!confirm('¿Deseas descargar un backup completo de la base de datos?')) return; try { const btn = document.querySelector('#grupoAdmin .btn-success'); const textoOriginal = btn ? btn.textContent : '💾 Backup'; if (btn) { btn.textContent = '⏳ Generando backup...'; btn.disabled = true; } const colecciones = ['clientes', 'vehiculos', 'reparaciones', 'empleados', 'config']; const backupData = {}; for (const nombreColeccion of colecciones) { const q = query(collection(db, nombreColeccion)); const snapshot = await getDocs(q); const docs = []; snapshot.forEach(docSnap => { docs.push({ id: docSnap.id, ...docSnap.data() }); }); backupData[nombreColeccion] = docs; } const json = JSON.stringify(backupData, null, 2); const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const ahora = new Date(); const fechaStr = `${ahora.getFullYear()}-${String(ahora.getMonth()+1).padStart(2,'0')}-${String(ahora.getDate()).padStart(2,'0')}`; const horaStr = `${String(ahora.getHours()).padStart(2,'0')}-${String(ahora.getMinutes()).padStart(2,'0')}`; const nombreArchivo = `backup-Nunez-${fechaStr}-${horaStr}.json`; const a = document.createElement('a'); a.href = url; a.download = nombreArchivo; document.body.appendChild(a); a.click(); document.body.removeChild(a); if (btn) { btn.textContent = textoOriginal; btn.disabled = false; } alert('✅ Backup descargado correctamente.'); } catch (error) { console.error('Error al generar backup:', error); alert('❌ Error al generar backup: ' + error.message); const btn = document.querySelector('#grupoAdmin .btn-success'); if (btn) { btn.textContent = '💾 Backup'; btn.disabled = false; } } }; // INICIALIZAR if (!auth.currentUser) { loginPanel.style.display = 'block'; } else { cargarClientes(); cargarSelectClientes(); cargarVehiculos(); } console.log('🚗 Nuñez Mecánica Automotriz – Sistema listo.');