gp
This commit is contained in:
@@ -30,7 +30,6 @@ server: &server
|
||||
name: "USITE "
|
||||
host: 0.0.0.0
|
||||
port: 8080
|
||||
upload_size: 200
|
||||
|
||||
profiler:
|
||||
enabled: false
|
||||
|
||||
+105
-19
@@ -1,26 +1,112 @@
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition-duration: 0.2s;
|
||||
transition-property: opacity;
|
||||
transition-timing-function: ease;
|
||||
/* =============================================
|
||||
DESIGN SYSTEM — soft_usite
|
||||
Primary: #8eb02f | Secondary: #1e293b
|
||||
============================================= */
|
||||
|
||||
:root {
|
||||
--color-primary: #8eb02f;
|
||||
--color-primary-dark: #6d8c24;
|
||||
--color-primary-light: #b5d94a;
|
||||
--color-secondary: #1e293b;
|
||||
--color-secondary-light:#334155;
|
||||
--color-bg: #f1f5f9;
|
||||
--color-card: #ffffff;
|
||||
--color-text: #1e293b;
|
||||
--color-muted: #64748b;
|
||||
--color-border: #e2e8f0;
|
||||
--color-danger: #ef4444;
|
||||
--color-success: #22c55e;
|
||||
--color-warning: #f59e0b;
|
||||
--radius: 0.5rem;
|
||||
--radius-lg: 0.75rem;
|
||||
--shadow-sm: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06);
|
||||
--shadow: 0 4px 6px -1px rgba(0,0,0,.1), 0 2px 4px -1px rgba(0,0,0,.06);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -2px rgba(0,0,0,.05);
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
/* === Transitions (Alpine.js) === */
|
||||
.fade-enter-active, .fade-leave-active { transition: opacity .2s ease; }
|
||||
.fade-enter, .fade-leave-active { opacity: 0; }
|
||||
|
||||
.sidebar {
|
||||
overflow-y: scroll; /* Add the ability to scroll */
|
||||
}
|
||||
/* === Sidebar scrollbar === */
|
||||
.sidebar-scroll { overflow-y: auto; scrollbar-width: none; -ms-overflow-style: none; }
|
||||
.sidebar-scroll::-webkit-scrollbar { display: none; }
|
||||
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
.sidebar::-webkit-scrollbar {
|
||||
display: none;
|
||||
/* === Buttons === */
|
||||
.btn {
|
||||
display: inline-flex; align-items: center; justify-content: center; gap: .375rem;
|
||||
padding: .5rem 1rem; border-radius: var(--radius); font-size: .875rem; font-weight: 500;
|
||||
line-height: 1.25rem; transition: all .15s ease; cursor: pointer; border: none; outline: none;
|
||||
}
|
||||
.btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
|
||||
.btn-primary { background: var(--color-primary); color: #fff; }
|
||||
.btn-primary:hover { background: var(--color-primary-dark); }
|
||||
.btn-primary:disabled { opacity: .6; cursor: not-allowed; }
|
||||
.btn-secondary { background: var(--color-secondary); color: #fff; }
|
||||
.btn-secondary:hover { background: var(--color-secondary-light); }
|
||||
.btn-ghost { background: transparent; color: var(--color-muted); border: 1px solid var(--color-border); }
|
||||
.btn-ghost:hover { background: var(--color-bg); color: var(--color-text); }
|
||||
.btn-danger { background: #fee2e2; color: var(--color-danger); }
|
||||
.btn-danger:hover { background: var(--color-danger); color: #fff; }
|
||||
.btn-sm { padding: .25rem .625rem; font-size: .75rem; }
|
||||
.btn-icon { padding: .375rem; border-radius: .375rem; }
|
||||
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
.sidebar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
/* === Cards === */
|
||||
.card { background: var(--color-card); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); border: 1px solid var(--color-border); }
|
||||
.card-header { padding: 1.25rem 1.5rem; border-bottom: 1px solid var(--color-border); display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
||||
.card-body { padding: 1.5rem; }
|
||||
|
||||
/* === Data Tables === */
|
||||
.data-table { width: 100%; border-collapse: collapse; font-size: .875rem; }
|
||||
.data-table thead tr { background: #f8fafc; border-bottom: 2px solid var(--color-border); }
|
||||
.data-table thead th { padding: .75rem 1rem; text-align: left; font-weight: 600; color: var(--color-secondary); font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; white-space: nowrap; }
|
||||
.data-table tbody tr { border-bottom: 1px solid var(--color-border); transition: background .1s ease; }
|
||||
.data-table tbody tr:hover { background: #f8fafc; }
|
||||
.data-table td { padding: .875rem 1rem; color: var(--color-text); vertical-align: middle; }
|
||||
|
||||
/* === Form Inputs === */
|
||||
.form-label { display: block; font-size: .875rem; font-weight: 500; color: var(--color-text); margin-bottom: .375rem; }
|
||||
.form-input {
|
||||
width: 100%; padding: .5rem .75rem; border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius); font-size: .875rem; color: var(--color-text);
|
||||
background: #fff; outline: none; transition: border-color .15s ease, box-shadow .15s ease;
|
||||
}
|
||||
.form-input:focus { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(142,176,47,.15); }
|
||||
.form-input::placeholder { color: #94a3b8; }
|
||||
|
||||
/* === Badges === */
|
||||
.badge { display: inline-flex; align-items: center; padding: .125rem .5rem; border-radius: 9999px; font-size: .75rem; font-weight: 500; line-height: 1.25rem; }
|
||||
.badge-success { background: #dcfce7; color: #16a34a; }
|
||||
.badge-danger { background: #fee2e2; color: #dc2626; }
|
||||
.badge-warning { background: #fef3c7; color: #d97706; }
|
||||
.badge-info { background: #dbeafe; color: #2563eb; }
|
||||
.badge-muted { background: #f1f5f9; color: #64748b; }
|
||||
|
||||
/* === Modals === */
|
||||
.modal-backdrop {
|
||||
position: fixed; inset: 0; background: rgba(15,23,42,.6); backdrop-filter: blur(2px);
|
||||
display: flex; align-items: center; justify-content: center; z-index: 50; padding: 1rem;
|
||||
}
|
||||
.modal-card { background: #fff; border-radius: calc(var(--radius) * 1.5); box-shadow: var(--shadow-lg); width: 100%; max-height: 90vh; overflow-y: auto; }
|
||||
.modal-header { padding: 1.25rem 1.5rem; border-bottom: 1px solid var(--color-border); display: flex; align-items: center; justify-content: space-between; }
|
||||
.modal-body { padding: 1.5rem; }
|
||||
.modal-footer { padding: 1rem 1.5rem; border-top: 1px solid var(--color-border); display: flex; justify-content: flex-end; gap: .75rem; }
|
||||
|
||||
/* === Avatar === */
|
||||
.ui-avatar {
|
||||
width: 2rem; height: 2rem; border-radius: 9999px;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-weight: 700; font-size: .75rem; color: #fff;
|
||||
background: var(--color-primary); text-transform: uppercase; flex-shrink: 0;
|
||||
}
|
||||
.ui-avatar-lg { width: 2.25rem; height: 2.25rem; font-size: .875rem; }
|
||||
|
||||
/* === Page Header === */
|
||||
.page-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 1.5rem; gap: 1rem; flex-wrap: wrap; }
|
||||
.page-title { font-size: 1.125rem; font-weight: 700; color: var(--color-text); line-height: 1.75rem; }
|
||||
.page-subtitle { font-size: .875rem; color: var(--color-muted); margin-top: .125rem; }
|
||||
|
||||
/* === Search Wrapper === */
|
||||
.search-wrapper { position: relative; }
|
||||
.search-wrapper .search-icon { position: absolute; left: .625rem; top: 50%; transform: translateY(-50%); color: var(--color-muted); pointer-events: none; }
|
||||
.search-wrapper input { padding-left: 2.25rem; }
|
||||
|
||||
@@ -1,9 +1,92 @@
|
||||
<form action="/do/login" method="POST" class="space-y-4">
|
||||
<div class="relative">
|
||||
<input type="text" name="nombre_usuario" placeholder="Usuario" required>
|
||||
<div class="w-full max-w-sm">
|
||||
<!-- Logo -->
|
||||
<div class="mb-8 text-center">
|
||||
<div class="inline-flex items-center justify-center w-14 h-14 rounded-2xl mb-4" style="background-color:#1e293b">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253M3 12c0 .778.099 1.533.284 2.253"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="text-2xl font-bold text-slate-800">Bienvenido</h1>
|
||||
<p class="text-sm text-slate-500 mt-1">Ingresa a tu cuenta para continuar</p>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<input type="password" name="password" placeholder="Contraseña" required>
|
||||
</div>
|
||||
<button type="submit">Iniciar sesión</button>
|
||||
</form>
|
||||
|
||||
<!-- Form -->
|
||||
<form action="/do/login" method="POST" class="space-y-4" x-data="{showPass: false, loading: false}" @submit="loading = true">
|
||||
<!-- Username -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1.5" for="nombre_usuario">Usuario</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="nombre_usuario" name="nombre_usuario"
|
||||
placeholder="Nombre de usuario" required autocomplete="username"
|
||||
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
|
||||
focus:border-[#8eb02f] transition-all"
|
||||
style="box-shadow:none"
|
||||
onfocus="this.style.boxShadow='0 0 0 3px rgba(142,176,47,0.15)'; this.style.borderColor='#8eb02f'"
|
||||
onblur="this.style.boxShadow='none'; this.style.borderColor='#e2e8f0'">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1.5" for="password">Contraseña</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="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>
|
||||
<input :type="showPass ? 'text' : 'password'" id="password" name="password"
|
||||
placeholder="••••••••" required autocomplete="current-password"
|
||||
class="w-full pl-10 pr-11 py-2.5 border border-slate-200 rounded-xl text-sm outline-none bg-white text-slate-800
|
||||
focus:border-[#8eb02f] transition-all"
|
||||
style="box-shadow:none"
|
||||
onfocus="this.style.boxShadow='0 0 0 3px rgba(142,176,47,0.15)'; this.style.borderColor='#8eb02f'"
|
||||
onblur="this.style.boxShadow='none'; this.style.borderColor='#e2e8f0'">
|
||||
<button type="button" @click="showPass = !showPass"
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600 transition-colors">
|
||||
<svg x-show="!showPass" 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="M2.036 12.322a1.012 1.012 0 010-.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 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
<svg x-show="showPass" 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="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Forgot password link -->
|
||||
<div class="flex justify-end">
|
||||
<a href="/password/reset" class="text-xs font-medium hover:underline" style="color:#8eb02f">
|
||||
¿Olvidaste tu contraseña?
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Submit -->
|
||||
<button type="submit" :disabled="loading"
|
||||
class="w-full py-2.5 rounded-xl text-sm font-semibold text-white transition-all mt-2"
|
||||
style="background-color:#8eb02f"
|
||||
:style="loading ? 'opacity:0.7; cursor:not-allowed' : ''"
|
||||
onmouseover="if(!this.disabled) this.style.backgroundColor='#6d8c24'"
|
||||
onmouseout="if(!this.disabled) this.style.backgroundColor='#8eb02f'">
|
||||
<span x-show="!loading" class="flex items-center justify-center gap-2">
|
||||
Iniciar sesión
|
||||
<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="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3"/>
|
||||
</svg>
|
||||
</span>
|
||||
<span x-show="loading" class="flex items-center justify-center gap-2">
|
||||
<svg class="animate-spin h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
|
||||
</svg>
|
||||
Ingresando...
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
+35
-26
@@ -1,32 +1,41 @@
|
||||
<!-- Container Principal -->
|
||||
<div x-data="modals" @keydown.escape="closeModal" class="bg-white rounded-lg shadow">
|
||||
<div class="container mx-auto p-6 w-full ">
|
||||
<h1 class="text-2xl font-bold mb-4">Lista de inicio </h1>
|
||||
<div class="justify-between w-full md:flex">
|
||||
<p class="mb-4 text-sm">Gestión de inicio.</p>
|
||||
<!-- Campo de Búsqueda -->
|
||||
<div class="mb-4 relative">
|
||||
<input type="text" x-model="searchQuery" placeholder="Buscar..."
|
||||
class="border border-gray-300 rounded w-full md:w-60 p-2 text-sm" @input="loadRegister()">
|
||||
|
||||
<!-- Contenedor para el botón o el ícono de carga -->
|
||||
<div x-show="isLoading" class="absolute right-2 top-2 flex items-center">
|
||||
<!-- SVG de cargando (puedes reemplazarlo con un botón si lo prefieres) -->
|
||||
<svg class="animate-spin h-5 w-5 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4">
|
||||
</circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
|
||||
</svg>
|
||||
<div x-data="modals" @keydown.escape="closeModal" class="bg-white rounded-lg shadow">
|
||||
<div class="container mx-auto p-6 w-full">
|
||||
<!-- Page header -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold">Lista de inicio</h1>
|
||||
<p class="text-xs text-slate-500 mt-0.5">Gestión de contenido de inicio</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<!-- Búsqueda -->
|
||||
<div class="relative">
|
||||
<div class="absolute left-2.5 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="text" x-model="searchQuery" placeholder="Buscar..."
|
||||
class="border border-gray-300 rounded w-full md:w-56" @input="loadRegister()">
|
||||
<div x-show="isLoading" class="absolute right-2.5 top-1/2 -translate-y-1/2">
|
||||
<svg class="animate-spin h-3.5 w-3.5 text-slate-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Nuevo -->
|
||||
<button class="flex items-center gap-2 text-white text-sm font-medium px-4 py-2 rounded-lg transition-colors"
|
||||
style="background-color:#8eb02f"
|
||||
onmouseover="this.style.backgroundColor='#6d8c24'"
|
||||
onmouseout="this.style.backgroundColor='#8eb02f'"
|
||||
x-on:click="addModal=true">
|
||||
<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.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/>
|
||||
</svg>
|
||||
Nuevo
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Botón Nuevo registro -->
|
||||
<div class="flex justify-end mb-4">
|
||||
<button class="bg-[#8eb02f] text-white px-4 py-2 rounded text-sm"
|
||||
x-on:click="addModal=true">Nuevo</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Agregar el GIF de carga -->
|
||||
|
||||
@@ -1,57 +1,86 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="es">
|
||||
|
||||
<head>
|
||||
<title>Main</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css">
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>U-site — Acceso</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.5/dist/cdn.min.js" defer></script>
|
||||
<style>
|
||||
.profile-action:hover .profile-action-dropdown {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
aside ul li:hover .menu-item-text {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
margin-left: 7rem;
|
||||
width: 10rem;
|
||||
padding: 22px;
|
||||
background-color: rgba(247, 250, 252, var(--bg-opacity));
|
||||
}
|
||||
|
||||
aside .logout:hover .menu-item-text {
|
||||
display: inline-block;
|
||||
padding: 22px;
|
||||
position: absolute;
|
||||
margin-left: 7rem;
|
||||
width: 10rem;
|
||||
}
|
||||
|
||||
.date:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
top: 12px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.date:after {
|
||||
margin-left: 15px;
|
||||
}
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
body { font-family: 'Inter', system-ui, sans-serif; }
|
||||
[x-cloak] { display: none !important; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="min-h-screen flex" style="background-color:#f1f5f9">
|
||||
|
||||
<body class="bg-gray-200 h-screen w-screen flex">
|
||||
<div class="flex-1 flex flex-col" x-data="{ open: false }">
|
||||
<button class="bg-blue-500 text-white p-4" @click="open = !open">Toggle Dropdown</button>
|
||||
<div x-show="open" class="mt-2 p-4 bg-white shadow-lg">
|
||||
Dropdown Content
|
||||
<!-- Left panel: brand -->
|
||||
<div class="hidden lg:flex lg:w-1/2 xl:w-2/5 flex-col justify-between p-12"
|
||||
style="background: linear-gradient(145deg, #1e293b 0%, #0f172a 100%)">
|
||||
<!-- Top: logo -->
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-xl flex items-center justify-center flex-shrink-0"
|
||||
style="background-color:#8eb02f">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253M3 12c0 .778.099 1.533.284 2.253"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="text-white font-bold text-xl tracking-tight">U-site</span>
|
||||
</div>
|
||||
|
||||
{{template "partials/landing-header" .}}
|
||||
<!-- Middle: tagline -->
|
||||
<div>
|
||||
<h2 class="text-white text-3xl font-bold leading-snug mb-4">
|
||||
Gestiona tu plataforma<br>
|
||||
<span style="color:#8eb02f">con total control</span>
|
||||
</h2>
|
||||
<p class="text-slate-400 text-sm leading-relaxed max-w-xs">
|
||||
Panel de administración seguro, rápido e intuitivo para gestionar usuarios, servidores, módulos y más.
|
||||
</p>
|
||||
|
||||
<!-- Feature bullets -->
|
||||
<ul class="mt-8 space-y-3">
|
||||
<li class="flex items-center gap-3 text-sm text-slate-400">
|
||||
<div class="w-5 h-5 rounded-full flex items-center justify-center flex-shrink-0" style="background-color:rgba(142,176,47,.2)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" style="color:#8eb02f" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/>
|
||||
</svg>
|
||||
</div>
|
||||
Control de roles y permisos
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-400">
|
||||
<div class="w-5 h-5 rounded-full flex items-center justify-center flex-shrink-0" style="background-color:rgba(142,176,47,.2)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" style="color:#8eb02f" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/>
|
||||
</svg>
|
||||
</div>
|
||||
Gestión de servidores y conexiones
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-400">
|
||||
<div class="w-5 h-5 rounded-full flex items-center justify-center flex-shrink-0" style="background-color:rgba(142,176,47,.2)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" style="color:#8eb02f" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/>
|
||||
</svg>
|
||||
</div>
|
||||
Sesiones seguras con tiempo de expiración
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Bottom: copyright -->
|
||||
<p class="text-slate-600 text-xs">© 2025 U-site. Todos los derechos reservados.</p>
|
||||
</div>
|
||||
|
||||
<!-- Right panel: form -->
|
||||
<div class="flex-1 flex items-center justify-center p-6 sm:p-10">
|
||||
{{embed}}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -5,59 +5,219 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>U-site</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<!-- Select2 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet"/>
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" href="/css/app.css">
|
||||
<!-- Axios -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios@1.6.7/dist/axios.min.js"></script>
|
||||
<!-- Alpine.js -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.5/dist/cdn.min.js" defer></script>
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<!-- Cargar jQuery primero -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- Luego cargar Select2 -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<!-- Select2 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
<style>
|
||||
[x-cloak] {
|
||||
display: none !important;
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
body { font-family: 'Inter', system-ui, -apple-system, sans-serif; }
|
||||
[x-cloak] { display: none !important; }
|
||||
|
||||
:root {
|
||||
--color-primary: #8eb02f;
|
||||
--color-primary-dark: #6d8c24;
|
||||
--color-secondary: #1e293b;
|
||||
--color-bg: #f1f5f9;
|
||||
--color-border: #e2e8f0;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
/* Sidebar scroll */
|
||||
.sidebar-scroll { scrollbar-width: none; -ms-overflow-style: none; }
|
||||
.sidebar-scroll::-webkit-scrollbar { display: none; }
|
||||
|
||||
/* Avatar circle */
|
||||
.ui-avatar {
|
||||
width: 2rem; height: 2rem; border-radius: 9999px;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-weight: 700; font-size: .7rem; color: #fff;
|
||||
background-color: var(--color-primary);
|
||||
text-transform: uppercase; flex-shrink: 0; user-select: none;
|
||||
}
|
||||
.ui-avatar-lg { width: 2.25rem; height: 2.25rem; font-size: .8rem; }
|
||||
|
||||
/* Pulse animation for activity dot */
|
||||
@keyframes pulse-ring {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: .75; transform: scale(1.15); }
|
||||
}
|
||||
.pulse-dot { animation: pulse-ring 2.5s ease-in-out infinite; }
|
||||
|
||||
/* Smooth focus inputs */
|
||||
.ui-input {
|
||||
width: 100%; padding: .5rem .75rem;
|
||||
border: 1px solid var(--color-border); border-radius: var(--radius);
|
||||
font-size: .875rem; outline: none; font-family: inherit;
|
||||
transition: border-color .15s ease, box-shadow .15s ease;
|
||||
}
|
||||
.ui-input:focus {
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 3px rgba(142, 176, 47, .15);
|
||||
}
|
||||
.ui-input::placeholder { color: #94a3b8; }
|
||||
|
||||
/* Active nav item in sidebar */
|
||||
.nav-active { background-color: var(--color-primary) !important; color: #fff !important; }
|
||||
|
||||
/* === Global content page improvements === */
|
||||
|
||||
/* Card wrapper (used in all content pages) */
|
||||
.bg-white.rounded-lg.shadow,
|
||||
.bg-white.rounded-lg {
|
||||
border-radius: 0.75rem !important;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04) !important;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
/* Page title */
|
||||
h1.text-2xl.font-bold {
|
||||
font-size: 1.125rem !important;
|
||||
font-weight: 700 !important;
|
||||
color: #1e293b !important;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
/* Search input improvements */
|
||||
input[placeholder="Buscar..."] {
|
||||
border-radius: 0.5rem !important;
|
||||
border: 1px solid #e2e8f0 !important;
|
||||
font-size: 0.875rem !important;
|
||||
transition: border-color .15s ease, box-shadow .15s ease !important;
|
||||
outline: none !important;
|
||||
padding: 0.5rem 0.75rem 0.5rem 2rem !important;
|
||||
}
|
||||
input[placeholder="Buscar..."]:focus {
|
||||
border-color: var(--color-primary) !important;
|
||||
box-shadow: 0 0 0 3px rgba(142,176,47,.12) !important;
|
||||
}
|
||||
|
||||
/* Table - global improvements */
|
||||
.table-auto, table.table-auto {
|
||||
border-collapse: collapse;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.table-auto thead tr, table.table-auto thead tr {
|
||||
background-color: #f8fafc !important;
|
||||
}
|
||||
.table-auto thead th, table.table-auto thead th {
|
||||
font-size: 0.7rem !important;
|
||||
font-weight: 600 !important;
|
||||
text-transform: uppercase !important;
|
||||
letter-spacing: 0.05em !important;
|
||||
color: #475569 !important;
|
||||
padding: 0.75rem 1rem !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.table-auto tbody td, table.table-auto tbody td {
|
||||
padding: 0.875rem 1rem !important;
|
||||
vertical-align: middle;
|
||||
font-size: 0.8125rem !important;
|
||||
}
|
||||
.table-auto tbody tr, table.table-auto tbody tr {
|
||||
transition: background-color 0.1s ease;
|
||||
}
|
||||
.table-auto tbody tr:hover, table.table-auto tbody tr:hover {
|
||||
background-color: #f8fafc !important;
|
||||
}
|
||||
|
||||
/* Action icons in rows */
|
||||
.table-auto button svg, table.table-auto button svg {
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
.table-auto button:hover svg, table.table-auto button:hover svg {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
/* Loading overlay */
|
||||
.fixed.inset-0.bg-gray-800 {
|
||||
background-color: rgba(15, 23, 42, 0.55) !important;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
/* Page loading bar at top */
|
||||
.page-content { animation: fadeIn .2s ease; }
|
||||
@keyframes fadeIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-200 w-full">
|
||||
<main class="flex justify-between w-full">
|
||||
<!-- Componente Lateral (Barra Lateral) -->
|
||||
<body class="bg-slate-100 w-full">
|
||||
<main class="flex w-full">
|
||||
<!-- Sidebar -->
|
||||
{{template "partials/lateral" .}}
|
||||
<div class="w-full">
|
||||
<!-- Main area -->
|
||||
<div class="flex-1 flex flex-col min-w-0">
|
||||
{{template "partials/header" .}}
|
||||
<div class="w-full px-3 py-3 h-[calc(100vh-64px)] overflow-auto">
|
||||
<div class="flex-1 p-4 overflow-auto page-content" style="height:calc(100vh - 64px)">
|
||||
{{embed}}
|
||||
</div>
|
||||
{{template "partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
<div x-data="sessionTimeout" x-init="init()" x-cloak class="hidden sm:block">
|
||||
<!-- Contador General de Inactividad -->
|
||||
<div x-show="!showModal" class="fixed top-2 right-20 border text rounded-lg p-2 bg-gray-100 text-sm">
|
||||
<p>Tiempo restante: <span x-text="formatTime(inactivityCountdown)"></span></p>
|
||||
<!-- Session Timeout -->
|
||||
<div x-data="sessionTimeout" x-init="init()" x-cloak>
|
||||
<!-- Activity pill -->
|
||||
<div x-show="!showModal"
|
||||
x-transition
|
||||
class="fixed top-4 right-4 z-50 hidden sm:flex items-center gap-2 bg-white border border-slate-200 shadow-sm rounded-full px-3 py-1.5 text-xs text-slate-500 select-none">
|
||||
<div class="w-2 h-2 rounded-full bg-emerald-400 pulse-dot flex-shrink-0"></div>
|
||||
<span>Sesión: <span class="font-semibold text-slate-700" x-text="formatTime(inactivityCountdown)"></span></span>
|
||||
</div>
|
||||
<!-- Modal -->
|
||||
<div x-show="showModal" class="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-75">
|
||||
<div class="bg-white rounded-lg shadow-md p-8 w-[600px] relative">
|
||||
<h2 class="text-xl font-bold mb-6 text-center">¿Deseas continuar?</h2>
|
||||
<p class="text-gray-600 mb-6 text-center text-base">
|
||||
Has estado inactivo. Elige "Continuar" para seguir con tu sesión o "Cerrar sesión" para salir.
|
||||
</p>
|
||||
<!-- Contador del tiempo para responder -->
|
||||
<p class="text-center text-red-500 font-semibold mb-4">
|
||||
Tiempo para responder: <span x-text="countdown"></span> seg
|
||||
</p>
|
||||
<div class="flex justify-around">
|
||||
<button @click="continueSession"
|
||||
class="px-6 py-3 bg-[#8eb02f] text-white text-base rounded-lg hover:bg-[#698126]">
|
||||
Continuar
|
||||
</button>
|
||||
<form action="/do/logout" method="POST" class="inline-block">
|
||||
<button type="submit"
|
||||
class="px-6 py-3 bg-gray-500 text-white text-base rounded-lg hover:bg-gray-600">
|
||||
Cerrar sesión
|
||||
<div x-show="showModal"
|
||||
x-transition
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/60 backdrop-blur-sm p-4">
|
||||
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-md overflow-hidden">
|
||||
<div class="px-6 py-5" style="background: linear-gradient(135deg, #1e293b 0%, #334155 100%)">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
style="background:rgba(251,191,36,.15)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" style="color:#fbbf24" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-white font-semibold">Sesión por vencer</h2>
|
||||
<p class="text-slate-400 text-xs mt-0.5">Tu sesión expirará por inactividad</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-6 py-6">
|
||||
<div class="flex items-center justify-center gap-2 bg-red-50 rounded-xl py-3 mb-5 border border-red-100">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-red-500 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span class="text-sm text-red-600">Cierre automático en <strong x-text="countdown"></strong> segundos</span>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<button @click="continueSession"
|
||||
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'">
|
||||
Continuar sesión
|
||||
</button>
|
||||
</form>
|
||||
<form action="/do/logout" method="POST" class="flex-1">
|
||||
<button type="submit"
|
||||
class="w-full py-2.5 rounded-xl text-sm font-semibold bg-slate-100 text-slate-700 hover:bg-slate-200 transition-colors">
|
||||
Cerrar sesión
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -67,22 +227,20 @@
|
||||
document.addEventListener("alpine:init", () => {
|
||||
Alpine.data("sessionTimeout", () => ({
|
||||
showModal: false,
|
||||
countdown: 60, // 10 segundos para responder
|
||||
inactivityCountdown: 300, // 5 minutos
|
||||
countdown: 60,
|
||||
inactivityCountdown: 300,
|
||||
inactivityTimer: null,
|
||||
timer: null,
|
||||
|
||||
init() {
|
||||
this.resetInactivityTimer();
|
||||
|
||||
// Detectar actividad
|
||||
window.addEventListener("mousemove", () => this.resetInactivityTimer());
|
||||
window.addEventListener("keydown", () => this.resetInactivityTimer());
|
||||
window.addEventListener("keydown", () => this.resetInactivityTimer());
|
||||
},
|
||||
|
||||
resetInactivityTimer() {
|
||||
clearTimeout(this.inactivityTimer);
|
||||
this.inactivityCountdown = 300; // Reiniciar contador a 5 minutos
|
||||
this.inactivityCountdown = 300;
|
||||
this.startInactivityTimer();
|
||||
},
|
||||
|
||||
@@ -102,13 +260,11 @@
|
||||
},
|
||||
|
||||
startCountdown() {
|
||||
this.countdown = 60; // Reiniciar contador del modal
|
||||
this.countdown = 60;
|
||||
clearInterval(this.timer);
|
||||
this.timer = setInterval(() => {
|
||||
this.countdown--;
|
||||
if (this.countdown <= 0) {
|
||||
this.logout();
|
||||
}
|
||||
if (this.countdown <= 0) this.logout();
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
@@ -124,9 +280,9 @@
|
||||
},
|
||||
|
||||
formatTime(seconds) {
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const remainingSeconds = seconds % 60;
|
||||
return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;
|
||||
const m = Math.floor(seconds / 60);
|
||||
const s = seconds % 60;
|
||||
return `${m}:${s.toString().padStart(2, "0")}`;
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -1,120 +1,5 @@
|
||||
<!-- container -->
|
||||
<nav
|
||||
class="fixed bottom-0 w-full border bg-white hidden
|
||||
overflow-x-auto">
|
||||
<!-- Bottom Icon Navigation Menu -->
|
||||
|
||||
<a
|
||||
href="."
|
||||
class="flex flex-col flex-grow items-center justify-center
|
||||
overflow-hidden whitespace-no-wrap text-sm transition-colors
|
||||
duration-100 ease-in-out hover:bg-gray-200 focus:text-orange-500">
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<polyline points="22 12 16 12 14 15 10 15 8 12 2 12"></polyline>
|
||||
<path
|
||||
d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0
|
||||
2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path>
|
||||
</svg>
|
||||
|
||||
<span class="hidden text-sm capitalize">Inbox</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="."
|
||||
class="flex flex-col flex-grow items-center justify-center
|
||||
overflow-hidden whitespace-no-wrap text-sm transition-colors
|
||||
duration-100 ease-in-out hover:bg-gray-200 text-orange-500">
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="feather feather-github">
|
||||
<path
|
||||
d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0
|
||||
0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77
|
||||
5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7
|
||||
0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0
|
||||
0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
|
||||
</svg>
|
||||
|
||||
<span class="text-sm capitalize">github</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="."
|
||||
class="flex flex-col flex-grow items-center justify-center
|
||||
overflow-hidden whitespace-no-wrap text-sm transition-colors
|
||||
duration-100 ease-in-out hover:bg-gray-200 focus:text-orange-500">
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="feather feather-bookmark">
|
||||
<path
|
||||
d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
|
||||
</svg>
|
||||
|
||||
<span class="hidden text-sm capitalize">bookmark</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="."
|
||||
class="flex flex-col flex-grow items-center justify-center
|
||||
overflow-hidden whitespace-no-wrap text-sm transition-colors
|
||||
duration-100 ease-in-out hover:bg-gray-200 focus:text-orange-500">
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
<path
|
||||
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83
|
||||
2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65
|
||||
0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0
|
||||
0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2
|
||||
2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0
|
||||
0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0
|
||||
4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2
|
||||
0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0
|
||||
1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1
|
||||
1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0
|
||||
0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0
|
||||
1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0
|
||||
0-1.51 1z"></path>
|
||||
</svg>
|
||||
|
||||
<span class="hidden text-sm capitalize">Settings</span>
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
<!-- Footer minimal — oculto en desktop, visible solo en impresión -->
|
||||
<footer class="hidden print:flex items-center justify-between px-6 py-3 border-t border-slate-200 bg-white text-xs text-slate-400">
|
||||
<span>U-site © 2025</span>
|
||||
<span>Panel de administración</span>
|
||||
</footer>
|
||||
|
||||
@@ -1,41 +1,82 @@
|
||||
<nav class="px-1 md:px-4 flex justify-between bg-white h-16 border-b-2" x-data="{
|
||||
open: false,
|
||||
}">
|
||||
<!-- top bar -->
|
||||
<header class="h-16 bg-white border-b border-slate-200 flex items-center justify-between px-4 md:px-6 flex-shrink-0 z-20"
|
||||
x-data="{
|
||||
open: false,
|
||||
initial: '{{ .user.NombreUsuario }}'.charAt(0).toUpperCase() || 'U',
|
||||
userName: '{{ .user.NombreUsuario }}',
|
||||
roleName: '{{ .user.Role.Name }}'
|
||||
}">
|
||||
|
||||
<ul class="flex items-center md:hidden">
|
||||
<!-- top bar left -->
|
||||
<div class="items-center justify-start h-16 md:px-10 py-2 object-cover">
|
||||
<img src="/img/logo.webp" alt="" class="w-20 ml-11 mt-1 md:mt-0 md:ml-0">
|
||||
<!-- Left: page context -->
|
||||
<div class="flex items-center gap-3">
|
||||
<!-- Mobile logo placeholder (sidebar button is in lateral.html) -->
|
||||
<div class="md:hidden w-8"></div>
|
||||
<!-- Breadcrumb / title area -->
|
||||
<div class="hidden sm:flex items-center gap-2 text-sm text-slate-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"/>
|
||||
</svg>
|
||||
<span class="text-slate-400">Panel de control</span>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<ul class="flex items-center">
|
||||
<!-- top bar center -->
|
||||
<li>
|
||||
<h1 class="mx-2 lg:pl-0 text-gray-700 md:text-sm text-xs" >{{ .user.NombreUsuario }} - {{ .user.Role.Name }}</h1>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Right: actions + user menu -->
|
||||
<div class="flex items-center gap-3">
|
||||
<!-- User info (desktop) -->
|
||||
<div class="hidden md:flex flex-col items-end mr-1">
|
||||
<span class="text-xs font-semibold text-slate-700 leading-tight" x-text="userName"></span>
|
||||
<span class="text-xs text-slate-400 leading-tight" x-text="roleName"></span>
|
||||
</div>
|
||||
|
||||
<ul class="flex items-center">
|
||||
<!-- top bar right -->
|
||||
<li class="h-8 w-8 relative profile-action">
|
||||
<button @click="open = !open" class="block h-8 w-8 rounded-full overflow-hidden border-1 border-gray-600 focus:outline-none focus:border-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-7 h-7">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
||||
<!-- Avatar dropdown -->
|
||||
<div class="relative">
|
||||
<button @click="open = !open"
|
||||
class="flex items-center gap-2 p-1 rounded-xl hover:bg-slate-100 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1"
|
||||
style="--tw-ring-color:#8eb02f">
|
||||
<div class="ui-avatar ui-avatar-lg" x-text="initial"></div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-3.5 w-3.5 text-slate-400 transition-transform duration-150"
|
||||
:class="open ? 'rotate-180' : ''"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- dropdown menu -->
|
||||
<div x-show="open" @click.outside="open = false" class="profile-action-dropdown absolute bg-gray-100 border border-t-0 shadow-xl text-gray-700 rounded-lg overflow-hidden w-48 top-10 right-0 mr-6 z-10">
|
||||
<a href="/app/profile" class="block px-4 py-2 hover:bg-gray-200">Mi cuenta</a>
|
||||
<a href="/do/logout" class="block px-4 py-2 hover:bg-gray-200"
|
||||
onclick="event.preventDefault(); document.getElementById('logout-form-profile').submit();">
|
||||
Cerrar sesión
|
||||
</a>
|
||||
<form id="logout-form-profile" action="/do/logout" method="POST" style="display: none;"></form>
|
||||
<!-- Dropdown -->
|
||||
<div x-show="open"
|
||||
@click.outside="open = false"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-95 -translate-y-1"
|
||||
x-transition:enter-end="opacity-100 scale-100 translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 scale-100 translate-y-0"
|
||||
x-transition:leave-end="opacity-0 scale-95 -translate-y-1"
|
||||
class="absolute right-0 top-12 w-52 bg-white rounded-xl shadow-lg border border-slate-200 overflow-hidden z-50">
|
||||
<!-- User info in dropdown -->
|
||||
<div class="px-4 py-3 border-b border-slate-100">
|
||||
<p class="text-sm font-semibold text-slate-800 truncate" x-text="userName"></p>
|
||||
<p class="text-xs text-slate-500 truncate" x-text="roleName"></p>
|
||||
</div>
|
||||
<!-- Links -->
|
||||
<div class="py-1">
|
||||
<a href="/app/profile"
|
||||
class="flex items-center gap-3 px-4 py-2.5 text-sm text-slate-700 hover:bg-slate-50 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
Mi cuenta
|
||||
</a>
|
||||
<div class="border-t border-slate-100 my-1"></div>
|
||||
<a href="/do/logout"
|
||||
class="flex items-center gap-3 px-4 py-2.5 text-sm text-red-600 hover:bg-red-50 transition-colors"
|
||||
onclick="event.preventDefault(); document.getElementById('header-logout').submit();">
|
||||
<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 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75"/>
|
||||
</svg>
|
||||
Cerrar sesión
|
||||
</a>
|
||||
<form id="header-logout" action="/do/logout" method="POST" class="hidden"></form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- dropdown menu end -->
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -1,88 +1,136 @@
|
||||
<div x-data="{
|
||||
<div x-data="{
|
||||
isSidebarOpen: false,
|
||||
currentPath: window.location.pathname
|
||||
}" class="flex min-h-screen">
|
||||
|
||||
<!-- Botón de menú para dispositivos móviles -->
|
||||
<div class="md:hidden fixed top-4 left-4 z-50">
|
||||
<button @click="isSidebarOpen = !isSidebarOpen" class="text-black focus:outline-none">
|
||||
<svg x-show="!isSidebarOpen" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
<svg x-show="isSidebarOpen" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
<!-- Mobile menu button -->
|
||||
<div class="md:hidden fixed top-3 left-3 z-50">
|
||||
<button @click="isSidebarOpen = !isSidebarOpen"
|
||||
class="p-2 rounded-xl shadow-lg text-white focus:outline-none"
|
||||
style="background-color:#1e293b">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Overlay para dispositivos móviles -->
|
||||
<div x-show="isSidebarOpen" @click="isSidebarOpen = false"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 z-30 md:hidden"></div>
|
||||
<!-- Mobile overlay -->
|
||||
<div x-show="isSidebarOpen"
|
||||
@click="isSidebarOpen = false"
|
||||
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-black/50 z-30 md:hidden"></div>
|
||||
|
||||
<!-- Barra lateral -->
|
||||
<div :class="{'translate-x-0': isSidebarOpen, '-translate-x-full': !isSidebarOpen}" class="fixed z-30 md:relative transform md:translate-x-0 transition-transform duration-300 ease-in-out w-64 md:w-52 drop-shadow-md text-black shadow flex flex-col justify-between bg-white h-screen">
|
||||
<!-- Sidebar -->
|
||||
<div :class="{'translate-x-0': isSidebarOpen, '-translate-x-full': !isSidebarOpen}"
|
||||
class="fixed z-40 md:relative transform md:translate-x-0 transition-transform duration-300 ease-in-out
|
||||
w-64 h-screen flex flex-col"
|
||||
style="background-color:#1e293b">
|
||||
|
||||
<!-- Encabezado de la barra lateral -->
|
||||
<div class="flex items-center justify-between h-16 px-10 py-2">
|
||||
<img src="/img/logo.webp" alt="Logo" class="w-36 mt-4 mb-2">
|
||||
<div class="md:hidden">
|
||||
<button @click="isSidebarOpen = false" class="text-black focus:outline-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12" />
|
||||
<!-- Logo Header -->
|
||||
<div class="flex items-center justify-between px-5 h-16 flex-shrink-0"
|
||||
style="border-bottom:1px solid rgba(255,255,255,0.07)">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<div class="w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style="background-color:#8eb02f">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253M3 12c0 .778.099 1.533.284 2.253"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<img src="/img/logo.webp" alt="U-site" class="h-6 w-auto object-contain opacity-90"
|
||||
style="filter:brightness(0) invert(1)">
|
||||
</div>
|
||||
<!-- Close button for mobile -->
|
||||
<button @click="isSidebarOpen = false"
|
||||
class="md:hidden p-1 rounded-lg text-slate-400 hover:text-white hover:bg-white/10 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" 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>
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="flex-1 overflow-y-auto sidebar-scroll px-3 py-3">
|
||||
<ul class="space-y-0.5">
|
||||
{{range $moduleName, $submodules := .modules}}
|
||||
<li x-data="{ showSubMenu: true }">
|
||||
<!-- Module header -->
|
||||
<button @click="showSubMenu = !showSubMenu"
|
||||
class="w-full flex items-center justify-between px-3 py-2.5 rounded-xl transition-all duration-150 group"
|
||||
:class="showSubMenu ? 'text-white' : 'text-slate-400 hover:text-white hover:bg-white/5'">
|
||||
<div class="flex items-center gap-2.5 min-w-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 flex-shrink-0 opacity-60" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z"/>
|
||||
</svg>
|
||||
<span class="text-xs font-semibold uppercase tracking-wider truncate">{{ $moduleName }}</span>
|
||||
</div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-3.5 w-3.5 flex-shrink-0 transition-transform duration-200 opacity-50"
|
||||
:class="showSubMenu ? 'rotate-180' : ''"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Submodule links -->
|
||||
<ul x-show="showSubMenu"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 -translate-y-1"
|
||||
x-transition:enter-end="opacity-100 translate-y-0"
|
||||
class="mt-0.5 ml-3 pl-3 space-y-0.5"
|
||||
style="border-left:1px solid rgba(255,255,255,0.07)">
|
||||
{{range $submodule := $submodules}}
|
||||
<li>
|
||||
<a href="{{ $submodule.url }}"
|
||||
class="flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium transition-all duration-150"
|
||||
:class="currentPath === '{{ $submodule.url }}'
|
||||
? 'text-white shadow-sm'
|
||||
: 'text-slate-400 hover:text-white hover:bg-white/5'"
|
||||
:style="currentPath === '{{ $submodule.url }}' ? 'background-color:#8eb02f' : ''">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 flex-shrink-0 opacity-70" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"/>
|
||||
</svg>
|
||||
{{ $submodule.title }}
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- User section at bottom -->
|
||||
<div class="flex-shrink-0 px-4 py-3" style="border-top:1px solid rgba(255,255,255,0.07)">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="ui-avatar ui-avatar-lg flex-shrink-0"
|
||||
x-data="{n:'{{ .user.NombreUsuario }}'}"
|
||||
x-text="n ? n.charAt(0).toUpperCase() : 'U'">
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-sm font-semibold text-white truncate">{{ .user.NombreUsuario }}</p>
|
||||
<p class="text-xs truncate" style="color:#64748b">{{ .user.Role.Name }}</p>
|
||||
</div>
|
||||
<a href="/do/logout"
|
||||
onclick="event.preventDefault(); document.getElementById('sl-logout').submit();"
|
||||
class="p-1.5 rounded-lg transition-colors flex-shrink-0"
|
||||
style="color:#475569"
|
||||
onmouseover="this.style.color='#f87171'; this.style.backgroundColor='rgba(255,255,255,0.05)'"
|
||||
onmouseout="this.style.color='#475569'; this.style.backgroundColor=''"
|
||||
title="Cerrar sesión">
|
||||
<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 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75"/>
|
||||
</svg>
|
||||
</a>
|
||||
<form id="sl-logout" action="/do/logout" method="POST" class="hidden"></form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contenido de la barra lateral -->
|
||||
<div class="overflow-y-auto h-full">
|
||||
<nav class="px-4 py-2">
|
||||
<ul>
|
||||
<!-- Recorrer módulos -->
|
||||
{{range $moduleName, $submodules := .modules}}
|
||||
<li class="my-1 cursor-pointer" x-data="{ showSubMenu: true }">
|
||||
<div class="flex items-center hover:bg-gray-200 px-2 rounded-lg py-2 font-semibold border" @click="showSubMenu = !showSubMenu">
|
||||
<div class="flex-shrink-0">
|
||||
<svg x-show="showSubMenu" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
|
||||
</svg>
|
||||
<svg x-show="!showSubMenu" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="ml-2 text-sm font-medium">
|
||||
{{ $moduleName }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Submenú -->
|
||||
<ul class="pl-6" x-show="showSubMenu" x-transition>
|
||||
{{range $submodule := $submodules}}
|
||||
<li>
|
||||
<a href="{{ $submodule.url }}"
|
||||
class="block hover:bg-gray-200 hover:font-semibold rounded-md px-3 py-2 text-sm text-black">
|
||||
{{ $submodule.title }}
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contenido principal -->
|
||||
<div class="flex-1">
|
||||
<!-- Tu contenido principal aquí -->
|
||||
</div>
|
||||
<!-- Desktop spacer (pushes content) -->
|
||||
<div class="hidden md:block w-64 flex-shrink-0"></div>
|
||||
</div>
|
||||
|
||||
+124
-29
@@ -1,41 +1,136 @@
|
||||
<div x-data="modals" @keydown.escape="closeModal"
|
||||
class="bg-white rounded-lg shadow">
|
||||
<div class="container mx-auto p-6 w-full ">
|
||||
<h1 class="text-2xl font-bold mb-4">Perfil</h1>
|
||||
<form @submit.prevent="submitForm">
|
||||
<div>
|
||||
<label class="block mb-2" for="name">Nombre</label>
|
||||
<input type="text" id="name" x-model="Name" placeholder="Nombre"
|
||||
class="border border-gray-300 rounded w-full p-2" required>
|
||||
<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>
|
||||
<label class="block mt-4 mb-2" for="Email">Email</label>
|
||||
<input id="Email" x-model="Email" placeholder="Email" class="border border-gray-300 rounded w-full p-2"
|
||||
required>
|
||||
<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 class="bg-[#8eb02f] text-white px-4 py-2 my-4 rounded text-center cursor-pointer"
|
||||
x-on:click="showPasswordModal = true;" title="Cambiar contraseña">Cambiar contraseña</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex justify-between">
|
||||
<button type="submit" class="bg-[#8eb02f] text-white px-4 py-2 rounded" :disabled="isLoading">
|
||||
<span x-show="!isLoading">Guardar Cambios</span>
|
||||
<span x-show="isLoading">Cargando...</span>
|
||||
<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
|
||||
class="fixed inset-0 bg-gray-800 bg-opacity-75 flex z-40 justify-center items-center text-sm">
|
||||
<div class="bg-white p-6 rounded shadow-lg max-w-sm w-full overflow-y-auto max-h-full">
|
||||
<form @submit.prevent="changePass">
|
||||
<label class="block mb-2" for="password">Nueva contraseña</label>
|
||||
<input type="text" id="password" x-model="password" placeholder="Ingrese una contraseña"
|
||||
class="border border-gray-300 rounded w-full p-2" required>
|
||||
<div class="mt-4 flex justify-between">
|
||||
<button type="submit" class="bg-[#8eb02f] text-white px-4 py-2 rounded">Guardar </button>
|
||||
<button type="button" class="bg-gray-600 text-white px-4 py-2 rounded"
|
||||
x-on:click="closeModal">Cancelar</button>
|
||||
<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>
|
||||
|
||||
+34
-25
@@ -1,32 +1,41 @@
|
||||
<!-- Container Principal -->
|
||||
<div x-data="modals" @keydown.escape="closeModal" class="bg-white rounded-lg shadow">
|
||||
<div class="container mx-auto p-6 w-full ">
|
||||
<h1 class="text-2xl font-bold mb-4">Lista de Roles</h1>
|
||||
<div class="justify-between w-full md:flex">
|
||||
<p class="mb-4 text-sm">Gestión de roles.</p>
|
||||
<!-- Campo de Búsqueda -->
|
||||
<div class="mb-4 relative">
|
||||
<input type="text" x-model="searchQuery" placeholder="Buscar..."
|
||||
class="border border-gray-300 rounded w-full md:w-60 p-2 text-sm" @input="loadRegister()">
|
||||
|
||||
<!-- Contenedor para el botón o el ícono de carga -->
|
||||
<div x-show="isLoading" class="absolute right-2 top-2 flex items-center">
|
||||
<!-- SVG de cargando (puedes reemplazarlo con un botón si lo prefieres) -->
|
||||
<svg class="animate-spin h-5 w-5 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4">
|
||||
</circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
|
||||
</svg>
|
||||
<div class="container mx-auto p-6 w-full">
|
||||
<!-- Page header -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold">Roles</h1>
|
||||
<p class="text-xs text-slate-500 mt-0.5">Gestión de roles y permisos</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<!-- Búsqueda -->
|
||||
<div class="relative">
|
||||
<div class="absolute left-2.5 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="text" x-model="searchQuery" placeholder="Buscar..."
|
||||
class="border border-gray-300 rounded w-full md:w-56" @input="loadRegister()">
|
||||
<div x-show="isLoading" class="absolute right-2.5 top-1/2 -translate-y-1/2">
|
||||
<svg class="animate-spin h-3.5 w-3.5 text-slate-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Nuevo -->
|
||||
<button class="flex items-center gap-2 text-white text-sm font-medium px-4 py-2 rounded-lg transition-colors"
|
||||
style="background-color:#8eb02f"
|
||||
onmouseover="this.style.backgroundColor='#6d8c24'"
|
||||
onmouseout="this.style.backgroundColor='#8eb02f'"
|
||||
x-on:click="addModal=true">
|
||||
<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.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/>
|
||||
</svg>
|
||||
Nuevo rol
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Botón Nuevo Módulo -->
|
||||
<div class="flex justify-end mb-4">
|
||||
<button class="bg-[#8eb02f] text-white px-4 py-2 rounded text-sm"
|
||||
x-on:click="addModal=true">Nuevo</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Agregar el GIF de carga -->
|
||||
|
||||
+35
-26
@@ -1,32 +1,41 @@
|
||||
<!-- Container Principal -->
|
||||
<div x-data="modals" @keydown.escape="closeModal" class="bg-white rounded-lg shadow">
|
||||
<div class="container mx-auto p-6 w-full ">
|
||||
<h1 class="text-2xl font-bold mb-4">Lista de Usuarios</h1>
|
||||
<div class="justify-between w-full md:flex">
|
||||
<p class="mb-4 text-sm">Gestión de usuarios.</p>
|
||||
<!-- Campo de Búsqueda -->
|
||||
<div class="mb-4 relative">
|
||||
<input type="text" x-model="searchQuery" placeholder="Buscar..."
|
||||
class="border border-gray-300 rounded w-full md:w-60 p-2 text-sm" @input="loadRegister()">
|
||||
|
||||
<!-- Contenedor para el botón o el ícono de carga -->
|
||||
<div x-show="isLoading" class="absolute right-2 top-2 flex items-center">
|
||||
<!-- SVG de cargando (puedes reemplazarlo con un botón si lo prefieres) -->
|
||||
<svg class="animate-spin h-5 w-5 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4">
|
||||
</circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
|
||||
</svg>
|
||||
<div x-data="modals" @keydown.escape="closeModal" class="bg-white rounded-lg shadow">
|
||||
<div class="container mx-auto p-6 w-full">
|
||||
<!-- Page header -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold">Usuarios</h1>
|
||||
<p class="text-xs text-slate-500 mt-0.5">Gestión de usuarios del sistema</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<!-- Búsqueda -->
|
||||
<div class="relative">
|
||||
<div class="absolute left-2.5 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="text" x-model="searchQuery" placeholder="Buscar..."
|
||||
class="border border-gray-300 rounded w-full md:w-56" @input="loadRegister()">
|
||||
<div x-show="isLoading" class="absolute right-2.5 top-1/2 -translate-y-1/2">
|
||||
<svg class="animate-spin h-3.5 w-3.5 text-slate-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Nuevo -->
|
||||
<button class="flex items-center gap-2 text-white text-sm font-medium px-4 py-2 rounded-lg transition-colors"
|
||||
style="background-color:#8eb02f"
|
||||
onmouseover="this.style.backgroundColor='#6d8c24'"
|
||||
onmouseout="this.style.backgroundColor='#8eb02f'"
|
||||
x-on:click="addModal=true">
|
||||
<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.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/>
|
||||
</svg>
|
||||
Nuevo usuario
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Botón Nuevo Módulo -->
|
||||
<div class="flex justify-end mb-4">
|
||||
<button class="bg-[#8eb02f] text-white px-4 py-2 rounded text-sm"
|
||||
x-on:click="addModal=true">Nuevo</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Agregar el GIF de carga -->
|
||||
|
||||
Reference in New Issue
Block a user