Add empleado, borrado del seeder, cambios esteticos y middleware.

This commit is contained in:
2026-06-10 15:57:51 -03:00
parent d0d61f7985
commit 2454ee3a23
20 changed files with 1039 additions and 200 deletions
@@ -0,0 +1,323 @@
<x-layout title="Empleados - Lauck">
<x-section-header subtitle="Personal" title="Administración de " highlight="Empleados" />
<div class="w-full max-w-5xl mx-auto space-y-6">
<!-- Formulario de Registro de Empleado -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl p-6 shadow-lg">
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-4">Registrar Nuevo Empleado</h3>
<form action="{{ route('admin.employees.store') }}" method="POST" class="space-y-4">
@csrf
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<!-- Nombre -->
<div>
<x-forms.label for="name" value="Nombre Completo" />
<x-forms.input id="name" name="name" type="text" required placeholder="Ej: Juan Pérez" :value="old('name')" :error="$errors->first('name')" />
</div>
<!-- Correo -->
<div>
<x-forms.label for="email" value="Correo Electrónico" />
<x-forms.input id="email" name="email" type="email" required placeholder="empleado@lauck.com" :value="old('email')" :error="$errors->first('email')" />
</div>
<!-- Botón Registrar (Desktop: abajo-derecha de la primera fila, o alineado. Pongámoslo al final) -->
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Contraseña -->
<div>
<x-forms.label for="password" value="Contraseña (Mín. 8 caracteres)" />
<div class="relative">
<x-forms.input id="password" name="password" type="password" required placeholder="••••••••" :error="$errors->first('password')" class="pr-10" />
<button type="button" id="togglePassword" class="absolute right-3 top-[11px] text-neutral-500 hover:text-black dark:hover:text-white transition-colors">
{{-- Ojo abierto --}}
<svg id="iconShow" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
{{-- Ojo cerrado --}}
<svg id="iconHide" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.477 0-8.268-2.943-9.542-7a9.956 9.956 0 012.293-3.95M6.938 6.938A9.956 9.956 0 0112 5c4.477 0 8.268 2.943 9.542 7a9.97 9.97 0 01-1.88 3.118M3 3l18 18" />
</svg>
</button>
</div>
</div>
<!-- Confirmar Contraseña -->
<div>
<x-forms.label for="password_confirmation" value="Confirmar Contraseña" />
<div class="relative">
<x-forms.input id="password_confirmation" name="password_confirmation" type="password" required placeholder="••••••••" class="pr-10" />
<button type="button" id="togglePasswordConf" class="absolute right-3 top-[11px] text-neutral-500 hover:text-black dark:hover:text-white transition-colors">
{{-- Ojo abierto --}}
<svg id="iconShowConf" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
{{-- Ojo cerrado --}}
<svg id="iconHideConf" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.477 0-8.268-2.943-9.542-7a9.956 9.956 0 012.293-3.95M6.938 6.938A9.956 9.956 0 0112 5c4.477 0 8.268 2.943 9.542 7a9.97 9.97 0 01-1.88 3.118M3 3l18 18" />
</svg>
</button>
</div>
</div>
</div>
<div class="flex justify-end pt-2">
<button type="submit" class="w-full md:w-auto px-6 py-2.5 bg-neutral-900 dark:bg-neon-lime text-neon-lime dark:text-neutral-900 font-bold rounded-lg hover:bg-neutral-800 hover:dark:bg-[#b3e600] transition-colors shadow-lg dark:shadow-neon-lime/20 uppercase text-xs tracking-wider">
Registrar Empleado
</button>
</div>
</form>
</div>
<!-- Tabla de Empleados -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl shadow-lg overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full text-sm text-left text-neutral-900 dark:text-white">
<thead class="text-xs text-neutral-800 dark:text-gray-300 uppercase bg-gray-400 dark:bg-neutral-800 border-b border-gray-300 dark:border-neutral-700">
<tr>
<th scope="col" class="px-6 py-3">Nombre</th>
<th scope="col" class="px-6 py-3">Correo Electrónico</th>
<th scope="col" class="px-6 py-3">Fecha de Registro</th>
<th scope="col" class="px-6 py-3 text-right">Acciones</th>
</tr>
</thead>
<tbody>
@forelse($employees as $employee)
<tr class="bg-gray-200/50 dark:bg-neutral-900/50 border-b border-gray-400 dark:border-neutral-800 hover:bg-gray-400 hover:dark:bg-neutral-800 transition-colors group">
<td class="px-6 py-4 font-bold text-neutral-900 dark:text-white">
{{ $employee->name }}
</td>
<td class="px-6 py-4 font-mono text-neutral-600 dark:text-gray-400">
{{ $employee->email }}
</td>
<td class="px-6 py-4 text-neutral-700 dark:text-gray-300">
{{ $employee->created_at->format('d/m/Y H:i') }}
</td>
<td class="px-6 py-4 text-right">
<div class="flex items-center justify-end gap-2">
<!-- Editar -->
<button onclick="openEditModal({{ $employee->id }}, '{{ addslashes($employee->name) }}', '{{ addslashes($employee->email) }}')" title="Editar Empleado" class="font-semibold p-2 text-blue-800 dark:text-blue-400 border-2 border-blue-800 rounded-lg hover:bg-blue-800 hover:text-white dark:hover:text-white transition-colors">
<svg class="size-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m14.304 4.844 2.852 2.852M7 7H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1v-4.5m2.409-9.91a2.017 2.017 0 0 1 0 2.853l-6.844 6.844L8 14l.713-3.565 6.844-6.844a2.015 2.015 0 0 1 2.852 0Z"/>
</svg>
</button>
<!-- Eliminar -->
<form action="{{ route('admin.employees.destroy', $employee) }}" method="POST" class="delete-form inline">
@csrf
@method('DELETE')
<button title="Eliminar Empleado" type="submit" class="p-2 font-bold dark:font-medium text-red-600 dark:text-red-400 border-2 border-red-600 rounded-lg hover:bg-red-600 hover:text-white dark:hover:text-white transition-colors">
<svg class="size-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 7h14m-9 3v8m4-8v8M10 3h4a1 1 0 0 1 1 1v3H9V4a1 1 0 0 1 1-1ZM6 7h12v13a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7Z"/>
</svg>
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="px-6 py-10 text-center text-gray-500 dark:text-gray-400 font-semibold">
No hay empleados registrados actualmente.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
<!-- Modal de Edición de Empleado -->
<div id="editEmployeeModal"
class="hidden fixed inset-0 z-50 flex items-center justify-center p-4 bg-neutral-950/60 backdrop-blur-sm transition-all duration-300">
<div class="relative p-6 rounded-2xl w-full max-w-md shadow-2xl border transition-all transform scale-100
bg-white border-neutral-200 text-neutral-900
dark:bg-neutral-900 dark:border-neutral-800 dark:text-white">
<!-- Close button (X) -->
<button type="button" onclick="closeEditModal()" class="absolute top-4 right-4 text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-350 transition-colors p-1.5 rounded-lg hover:bg-neutral-100 dark:hover:bg-neutral-800">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<!-- Header -->
<div class="mb-5 pb-3 border-b border-neutral-200 dark:border-neutral-800 pr-8">
<h3 class="text-lg font-black text-neutral-900 dark:text-white flex items-center gap-2">
<svg class="w-5 h-5 text-neutral-500 dark:text-neutral-400" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.83 21.75a0.75 0.75 0 01-0.343.203l-4.852 1.213a0.075 0.075 0 01-0.09-.09l1.213-4.852a0.75 0.75 0 01.203-.343L16.862 4.487zm0 0L19.5 7.125" />
</svg>
Editar Empleado
</h3>
</div>
<form id="editEmployeeForm" method="POST" class="space-y-4">
@csrf
@method('PUT')
<!-- Nombre -->
<div>
<x-forms.label for="edit_name" value="Nombre Completo" />
<x-forms.input id="edit_name" name="name" type="text" required placeholder="Ej: Juan Pérez" />
</div>
<!-- Correo -->
<div>
<x-forms.label for="edit_email" value="Correo Electrónico" />
<x-forms.input id="edit_email" name="email" type="email" required placeholder="empleado@lauck.com" />
</div>
<!-- Contraseña opcional -->
<div>
<x-forms.label for="edit_password" value="Nueva Contraseña (Opcional)" />
<div class="relative">
<x-forms.input id="edit_password" name="password" type="password" placeholder="Dejar vacío para no cambiar" class="pr-10" />
<button type="button" id="toggleEditPassword" class="absolute right-3 top-[11px] text-neutral-500 hover:text-black dark:hover:text-white transition-colors">
{{-- Ojo abierto --}}
<svg id="iconEditShow" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
{{-- Ojo cerrado --}}
<svg id="iconEditHide" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.477 0-8.268-2.943-9.542-7a9.956 9.956 0 012.293-3.95M6.938 6.938A9.956 9.956 0 0112 5c4.477 0 8.268 2.943 9.542 7a9.97 9.97 0 01-1.88 3.118M3 3l18 18" />
</svg>
</button>
</div>
</div>
<!-- Confirmar Contraseña opcional -->
<div>
<x-forms.label for="edit_password_confirmation" value="Confirmar Nueva Contraseña" />
<div class="relative">
<x-forms.input id="edit_password_confirmation" name="password_confirmation" type="password" placeholder="Dejar vacío para no cambiar" class="pr-10" />
<button type="button" id="toggleEditPasswordConf" class="absolute right-3 top-[11px] text-neutral-500 hover:text-black dark:hover:text-white transition-colors">
{{-- Ojo abierto --}}
<svg id="iconEditShowConf" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
{{-- Ojo cerrado --}}
<svg id="iconEditHideConf" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.477 0-8.268-2.943-9.542-7a9.956 9.956 0 012.293-3.95M6.938 6.938A9.956 9.956 0 0112 5c4.477 0 8.268 2.943 9.542 7a9.97 9.97 0 01-1.88 3.118M3 3l18 18" />
</svg>
</button>
</div>
</div>
<!-- Acciones -->
<div class="flex justify-end gap-2 pt-3 border-t border-neutral-100 dark:border-neutral-800">
<button type="button" onclick="closeEditModal()"
class="px-4 py-2 text-xs font-bold rounded-lg transition-colors bg-neutral-100 hover:bg-neutral-200 text-neutral-700 dark:bg-neutral-800 dark:hover:bg-neutral-700 dark:text-white">
Cancelar
</button>
<button type="submit"
class="px-4 py-2 text-xs font-bold rounded-lg transition-colors bg-neutral-950 hover:bg-neutral-900/80 text-neon-lime dark:bg-neon-lime dark:hover:bg-[#b3e600] dark:text-neutral-900">
Guardar Cambios
</button>
</div>
</form>
</div>
</div>
@push('scripts')
<script>
function openEditModal(id, name, email) {
const modal = document.getElementById('editEmployeeModal');
const form = document.getElementById('editEmployeeForm');
document.getElementById('edit_name').value = name;
document.getElementById('edit_email').value = email;
document.getElementById('edit_password').value = '';
document.getElementById('edit_password_confirmation').value = '';
form.action = `/admin/employees/${id}`;
modal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
}
function closeEditModal() {
const modal = document.getElementById('editEmployeeModal');
modal.classList.add('hidden');
document.body.style.overflow = 'auto';
}
document.addEventListener('DOMContentLoaded', function() {
// Cierre por click en backdrop
const editModal = document.getElementById('editEmployeeModal');
if (editModal) {
editModal.addEventListener('click', function(e) {
if (e.target === editModal) {
closeEditModal();
}
});
}
// Toggle contraseña principal (Registro)
const togglePassword = document.getElementById('togglePassword');
const passwordInput = document.getElementById('password');
const iconShow = document.getElementById('iconShow');
const iconHide = document.getElementById('iconHide');
if (togglePassword && passwordInput) {
togglePassword.addEventListener('click', function() {
const isPassword = passwordInput.getAttribute('type') === 'password';
passwordInput.setAttribute('type', isPassword ? 'text' : 'password');
iconShow.classList.toggle('hidden', isPassword);
iconHide.classList.toggle('hidden', !isPassword);
});
}
// Toggle confirmar contraseña (Registro)
const togglePasswordConf = document.getElementById('togglePasswordConf');
const passwordConfInput = document.getElementById('password_confirmation');
const iconShowConf = document.getElementById('iconShowConf');
const iconHideConf = document.getElementById('iconHideConf');
if (togglePasswordConf && passwordConfInput) {
togglePasswordConf.addEventListener('click', function() {
const isPassword = passwordConfInput.getAttribute('type') === 'password';
passwordConfInput.setAttribute('type', isPassword ? 'text' : 'password');
iconShowConf.classList.toggle('hidden', isPassword);
iconHideConf.classList.toggle('hidden', !isPassword);
});
}
// Toggle contraseña (Edición)
const toggleEditPassword = document.getElementById('toggleEditPassword');
const editPasswordInput = document.getElementById('edit_password');
const iconEditShow = document.getElementById('iconEditShow');
const iconEditHide = document.getElementById('iconEditHide');
if (toggleEditPassword && editPasswordInput) {
toggleEditPassword.addEventListener('click', function() {
const isPassword = editPasswordInput.getAttribute('type') === 'password';
editPasswordInput.setAttribute('type', isPassword ? 'text' : 'password');
iconEditShow.classList.toggle('hidden', isPassword);
iconEditHide.classList.toggle('hidden', !isPassword);
});
}
// Toggle confirmar contraseña (Edición)
const toggleEditPasswordConf = document.getElementById('toggleEditPasswordConf');
const editPasswordConfInput = document.getElementById('edit_password_confirmation');
const iconEditShowConf = document.getElementById('iconEditShowConf');
const iconEditHideConf = document.getElementById('iconEditHideConf');
if (toggleEditPasswordConf && editPasswordConfInput) {
toggleEditPasswordConf.addEventListener('click', function() {
const isPassword = editPasswordConfInput.getAttribute('type') === 'password';
editPasswordConfInput.setAttribute('type', isPassword ? 'text' : 'password');
iconEditShowConf.classList.toggle('hidden', isPassword);
iconEditHideConf.classList.toggle('hidden', !isPassword);
});
}
});
</script>
@endpush
</x-layout>