232 lines
13 KiB
HTML
Executable File
232 lines
13 KiB
HTML
Executable File
<div x-data="modals" @keydown.escape="closeModal"
|
|
class="bg-white rounded-lg shadow max-w-2xl">
|
|
<div class="p-6">
|
|
<!-- Header -->
|
|
<div class="flex items-center gap-4 mb-6 pb-6 border-b border-slate-100">
|
|
<div class="w-14 h-14 rounded-2xl flex items-center justify-center text-white text-xl font-bold flex-shrink-0"
|
|
style="background: linear-gradient(135deg, #8eb02f 0%, #6d8c24 100%)"
|
|
x-data="{n:'{{ .user.NombreUsuario }}'}"
|
|
x-text="n ? n.charAt(0).toUpperCase() : 'U'">
|
|
</div>
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-slate-800">Mi perfil</h1>
|
|
<p class="text-xs text-slate-500 mt-0.5">Administra tu información personal y seguridad</p>
|
|
</div>
|
|
</div>
|
|
|
|
<form @submit.prevent="submitForm" class="space-y-5">
|
|
<!-- Nombre -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-700 mb-1.5" for="name">Nombre completo</label>
|
|
<div class="relative">
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z"/>
|
|
</svg>
|
|
</div>
|
|
<input type="text" id="name" x-model="Name" placeholder="Ingresa tu nombre completo"
|
|
class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl text-sm outline-none bg-white text-slate-800 transition-all"
|
|
style="box-shadow:none"
|
|
onfocus="this.style.borderColor='#8eb02f'; this.style.boxShadow='0 0 0 3px rgba(142,176,47,0.15)'"
|
|
onblur="this.style.borderColor='#e2e8f0'; this.style.boxShadow='none'"
|
|
required>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Email -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-700 mb-1.5" for="Email">Correo electrónico</label>
|
|
<div class="relative">
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75"/>
|
|
</svg>
|
|
</div>
|
|
<input id="Email" x-model="Email" placeholder="correo@ejemplo.com" type="email"
|
|
class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl text-sm outline-none bg-white text-slate-800 transition-all"
|
|
style="box-shadow:none"
|
|
onfocus="this.style.borderColor='#8eb02f'; this.style.boxShadow='0 0 0 3px rgba(142,176,47,0.15)'"
|
|
onblur="this.style.borderColor='#e2e8f0'; this.style.boxShadow='none'"
|
|
required>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Actions -->
|
|
<div class="flex items-center gap-3 pt-2">
|
|
<button type="submit"
|
|
class="flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-semibold text-white transition-all"
|
|
style="background-color:#8eb02f"
|
|
onmouseover="if(!this.disabled) this.style.backgroundColor='#6d8c24'"
|
|
onmouseout="if(!this.disabled) this.style.backgroundColor='#8eb02f'"
|
|
:disabled="isLoading"
|
|
:style="isLoading ? 'opacity:0.7;cursor:not-allowed' : ''">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/>
|
|
</svg>
|
|
<span x-show="!isLoading">Guardar cambios</span>
|
|
<span x-show="isLoading">Guardando...</span>
|
|
</button>
|
|
<button type="button"
|
|
class="flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-semibold bg-slate-100 text-slate-700 hover:bg-slate-200 transition-colors"
|
|
x-on:click="showPasswordModal = true;">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z"/>
|
|
</svg>
|
|
Cambiar contraseña
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<!-- Modal cambiar contraseña -->
|
|
<div x-cloak x-show="showPasswordModal"
|
|
x-transition:enter="transition ease-out duration-200"
|
|
x-transition:enter-start="opacity-0"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in duration-150"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
class="fixed inset-0 bg-slate-900/60 backdrop-blur-sm flex z-50 items-center justify-center p-4">
|
|
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-sm overflow-hidden"
|
|
x-transition:enter="transition ease-out duration-200"
|
|
x-transition:enter-start="opacity-0 scale-95"
|
|
x-transition:enter-end="opacity-100 scale-100">
|
|
<!-- Modal Header -->
|
|
<div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-8 h-8 rounded-lg flex items-center justify-center" style="background-color:rgba(142,176,47,.1)">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" style="color:#8eb02f" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="text-sm font-semibold text-slate-800">Nueva contraseña</h3>
|
|
</div>
|
|
<button @click="closeModal" class="p-1.5 rounded-lg text-slate-400 hover:text-slate-600 hover:bg-slate-100 transition-colors">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<!-- Modal Body -->
|
|
<form @submit.prevent="changePass" class="px-6 py-5 space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-700 mb-1.5" for="password">Nueva contraseña</label>
|
|
<input type="password" id="password" x-model="password"
|
|
placeholder="Mínimo 8 caracteres"
|
|
class="w-full px-3 py-2.5 border border-slate-200 rounded-xl text-sm outline-none bg-white transition-all"
|
|
style="box-shadow:none"
|
|
onfocus="this.style.borderColor='#8eb02f'; this.style.boxShadow='0 0 0 3px rgba(142,176,47,0.15)'"
|
|
onblur="this.style.borderColor='#e2e8f0'; this.style.boxShadow='none'"
|
|
required>
|
|
</div>
|
|
<div class="flex gap-3 pt-1">
|
|
<button type="submit"
|
|
class="flex-1 py-2.5 rounded-xl text-sm font-semibold text-white transition-all"
|
|
style="background-color:#8eb02f"
|
|
onmouseover="this.style.backgroundColor='#6d8c24'"
|
|
onmouseout="this.style.backgroundColor='#8eb02f'">
|
|
Guardar
|
|
</button>
|
|
<button type="button"
|
|
class="flex-1 py-2.5 rounded-xl text-sm font-semibold bg-slate-100 text-slate-700 hover:bg-slate-200 transition-colors"
|
|
x-on:click="closeModal">
|
|
Cancelar
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<!-- Alpine.js Script -->
|
|
<script>
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.data('modals', () => ({
|
|
showPasswordModal: false,
|
|
Name: '',
|
|
Email: '',
|
|
password: '',
|
|
Usuario: '',
|
|
rolID: '',
|
|
Id: null,
|
|
isLoading: false,
|
|
usuarioID: '{{ .user.ID }}', // Inicializar directamente en el Alpine.js
|
|
|
|
init() {
|
|
this.loadRegister();
|
|
},
|
|
|
|
loadRegister() {
|
|
this.isLoading = true;
|
|
fetch(`/app/user/` + this.usuarioID)
|
|
.then(response => {
|
|
if (!response.ok) throw new Error('Error al cargar el usuario');
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
this.Name = data.user.name || '';
|
|
this.Email = data.user.email || '';
|
|
this.Usuario = data.user.nombre_usuario || '';
|
|
this.rolID = data.user.role_id || '';
|
|
})
|
|
.catch(error => alert(`Error: ${error.message}`)) // Manejo de errores
|
|
.finally(() => {
|
|
this.isLoading = false;
|
|
});
|
|
},
|
|
|
|
closeModal() {
|
|
this.showPasswordModal = false;
|
|
},
|
|
|
|
submitForm() {
|
|
this.isLoading = true;
|
|
fetch(`/app/users/` + this.usuarioID, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
name: this.Name,
|
|
email: this.Email,
|
|
nombre_usuario: this.Usuario,
|
|
role_id: this.rolID,
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) throw new Error('Error al actualizar el usuario');
|
|
this.loadRegister();
|
|
return response.json();
|
|
})
|
|
.then(() => {
|
|
alert('Usuario actualizado con éxito');
|
|
})
|
|
.catch(error => alert(`Error: ${error.message}`))
|
|
.finally(() => {
|
|
this.isLoading = false;
|
|
});
|
|
},
|
|
|
|
changePass() {
|
|
this.isLoading = true;
|
|
fetch(`/app/password/` + this.usuarioID, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
password: this.password,
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) throw new Error('Error al cambiar la contraseña');
|
|
return response.json();
|
|
})
|
|
.then(() => {
|
|
this.password = '';
|
|
this.closeModal();
|
|
alert('Contraseña cambiada con éxito');
|
|
})
|
|
.catch(error => alert(`Error: ${error.message}`))
|
|
.finally(() => {
|
|
this.isLoading = false;
|
|
});
|
|
},
|
|
}));
|
|
});
|
|
</script>
|
|
</div>
|