This commit is contained in:
Lizandro Guarnizo
2026-04-18 20:07:42 -05:00
parent 535e10f714
commit 9108a584cf
3 changed files with 82 additions and 39 deletions
+12
View File
@@ -85,6 +85,10 @@ class ShowUsuarios extends Component
public $editarTarifaRol = false;
public $informacionTarifaRol;
public $showActionMenu = false;
public $actionMenuUser = null;
public $actionMenuSubvendedor = false;
public $estadoTarifa = false;
public $estadoPromo = false;
@@ -471,6 +475,14 @@ class ShowUsuarios extends Component
]);
}
public function abrirAcciones($id)
{
$usuario = User::findOrFail($id);
$this->actionMenuUser = $usuario;
$this->actionMenuSubvendedor = (bool) $usuario->subvendedor;
$this->showActionMenu = true;
}
public function editar($id)
{
$this->id_edit = $id;
-8
View File
@@ -37,14 +37,6 @@
</div>
<script src="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.js"></script>
<script>
document.addEventListener('alpine:init', () => {
Alpine.store('openDropdown', null);
Livewire.hook('message.received', () => {
Alpine.store('openDropdown', null);
});
});
</script>
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
@livewireScripts
<script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
@@ -8,11 +8,7 @@
modTarifas: @entangle('modTarifas'),
modalSaldoDesc: @entangle('modalSaldoDesc'),
editarTarifaRol: @entangle('editarTarifaRol'),
init() {
['editar','gestion','gestionPromo','modalSaldo','modalPassword','modTarifas','modalSaldoDesc','editarTarifaRol'].forEach(prop => {
this.$watch(prop, () => { Alpine.store('openDropdown', null); });
});
}
showActionMenu: @entangle('showActionMenu')
}" class="h-full w-full md:my-2">
<div class="absolute backdrop-blur-sm bg-white/70 left-0 bottom-0 top-0 right-0 h-full z-40" wire:loading wire:target="gestionarPromociones,quitarSubvendedor,ponerSubvendedor,actualizarTarifaRol,addpromos,eliminarUsuarioPromo,gestionar,eliminarUsuarioTarifa,addtarifas,crear,actualizar,editar,eliminar,limpiarInputAdd,limpiarInputEdit,editarPassword,limpiarContra,confirmarNewPassword,saldo,addSaldo,addSaldoSubvendedor">
<div class="grid h-full place-items-center ">
@@ -81,7 +77,7 @@
</x-slot>
<x-slot name="tbody">
@foreach ($usuarios as $usuario)
<tr wire:key="usuario-{{ $usuario->id }}" class="border-gray-200 text-left hover:bg-purple-50 transition-colors" x-data="{ openDetails: false, menuX: 0, menuY: 0, toggleMenu(btn){ const r = btn.getBoundingClientRect(); menuX = r.right - 160; menuY = r.bottom + 4; $store.openDropdown = ($store.openDropdown === {{ $usuario->id }}) ? null : {{ $usuario->id }}; } }">
<tr wire:key="usuario-{{ $usuario->id }}" class="border-gray-200 text-left hover:bg-purple-50 transition-colors" x-data="{ openDetails: false }">
@if (auth()->user()->rol_id == 5)
<td>{{ $usuario->subvendedor_relacion->name ?? 'N/A' }}</td>
@@ -148,42 +144,19 @@
@if (auth()->user()->rol->nombre == 'super')
<td>
<button @click="toggleMenu($el)" class="p-1 rounded-lg hover:bg-gray-100">
<button wire:click="abrirAcciones({{ $usuario->id }})" class="p-1 rounded-lg hover:bg-gray-100">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 cursor-pointer">
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM18.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0z" />
</svg>
</button>
<div x-cloak x-show="$store.openDropdown === {{ $usuario->id }} && menuX > 0" @click.away="$store.openDropdown = null" :style="`position:fixed;top:${menuY}px;left:${menuX}px;`" class="z-50 bg-white text-center w-[10rem] rounded-xl border border-gray-200 shadow-xl">
<button x-on:click="$store.openDropdown = null" wire:click="editar({{ $usuario->id }})" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100 hover:rounded-t-xl">Editar</button>
<button x-on:click="$store.openDropdown = null" wire:click="editarPassword({{ $usuario->id }})" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100">Editar contraseña</button>
<button x-on:click="$store.openDropdown = null" wire:click="saldo({{ $usuario->id }})" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100">Agregar saldo</button>
<button x-on:click="$store.openDropdown = null" wire:click="saldodesc({{ $usuario->id }})" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100">Descontar saldo</button>
<button x-on:click="$store.openDropdown = null; gestion=true" wire:click="gestionar({{ $usuario->id }},'{{ $usuario->rol_id }}','{{ $usuario->name }}')" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100">Precio servicio</button>
<button x-on:click="$store.openDropdown = null; gestionPromo=true" wire:click="gestionarPromociones({{ $usuario->id }},'{{ $usuario->name }}')" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100">Precio promoción</button>
<button x-on:click="$store.openDropdown = null" wire:click="eliminar({{ $usuario->id }})" class="text-white w-full py-1.5 text-sm bg-red-400 rounded-b-xl hover:bg-red-600">Eliminar</button>
</div>
</td>
@elseif (Auth::user()->subvendedor && $config->subvendedor)
<td>
<button @click="toggleMenu($el)" class="p-1 rounded-lg hover:bg-gray-100">
<button wire:click="abrirAcciones({{ $usuario->id }})" class="p-1 rounded-lg hover:bg-gray-100">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 cursor-pointer">
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM18.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0z" />
</svg>
</button>
<div x-cloak x-show="$store.openDropdown === {{ $usuario->id }} && menuX > 0" @click.away="$store.openDropdown = null" :style="`position:fixed;top:${menuY}px;left:${menuX}px;`" class="z-50 bg-white text-center w-[10rem] rounded-xl border border-gray-200 shadow-xl">
<button x-on:click="$store.openDropdown = null" wire:click="editar({{ $usuario->id }})" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100 hover:rounded-t-xl">Editar</button>
<button x-on:click="$store.openDropdown = null" wire:click="editarPassword({{ $usuario->id }})" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100">Editar contraseña</button>
@if ($usuario->subvendedor)
<button x-on:click="$store.openDropdown = null" wire:click="quitarSubvendedor({{ $usuario->id }})" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100">Quitar subvendedor</button>
@else
<button x-on:click="$store.openDropdown = null" wire:click="ponerSubvendedor({{ $usuario->id }})" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100">Habilitar subvendedor</button>
@endif
<button x-on:click="$store.openDropdown = null; gestion=true" wire:click="gestionar({{ $usuario->id }},'{{ $usuario->rol_id }}','{{ $usuario->name }}')" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100">Precio servicio</button>
<button x-on:click="$store.openDropdown = null; gestionPromo=true" wire:click="gestionarPromociones({{ $usuario->id }},'{{ $usuario->name }}')" class="text-gray-600 w-full py-1.5 text-sm hover:bg-gray-50 border-b border-gray-100">Precio promoción</button>
<button x-on:click="$store.openDropdown = null" wire:click="eliminar({{ $usuario->id }})" class="text-white w-full py-1.5 text-sm bg-red-400 rounded-b-xl hover:bg-red-600">Eliminar</button>
</div>
</td>
@else
@endif
@@ -195,6 +168,72 @@
</x-slot>
</x-primary-table>
{{-- Panel de acciones del usuario (controlado 100% por Livewire) --}}
<div x-cloak x-show="showActionMenu" 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 z-50 flex items-end sm:items-center justify-center bg-black/30" @click.self="$wire.set('showActionMenu', false)">
<div x-transition:enter="transition ease-out duration-200" x-transition:enter-start="translate-y-4 opacity-0 sm:translate-y-0 sm:scale-95" x-transition:enter-end="translate-y-0 opacity-100 sm:scale-100" class="bg-white w-full sm:w-72 rounded-t-2xl sm:rounded-2xl shadow-xl overflow-hidden">
@if ($actionMenuUser)
<div class="px-4 py-3 border-b border-gray-100 flex items-center justify-between">
<div>
<p class="font-semibold text-gray-800 text-sm">{{ $actionMenuUser->name }}</p>
<p class="text-xs text-gray-400">{{ $actionMenuUser->email }}</p>
</div>
<button wire:click="$set('showActionMenu', false)" class="text-gray-400 hover:text-gray-600">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="py-1">
<button wire:click="editar({{ $actionMenuUser->id }}); $set('showActionMenu', false)" class="flex items-center gap-3 w-full px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Z" /></svg>
Editar usuario
</button>
<button wire:click="editarPassword({{ $actionMenuUser->id }}); $set('showActionMenu', false)" class="flex items-center gap-3 w-full px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 5.25a3 3 0 0 1 3 3m3 0a6 6 0 0 1-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1 1 21.75 8.25Z" /></svg>
Editar contraseña
</button>
@if (auth()->user()->rol->nombre == 'super')
<button wire:click="saldo({{ $actionMenuUser->id }}); $set('showActionMenu', false)" class="flex items-center gap-3 w-full px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /></svg>
Agregar saldo
</button>
<button wire:click="saldodesc({{ $actionMenuUser->id }}); $set('showActionMenu', false)" class="flex items-center gap-3 w-full px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14" /></svg>
Descontar saldo
</button>
@endif
@if (auth()->user()->subvendedor && $config->subvendedor)
@if ($actionMenuSubvendedor)
<button wire:click="quitarSubvendedor({{ $actionMenuUser->id }}); $set('showActionMenu', false)" class="flex items-center gap-3 w-full px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M22 10.5h-6m-2.25-4.125a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0ZM4 19.235v-.11a6.375 6.375 0 0 1 12.75 0v.109A12.318 12.318 0 0 1 10.374 21c-2.331 0-4.512-.645-6.374-1.766Z" /></svg>
Quitar subvendedor
</button>
@else
<button wire:click="ponerSubvendedor({{ $actionMenuUser->id }}); $set('showActionMenu', false)" class="flex items-center gap-3 w-full px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M18 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0ZM3 19.235v-.11a6.375 6.375 0 0 1 12.75 0v.109A12.318 12.318 0 0 1 9.374 21c-2.331 0-4.512-.645-6.374-1.766Z" /></svg>
Habilitar subvendedor
</button>
@endif
@endif
<button wire:click="gestionar({{ $actionMenuUser->id }},'{{ $actionMenuUser->rol_id }}','{{ $actionMenuUser->name }}'); $set('showActionMenu', false)" class="flex items-center gap-3 w-full px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M9.568 3H5.25A2.25 2.25 0 0 0 3 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 0 0 5.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 0 0 9.568 3Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M6 6h.008v.008H6V6Z" /></svg>
Precio servicio
</button>
<button wire:click="gestionarPromociones({{ $actionMenuUser->id }},'{{ $actionMenuUser->name }}'); $set('showActionMenu', false)" class="flex items-center gap-3 w-full px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M9.568 3H5.25A2.25 2.25 0 0 0 3 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 0 0 5.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 0 0 9.568 3Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M6 6h.008v.008H6Z" /></svg>
Precio promoción
</button>
<div class="border-t border-gray-100 mt-1 pt-1">
<button wire:click="eliminar({{ $actionMenuUser->id }}); $set('showActionMenu', false)" class="flex items-center gap-3 w-full px-4 py-2.5 text-sm text-red-600 hover:bg-red-50">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" /></svg>
Eliminar usuario
</button>
</div>
</div>
@endif
</div>
</div>
{{-- Gestionar precios --}}
<div x-cloak x-show="gestion" x-transition:enter="transition duration-350"
x-transition:enter-start="opacity-0 scale-100" x-transition:enter-end="opacity-100 scale-100"