Files
soft_usite/resources/views/actualizacion_datos_usuarios.html
T
2025-02-06 14:22:29 -05:00

477 lines
26 KiB
HTML

<div x-data="app" class="bg-white rounded-lg shadow">
<div x-show="loading" class="fixed inset-0 bg-gray-800 bg-opacity-75 flex z-50 justify-center items-center">
<img src="../img/loading.gif" alt="Cargando..." class="w-16 h-16" />
</div>
<div class="container mx-auto p-6 w-full">
<div class="justify-between items-center w-full md:flex">
<div>
<h1 class="text-2xl font-bold mb-2">Lista de actualizaciones de datos de usuario</h1>
<p class="mb-4 text-sm">Gestión de actualizaciones de datos de usuario.</p>
</div>
<div class="mb-4 md:mb-0 relative">
<input type="text" placeholder="Buscar..." class="border border-gray-300 rounded p-2 w-full"
x-model="search" @input.debounce.500ms="loadData()" />
</div>
<div class="flex itmems-center justify-end mb-4 md:mb-0">
<button @click="exportToExcel()" class="bg-[#8eb02f] text-white px-4 py-2 rounded text-sm">Exportar a
Excel</button>
</div>
</div>
<div class="overflow-x-auto">
<table class="table-auto w-full">
<thead class="text-sm text-left py-4 border-b border-gray-300">
<tr class="text-left font-semibold border-collapse">
<th class="py-2 px-4 border-b">Código</th>
<th class="py-2 px-4 border-b">Nombre</th>
<th class="py-2 px-4 border-b">Tipo persona</th>
<th class="py-2 px-4 border-b">Celular</th>
<th class="py-2 px-4 border-b">Celular alternativo</th>
<th class="py-2 px-4 border-b">Correo</th>
<th class="py-2 px-4 border-b">Dirección</th>
<th class="py-2 px-4 border-b">Tipo documento</th>
<th class="py-2 px-4 border-b">N° documento</th>
<th class="py-2 px-4 border-b">OTP</th>
<th class="py-2 px-4 border-b">Revisado</th>
<th class="py-2 px-4 border-b">Fecha registro</th>
<th class="py-2 px-4 border-b"></th>
</tr>
</thead>
<tbody class="text-gray-500 select-none text-sm">
<template x-for="data in datos" :key="data.id">
<tr class="hover:bg-gray-100">
<td class="py-3 text-xs px-4 border-b" x-text="data.codigo"></td>
<td class="py-3 text-xs px-4 border-b" x-text="data.name"></td>
<td class="py-3 text-xs px-4 border-b" x-text="data.tipo_persona"></td>
<td class="py-3 text-xs px-4 border-b" x-text="data.movil1"></td>
<td class="py-3 text-xs px-4 border-b" x-text="data.movil2"></td>
<td class="py-3 text-xs px-4 border-b" x-text="data.email"></td>
<td class="py-3 text-xs px-4 border-b" x-text="data.direccion"></td>
<td class="py-3 text-xs px-4 border-b" x-text="data.tipo_documento"></td>
<td class="py-3 text-xs px-4 border-b" x-text="data.documento"></td>
<td class="py-3 text-xs px-4 border-b">
<div class="flex items-center">
<div :class="data.validacion_otp ? 'bg-green-500' : 'bg-gray-400'"
class="w-3 h-3 rounded-full mr-2"></div>
<span x-text="data.validacion_otp ? 'Validado' : 'No Validado'"></span>
</div>
</td>
<td class="py-3 text-xs px-4 border-b">
<div class="flex items-center">
<div :class="data.revisado ? 'bg-green-500' : 'bg-gray-400'"
class="w-3 h-3 rounded-full mr-2"></div>
<span x-text="data.revisado ? 'Revisado' : 'No Revisado'"></span>
</div>
</td>
<td class="py-3 text-xs px-4 border-b"
x-text="data.created_at ? new Date(data.created_at).toLocaleDateString('es-ES', { year: 'numeric', month: 'long', day: 'numeric' }) : 'Fecha no disponible'">
</td>
<td class="py-3 text-xs px-4 border-b">
<div class="flex items-center gap-2">
<button @click="openAddModal(data)" title="Añadir nota">
<svg class="w-5 text-gray-500" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12H14M12 10V14M19.9592 15H16.6C16.0399 15 15.7599 15 15.546 15.109C15.3578 15.2049 15.2049 15.3578 15.109 15.546C15 15.7599 15 16.0399 15 16.6V19.9592M20 14.1031V7.2C20 6.07989 20 5.51984 19.782 5.09202C19.5903 4.71569 19.2843 4.40973 18.908 4.21799C18.4802 4 17.9201 4 16.8 4H7.2C6.0799 4 5.51984 4 5.09202 4.21799C4.71569 4.40973 4.40973 4.71569 4.21799 5.09202C4 5.51984 4 6.0799 4 7.2V16.8C4 17.9201 4 18.4802 4.21799 18.908C4.40973 19.2843 4.71569 19.5903 5.09202 19.782C5.51984 20 6.0799 20 7.2 20H14.1031C14.5923 20 14.8369 20 15.067 19.9447C15.2711 19.8957 15.4662 19.8149 15.6451 19.7053C15.847 19.5816 16.0199 19.4086 16.3658 19.0627L19.0627 16.3658C19.4086 16.0199 19.5816 15.847 19.7053 15.6451C19.8149 15.4662 19.8957 15.2711 19.9447 15.067C20 14.8369 20 14.5923 20 14.1031Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</button>
<button @click="openViewModal(data)" title="Ver">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 text-[#8eb02f]">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
</button>
<!--button @click="openEditModal(data)" title="Editar">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5">
<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
</svg>
</button>
<button @click="openDeleteModal(data)" title="Eliminar">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 text-red-500">
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
</svg>
</button-->
</div>
</td>
</tr>
</template>
</tbody>
</table>
</div>
<div class="flex justify-start items-center gap-2 p-4 text-sm">
<button @click="page = Math.max(1, page - 1); loadData()" :disabled="page === 1"
class="px-4 py-2 text-black bg-gray-200 rounded-l-md opacity-50 disabled:cursor-not-allowed">
Anterior
</button>
<template x-for="pageNum in paginatedPages" :key="pageNum">
<button @click="goToPage(pageNum)" class="px-4 py-2 text-black bg-gray-200 bg-black text-gray-50 rounded-md hover:bg-gray-300"
:class="{ 'bg-black text-white': pageNum === page }" x-text="pageNum"></button>
</template>
<button @click="page += 1; loadData()" :disabled="datos.length < limit" :disabled="page === totalPages"
class="px-4 py-2 text-black bg-gray-200 rounded-r-md opacity-50 disabled:cursor-not-allowed">
Siguiente
</button>
</div>
</div>
<!-- Modales -->
<div x-show="viewModal" x-transition:enter="transition ease-out duration-300 transform" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-200 transform" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"
class="fixed inset-0 overflow-auto bg-gray-800 bg-opacity-75 flex justify-center items-center text-sm z-50">
<div class="bg-white rounded-lg shadow-lg p-6 w-11/12 max-w-lg">
<h2 class="text-xl font-bold mb-4">Ver actualizacion de datos de usuario</h2>
<div class="w-full mb-4">
<label class="block mb-2">Tipo de usuario:</label>
<input type="text" x-model="selectedItem.tipo_usuario" disabled
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el tipo de usuario" />
</div>
<div class="flex flex-col sm:flex-row justify-between gap-4 mb-4">
<div class="w-full">
<label class="block mb-2">Tipo de persona:</label>
<input type="text" x-model="selectedItem.tipo_persona" disabled
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el tipo de persona" />
</div>
<div class="w-full">
<label class="block mb-2">Nombre:</label>
<input type="text" x-model="selectedItem.name" disabled
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el nombre" />
</div>
</div>
<div class="flex flex-col sm:flex-row justify-between gap-4 mb-4">
<div class="w-full">
<label class="block mb-2">Tipo de documento:</label>
<input type="email" x-model="selectedItem.tipo_documento" disabled
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el email" />
</div>
<div class="w-full">
<label class="block mb-2">Documento:</label>
<input type="email" x-model="selectedItem.documento" disabled
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el email" />
</div>
</div>
<div class="flex flex-col sm:flex-row justify-between gap-4 mb-4">
<div class="w-full">
<label class="block mb-2">Celular:</label>
<input type="email" x-model="selectedItem.movil1" disabled
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el celular" />
</div>
<div class="w-full">
<label class="block mb-2">Celular alternativo:</label>
<input type="email" x-model="selectedItem.movil2" disabled
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el celular alternativo" />
</div>
</div>
<div class="flex flex-col sm:flex-row justify-between gap-4 mb-4">
<div class="w-full">
<label class="block mb-2">Email:</label>
<input type="email" x-model="selectedItem.email" disabled
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el email" />
</div>
<div class="w-full">
<label class="block mb-2">Dirección:</label>
<input type="email" x-model="selectedItem.direccion" disabled
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el direccion" />
</div>
</div>
<div class="flex justify-between gap-2 text-sm">
<button @click="addModal = true" class="px-4 py-2 bg-[#8eb02f] text-white rounded">Añadir nota</button>
<button @click="viewModal = false" class="px-4 py-2 bg-gray-600 text-white rounded">Cerrar</button>
</div>
</div>
</div>
<div x-show="editModal" x-transition:enter="transition ease-out duration-300 transform" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-200 transform" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"
class="fixed inset-0 overflow-auto bg-gray-800 bg-opacity-75 flex justify-center items-center text-sm z-50">
<div class="bg-white rounded-lg shadow-lg p-6 w-11/12 max-w-lg">
<h2 class="text-xl font-bold mb-4">Editar actualizacion de datos de usuario</h2>
<div class="w-full mb-4">
<label class="block mb-2">Tipo de usuario:</label>
<input type="text" x-model="selectedItem.tipo_usuario"
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el tipo de usuario" />
</div>
<div class="flex flex-col sm:flex-row justify-between gap-4 mb-4">
<div class="w-full">
<label class="block mb-2">Tipo de persona:</label>
<input type="text" x-model="selectedItem.tipo_persona"
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el tipo de persona" />
</div>
<div class="w-full">
<label class="block mb-2">Nombre:</label>
<input type="text" x-model="selectedItem.name"
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el nombre" />
<p x-show="errors.name" x-text="errors.name" class="text-red-500 text-sm"></p>
</div>
</div>
<div class="flex flex-col sm:flex-row justify-between gap-4 mb-4">
<div class="w-full">
<label class="block mb-2">Tipo de documento:</label>
<input type="email" x-model="selectedItem.tipo_documento"
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el email" />
<p x-show="errors.tipo_documento" x-text="errors.tipo_documento" class="text-red-500 text-sm"></p>
</div>
<div class="w-full">
<label class="block mb-2">Documento:</label>
<input type="email" x-model="selectedItem.documento"
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el email" />
<p x-show="errors.documento" x-text="errors.documento" class="text-red-500 text-sm"></p>
</div>
</div>
<div class="flex flex-col sm:flex-row justify-between gap-4 mb-4">
<div class="w-full">
<label class="block mb-2">Celular:</label>
<input type="email" x-model="selectedItem.movil1"
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el celular" />
</div>
<div class="w-full">
<label class="block mb-2">Celular alternativo:</label>
<input type="email" x-model="selectedItem.movil2"
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el celular alternativo" />
</div>
</div>
<div class="flex flex-col sm:flex-row justify-between gap-4 mb-4">
<div class="w-full">
<label class="block mb-2">Email:</label>
<input type="email" x-model="selectedItem.email"
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el email" />
</div>
<div class="w-full">
<label class="block mb-2">Dirección:</label>
<input type="email" x-model="selectedItem.direccion"
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el direccion" />
</div>
</div>
<div class="flex justify-between gap-2 text-sm">
<button @click="updateItem()" class="px-4 py-2 bg-[#8eb02f] text-white rounded">Actualizar</button>
<button @click="closeModals()" class="px-4 py-2 bg-gray-600 text-white rounded">Cerrar</button>
</div>
</div>
</div>
<div x-show="addModal" class="fixed inset-0 bg-gray-800 bg-opacity-75 flex justify-center items-center text-sm z-50">
<div class="bg-white rounded-lg shadow-lg p-6 w-11/12 max-w-lg">
<h2 class="text-xl font-bold mb-4">Añadir nota al cliente</h2>
<div class="mb-4">
<label class="block mb-2">Notas:</label>
<textarea x-model="item.notas" class="border border-gray-300 rounded p-2 w-full"
placeholder="Ingresa la nota"></textarea>
</div>
<div class="flex justify-between gap-2 text-sm">
<button @click="addNote()" class="px-4 py-2 bg-[#8eb02f] text-white rounded">Añadir nota</button>
<button @click="addModal = false" class="px-4 py-2 bg-gray-600 text-white rounded">Cancelar</button>
</div>
</div>
</div>
<div x-show="deleteModal" class="fixed inset-0 overflow-auto bg-gray-800 bg-opacity-75 flex justify-center items-center z-50">
<div class="bg-white rounded-lg shadow-lg p-6 w-11/12 max-w-lg">
<h2 class="text-xl font-bold mb-4">Confirmar Eliminación</h2>
<p>¿Estás seguro de que deseas eliminar este registro?</p>
<div class="flex mt-4 justify-end gap-2 text-sm">
<button @click="deleteItem()" class="px-4 py-2 bg-red-500 text-white rounded">Eliminar</button>
<button @click="closeModals()" class="bg-gray-600 text-white px-4 py-2 rounded">Cancelar</button>
</div>
</div>
</div>
</div>
<script>
function app() {
return {
datos: [],
loading: false,
limit: 10,
page: 1,
search: '',
totalPages: 0,
paginatedPages: [],
// modales
viewModal: false,
addModal: false,
editModal: false,
deleteModal: false,
// inputs
item: {
id: '', notas: ''
},
selectedItem: {},
errors: {},
loadData() {
if (this.search !== '') {
this.page = 1;
this.limit = 100;
} else {
this.limit = 10;
}
this.loading = true;
axios.get('/app/loadactualizaciondatosusuarios', {
params: {
page: this.page,
limit: this.limit,
search: this.search
}
}).then(response => {
this.datos = response.data.registros.map(data => ({
id: data.ID,
tipo_persona: data.tipo_persona,
name: data.name,
movil1: data.movil1,
movil2: data.movil2,
email: data.email,
codigo: data.codigo,
tipo_usuario: data.tipo_usuario,
direccion: data.direccion,
tipo_documento: data.tipo_documento,
documento: data.documento,
notas: data.notas,
revisado: data.revisado,
created_at: data.CreatedAt,
validacion_otp: data.validacion_otp
}));
this.totalPages = response.data.totalPages;
this.calculatePaginatedPages();
}).catch(error => {
console.error('Error al obtener datos:', error);
}).finally(() => {
this.loading = false;
});
},
setSelectedItem(data) {
this.selectedItem = { ...data };
this.errors = {};
},
resetSelectedItem() {
this.selectedItem = {};
this.errors = {};
},
calculatePaginatedPages() {
const maxVisiblePages = 5;
const startPage = Math.max(1, this.page - Math.floor(maxVisiblePages / 2));
const endPage = Math.min(this.totalPages, startPage + maxVisiblePages - 1);
this.paginatedPages = Array.from({ length: endPage - startPage + 1 }, (_, i) => i + startPage);
},
goToPage(page) {
this.page = page;
this.loadData();
},
setLoading(value) {
this.loading = value;
},
validateFields(fields) {
this.errors = {};
fields.forEach(field => {
if (!this.selectedItem[field]) {
this.errors[field] = 'Este campo es obligatorio';
}
});
return Object.keys(this.errors).length === 0;
},
init() {
this.loadData();
},
openViewModal(data) {
this.setSelectedItem(data);
this.viewModal = true;
},
openEditModal(data) {
this.setSelectedItem(data);
this.editModal = true;
},
updateItem() {
if (this.validateFields(['tipo_documento', 'documento', 'name'])) {
this.setLoading(true);
axios.put(`/app/actualizacion-datos-usuario/${this.selectedItem.id}`, this.selectedItem)
.then(() => {
alert("Registro actualizado exitosamente.");
this.resetSelectedItem();
this.loadData();
})
.catch(error => console.error("Error al actualizar el registro:", error))
.finally(() => {
this.closeModals()
this.setLoading(false);
});
}
},
deleteItem() {
this.setLoading(true);
axios.delete(`/app/actualizacion-datos-usuario/${this.selectedItem.id}`)
.then(() => {
alert("Registro eliminado exitosamente.");
this.resetSelectedItem();
this.loadData();
})
.catch(error => console.error("Error al eliminar el registro:", error))
.finally(() => this.setLoading(false));
},
openAddModal(data) {
this.selectedItem.id = data.id;
this.addModal = true;
},
openDeleteModal(data) {
this.selectedItem.id = data.id;
this.deleteModal = true;
},
closeModals(){
this.viewModal = false;
this.addModal = false;
this.editModal = false;
this.deleteModal = false;
},
addNote() {
if (this.selectedItem.id && this.item.notas) {
axios.put(`/app/actualizacion-datos-usuario/${this.selectedItem.id}`, {
notas: this.item.notas,
revisado: true
}).then(() => {
alert("Nota añadida exitosamente.");
this.item.notas = '';
this.addModal = false;
this.loadData();
}).catch(error => console.error("Error al añadir la nota:", error));
} else {
alert('Debe seleccionar un usuario y añadir una nota');
}
},
exportToExcel() {
axios.get('/app/export/actualizacion-datos-usuarios', { responseType: 'blob' })
.then(response => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'actualizacion_datos_usuarios.xlsx');
document.body.appendChild(link);
link.click();
})
.catch(error => console.error('Error al exportar datos:', error));
}
}
}
</script>