|
@ -1,7 +1,9 @@ |
|
|
@extends('layouts.app') |
|
|
@extends('layouts.app') |
|
|
|
|
|
|
|
|
@section('title', 'Reporte de Usuario - PrestamosTecmm') |
|
|
@section('title', 'Reporte de Usuario - PrestamosTecmm') |
|
|
|
|
|
@push('head') |
|
|
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}"> |
|
|
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}"> |
|
|
|
|
|
@endpush |
|
|
|
|
|
|
|
|
@section('content') |
|
|
@section('content') |
|
|
<link rel="stylesheet" href="{{ asset('css/user-dashboard.css') }}"> |
|
|
<link rel="stylesheet" href="{{ asset('css/user-dashboard.css') }}"> |
|
@ -100,6 +102,7 @@ |
|
|
<th>Fecha salida</th> |
|
|
<th>Fecha salida</th> |
|
|
<th>Fecha regreso</th> |
|
|
<th>Fecha regreso</th> |
|
|
<th>Motivo</th> |
|
|
<th>Motivo</th> |
|
|
|
|
|
<th>Acciones</th> |
|
|
</tr> |
|
|
</tr> |
|
|
</thead> |
|
|
</thead> |
|
|
<tbody> |
|
|
<tbody> |
|
@ -108,7 +111,24 @@ |
|
|
</table> |
|
|
</table> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<!-- jsPDF y autoTable --> |
|
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> |
|
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.7.0/jspdf.plugin.autotable.min.js"></script> |
|
|
<script> |
|
|
<script> |
|
|
|
|
|
// Iconos SVG para editar, eliminar y PDF |
|
|
|
|
|
const iconoEditar = `<button type='button' class='btn-accion editar' title='Editar' style='background:none;border:none;cursor:pointer;'><i class="fas fa-edit" style="color:#3182ce;font-size:20px;"></i></button>`; |
|
|
|
|
|
const iconoEliminar = `<button type='button' class='btn-accion eliminar' title='Eliminar' style='background:none;border:none;cursor:pointer;'><i class="fas fa-trash-alt" style="color:#e53e3e;font-size:20px;"></i></button>`; |
|
|
|
|
|
const iconoPDF = `<button type='button' class='btn-accion pdf' title='Generar PDF' style='background:none;border:none;cursor:pointer;'><i class="fas fa-file-pdf" style="color:#e53e3e;font-size:20px;"></i></button>`; |
|
|
|
|
|
|
|
|
|
|
|
let filaEditando = null; |
|
|
|
|
|
|
|
|
|
|
|
// Agregar SweetAlert2 CDN si no está |
|
|
|
|
|
if (!window.Swal) { |
|
|
|
|
|
var script = document.createElement('script'); |
|
|
|
|
|
script.src = 'https://cdn.jsdelivr.net/npm/sweetalert2@11'; |
|
|
|
|
|
document.head.appendChild(script); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
document.getElementById('form-reporte').addEventListener('submit', function(e) { |
|
|
document.getElementById('form-reporte').addEventListener('submit', function(e) { |
|
|
e.preventDefault(); |
|
|
e.preventDefault(); |
|
|
// Obtener los archivos y datos |
|
|
// Obtener los archivos y datos |
|
@ -119,14 +139,37 @@ |
|
|
const fechaRegreso = document.getElementById('fecha_regreso').value; |
|
|
const fechaRegreso = document.getElementById('fecha_regreso').value; |
|
|
const motivo = document.getElementById('motivo').value; |
|
|
const motivo = document.getElementById('motivo').value; |
|
|
|
|
|
|
|
|
// Función para crear miniatura o mostrar nombre si no hay archivo |
|
|
|
|
|
function crearMiniatura(archivo) { |
|
|
function crearMiniatura(archivo) { |
|
|
if (!archivo) return ''; |
|
|
if (!archivo) return ''; |
|
|
const url = URL.createObjectURL(archivo); |
|
|
const url = URL.createObjectURL(archivo); |
|
|
return `<div><img src="${url}" class='miniatura-img img-clickable' data-img-url="${url}" alt="${archivo.name}"><br><span>${archivo.name}</span></div>`; |
|
|
return `<div><img src="${url}" class='miniatura-img img-clickable' data-img-url="${url}" alt="${archivo ? archivo.name : ''}"><br><span>${archivo.name}</span></div>`; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Insertar fila en la tabla |
|
|
// Si estamos editando una fila, actualizamos esa fila y no agregamos una nueva |
|
|
|
|
|
if (filaEditando) { |
|
|
|
|
|
filaEditando.innerHTML = ` |
|
|
|
|
|
<td>${crearMiniatura(fotoGasIni)}</td> |
|
|
|
|
|
<td>${crearMiniatura(fotoVehiculo)}</td> |
|
|
|
|
|
<td>${crearMiniatura(fotoGasFin)}</td> |
|
|
|
|
|
<td>${fechaSalida ? fechaSalida.replace('T', ' ') : ''}</td> |
|
|
|
|
|
<td>${fechaRegreso ? fechaRegreso.replace('T', ' ') : ''}</td> |
|
|
|
|
|
<td>${motivo}</td> |
|
|
|
|
|
<td>${iconoEditar}${iconoEliminar}${iconoPDF}</td> |
|
|
|
|
|
`; |
|
|
|
|
|
filaEditando = null; |
|
|
|
|
|
document.getElementById('form-reporte').reset(); |
|
|
|
|
|
asignarEventosMiniaturas(); |
|
|
|
|
|
asignarEventosAcciones(); |
|
|
|
|
|
Swal.fire({ |
|
|
|
|
|
icon: 'success', |
|
|
|
|
|
title: '¡Editado correctamente!', |
|
|
|
|
|
showConfirmButton: false, |
|
|
|
|
|
timer: 1500 |
|
|
|
|
|
}); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Si no estamos editando, agregamos una nueva fila |
|
|
const tabla = document.getElementById('tabla-reporte').querySelector('tbody'); |
|
|
const tabla = document.getElementById('tabla-reporte').querySelector('tbody'); |
|
|
const fila = document.createElement('tr'); |
|
|
const fila = document.createElement('tr'); |
|
|
fila.innerHTML = ` |
|
|
fila.innerHTML = ` |
|
@ -136,14 +179,18 @@ |
|
|
<td>${fechaSalida ? fechaSalida.replace('T', ' ') : ''}</td> |
|
|
<td>${fechaSalida ? fechaSalida.replace('T', ' ') : ''}</td> |
|
|
<td>${fechaRegreso ? fechaRegreso.replace('T', ' ') : ''}</td> |
|
|
<td>${fechaRegreso ? fechaRegreso.replace('T', ' ') : ''}</td> |
|
|
<td>${motivo}</td> |
|
|
<td>${motivo}</td> |
|
|
|
|
|
<td>${iconoEditar}${iconoEliminar}${iconoPDF}</td> |
|
|
`; |
|
|
`; |
|
|
tabla.appendChild(fila); |
|
|
tabla.appendChild(fila); |
|
|
|
|
|
|
|
|
// Limpiar formulario |
|
|
|
|
|
document.getElementById('form-reporte').reset(); |
|
|
document.getElementById('form-reporte').reset(); |
|
|
|
|
|
|
|
|
// Volver a asignar eventos a todas las miniaturas |
|
|
|
|
|
asignarEventosMiniaturas(); |
|
|
asignarEventosMiniaturas(); |
|
|
|
|
|
asignarEventosAcciones(); |
|
|
|
|
|
Swal.fire({ |
|
|
|
|
|
icon: 'success', |
|
|
|
|
|
title: '¡Guardado correctamente!', |
|
|
|
|
|
showConfirmButton: false, |
|
|
|
|
|
timer: 1500 |
|
|
|
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Modal para mostrar imagen grande |
|
|
// Modal para mostrar imagen grande |
|
@ -157,7 +204,7 @@ |
|
|
modal.style.width = '100vw'; |
|
|
modal.style.width = '100vw'; |
|
|
modal.style.height = '100vh'; |
|
|
modal.style.height = '100vh'; |
|
|
modal.style.background = 'rgba(0,0,0,0.7)'; |
|
|
modal.style.background = 'rgba(0,0,0,0.7)'; |
|
|
modal.style.display = 'flex'; |
|
|
modal.style.display = 'none'; |
|
|
modal.style.alignItems = 'center'; |
|
|
modal.style.alignItems = 'center'; |
|
|
modal.style.justifyContent = 'center'; |
|
|
modal.style.justifyContent = 'center'; |
|
|
modal.style.zIndex = 9999; |
|
|
modal.style.zIndex = 9999; |
|
@ -169,7 +216,6 @@ |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
`; |
|
|
document.body.appendChild(modal); |
|
|
document.body.appendChild(modal); |
|
|
// Cerrar modal al hacer clic fuera de la imagen o en la X |
|
|
|
|
|
modal.addEventListener('click', function(e) { |
|
|
modal.addEventListener('click', function(e) { |
|
|
if (e.target === modal || e.target.id === 'cerrar-modal-img') { |
|
|
if (e.target === modal || e.target.id === 'cerrar-modal-img') { |
|
|
modal.style.display = 'none'; |
|
|
modal.style.display = 'none'; |
|
@ -177,7 +223,6 @@ |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Asignar evento a miniaturas |
|
|
|
|
|
function asignarEventosMiniaturas() { |
|
|
function asignarEventosMiniaturas() { |
|
|
crearModalImagen(); |
|
|
crearModalImagen(); |
|
|
document.querySelectorAll('.img-clickable').forEach(img => { |
|
|
document.querySelectorAll('.img-clickable').forEach(img => { |
|
@ -191,8 +236,158 @@ |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Inicializar eventos para miniaturas existentes (por si acaso) |
|
|
// Asignar eventos a los botones de editar, eliminar y PDF |
|
|
window.onload = asignarEventosMiniaturas; |
|
|
function asignarEventosAcciones() { |
|
|
|
|
|
document.querySelectorAll('.btn-accion.eliminar').forEach(btn => { |
|
|
|
|
|
btn.onclick = function() { |
|
|
|
|
|
Swal.fire({ |
|
|
|
|
|
title: '¿Estás seguro?', |
|
|
|
|
|
text: 'Esta acción no se puede deshacer', |
|
|
|
|
|
icon: 'warning', |
|
|
|
|
|
showCancelButton: true, |
|
|
|
|
|
confirmButtonColor: '#3085d6', |
|
|
|
|
|
cancelButtonColor: '#d33', |
|
|
|
|
|
confirmButtonText: 'Sí, eliminar', |
|
|
|
|
|
cancelButtonText: 'Cancelar' |
|
|
|
|
|
}).then((result) => { |
|
|
|
|
|
if (result.isConfirmed) { |
|
|
|
|
|
const fila = btn.closest('tr'); |
|
|
|
|
|
fila.remove(); |
|
|
|
|
|
Swal.fire({ |
|
|
|
|
|
icon: 'success', |
|
|
|
|
|
title: '¡Eliminado correctamente!', |
|
|
|
|
|
showConfirmButton: false, |
|
|
|
|
|
timer: 1500 |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
}; |
|
|
|
|
|
}); |
|
|
|
|
|
document.querySelectorAll('.btn-accion.editar').forEach(btn => { |
|
|
|
|
|
btn.onclick = function() { |
|
|
|
|
|
const fila = btn.closest('tr'); |
|
|
|
|
|
const celdas = fila.querySelectorAll('td'); |
|
|
|
|
|
document.getElementById('foto_gasolina_inicio').value = ''; |
|
|
|
|
|
document.getElementById('foto_vehiculo').value = ''; |
|
|
|
|
|
document.getElementById('foto_gasolina_fin').value = ''; |
|
|
|
|
|
document.getElementById('fecha_salida').value = celdas[3].innerText.replace(' ', 'T'); |
|
|
|
|
|
document.getElementById('fecha_regreso').value = celdas[4].innerText.replace(' ', 'T'); |
|
|
|
|
|
document.getElementById('motivo').value = celdas[5].innerText; |
|
|
|
|
|
filaEditando = fila; |
|
|
|
|
|
window.scrollTo({top:0, behavior:'smooth'}); |
|
|
|
|
|
}; |
|
|
|
|
|
}); |
|
|
|
|
|
document.querySelectorAll('.btn-accion.pdf').forEach(btn => { |
|
|
|
|
|
btn.onclick = function() { |
|
|
|
|
|
const fila = btn.closest('tr'); |
|
|
|
|
|
const celdas = fila.querySelectorAll('td'); |
|
|
|
|
|
generarPDFPersonalizado({ |
|
|
|
|
|
motivo: celdas[5].innerText.trim(), |
|
|
|
|
|
fechaSalida: celdas[3].innerText.trim(), |
|
|
|
|
|
fechaRegreso: celdas[4].innerText.trim(), |
|
|
|
|
|
imagenes: [ |
|
|
|
|
|
fila.querySelectorAll('img.miniatura-img')[0]?.src, |
|
|
|
|
|
fila.querySelectorAll('img.miniatura-img')[1]?.src, |
|
|
|
|
|
fila.querySelectorAll('img.miniatura-img')[2]?.src |
|
|
|
|
|
].filter(Boolean), |
|
|
|
|
|
callback: function() { |
|
|
|
|
|
Swal.fire({ |
|
|
|
|
|
icon: 'success', |
|
|
|
|
|
title: '¡PDF generado!', |
|
|
|
|
|
showConfirmButton: false, |
|
|
|
|
|
timer: 1500 |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
}; |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Generar PDF con la estructura solicitada, sin recuadros y fechas bien alineadas |
|
|
|
|
|
function generarPDFPersonalizado(data) { |
|
|
|
|
|
const { jsPDF } = window.jspdf; |
|
|
|
|
|
const doc = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'letter' }); |
|
|
|
|
|
const pageWidth = doc.internal.pageSize.getWidth(); |
|
|
|
|
|
const pageHeight = doc.internal.pageSize.getHeight(); |
|
|
|
|
|
|
|
|
|
|
|
// Título |
|
|
|
|
|
doc.setFont('helvetica', 'bold'); |
|
|
|
|
|
doc.setFontSize(30); |
|
|
|
|
|
doc.text('PRESTAMOS TECMM', pageWidth / 2, 60, { align: 'center' }); |
|
|
|
|
|
|
|
|
|
|
|
// REPORTE |
|
|
|
|
|
doc.setFontSize(13); |
|
|
|
|
|
doc.setFont('helvetica', 'bold'); |
|
|
|
|
|
doc.text('REPORTE', 40, 110); |
|
|
|
|
|
doc.setFont('helvetica', 'normal'); |
|
|
|
|
|
doc.setFontSize(12); |
|
|
|
|
|
doc.text(data.motivo || '', 40, 130, { maxWidth: 250 }); |
|
|
|
|
|
|
|
|
|
|
|
// FECHA Y HORA (alineado a la derecha, una debajo de otra) |
|
|
|
|
|
doc.setFont('helvetica', 'bold'); |
|
|
|
|
|
doc.setFontSize(13); |
|
|
|
|
|
doc.text('FECHA Y HORA', 320, 110); |
|
|
|
|
|
doc.setFont('helvetica', 'normal'); |
|
|
|
|
|
doc.setFontSize(12); |
|
|
|
|
|
doc.text(`Salida: ${data.fechaSalida || ''}`, 320, 130); |
|
|
|
|
|
doc.text(`Regreso: ${data.fechaRegreso || ''}`, 320, 150); |
|
|
|
|
|
|
|
|
|
|
|
// LAS 3 IMÁGENES |
|
|
|
|
|
doc.setFont('helvetica', 'bold'); |
|
|
|
|
|
doc.setFontSize(13); |
|
|
|
|
|
doc.text('LAS 3 IMÁGENES', 320, 190); |
|
|
|
|
|
let yImg = 210; |
|
|
|
|
|
let xImg = 320; |
|
|
|
|
|
const imagenes = (data.imagenes || []).filter(Boolean); |
|
|
|
|
|
let cargadas = 0; |
|
|
|
|
|
if (imagenes.length > 0) { |
|
|
|
|
|
imagenes.forEach((src, idx) => { |
|
|
|
|
|
if (src && src.startsWith('blob:')) { |
|
|
|
|
|
const img = new window.Image(); |
|
|
|
|
|
img.crossOrigin = ''; |
|
|
|
|
|
img.onload = function() { |
|
|
|
|
|
doc.addImage(img, 'PNG', xImg, yImg, 60, 60); |
|
|
|
|
|
xImg += 70; |
|
|
|
|
|
cargadas++; |
|
|
|
|
|
if (cargadas === imagenes.length) { |
|
|
|
|
|
// Firma |
|
|
|
|
|
doc.setFont('helvetica', 'normal'); |
|
|
|
|
|
doc.setFontSize(15); |
|
|
|
|
|
doc.line(150, 500, 350, 500); |
|
|
|
|
|
doc.text('FIRMA', pageWidth / 2, 520, { align: 'center' }); |
|
|
|
|
|
doc.save('reporte_prestamo.pdf'); |
|
|
|
|
|
if (typeof data.callback === 'function') data.callback(); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
img.onerror = function() { |
|
|
|
|
|
cargadas++; |
|
|
|
|
|
if (cargadas === imagenes.length) { |
|
|
|
|
|
doc.setFont('helvetica', 'normal'); |
|
|
|
|
|
doc.setFontSize(15); |
|
|
|
|
|
doc.line(150, 500, 350, 500); |
|
|
|
|
|
doc.text('FIRMA', pageWidth / 2, 520, { align: 'center' }); |
|
|
|
|
|
doc.save('reporte_prestamo.pdf'); |
|
|
|
|
|
if (typeof data.callback === 'function') data.callback(); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
img.src = src; |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
} else { |
|
|
|
|
|
// Firma |
|
|
|
|
|
doc.setFont('helvetica', 'normal'); |
|
|
|
|
|
doc.setFontSize(15); |
|
|
|
|
|
doc.line(150, 500, 350, 500); |
|
|
|
|
|
doc.text('FIRMA', pageWidth / 2, 520, { align: 'center' }); |
|
|
|
|
|
doc.save('reporte_prestamo.pdf'); |
|
|
|
|
|
if (typeof data.callback === 'function') data.callback(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
window.onload = function() { |
|
|
|
|
|
asignarEventosMiniaturas(); |
|
|
|
|
|
asignarEventosAcciones(); |
|
|
|
|
|
}; |
|
|
</script> |
|
|
</script> |
|
|
</body> |
|
|
</body> |
|
|
@endsection |
|
|
@endsection |
|
|