Files
soft_usite/resources/views/profile.html
T
2026-04-29 22:07:37 -05:00

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>