Feature: Toggle para mostrar/ocultar sesiones stale
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 30s

- Sesiones stale ocultas por defecto
- Badge de 'Stale' clickeable para mostrar/ocultar
- Indicador visual (ojo abierto/cerrado) del estado del filtro
- Mensaje informativo cuando hay stale ocultas
This commit is contained in:
2025-11-25 01:24:33 -06:00
parent 6639c93cfe
commit b3f7de1442

View File

@@ -261,8 +261,13 @@ const pagedDevices = computed(() => devicesAll.value.slice(devicePage.value*page
watch([devicesAll, () => layoutMode.value], () => { devicePage.value = 0; });
const sessionPage = ref(0);
const pagedSessions = computed(() => sessions.value.slice(sessionPage.value*pageSize, sessionPage.value*pageSize + pageSize));
watch([sessions, () => layoutMode.value], () => { sessionPage.value = 0; });
const showStaleSessions = ref(false);
const filteredSessions = computed(() => {
if (showStaleSessions.value) return sessions.value;
return sessions.value.filter(s => s.status !== 'stale');
});
const pagedSessions = computed(() => filteredSessions.value.slice(sessionPage.value*pageSize, sessionPage.value*pageSize + pageSize));
watch([filteredSessions, () => layoutMode.value], () => { sessionPage.value = 0; });
const filteredRequestsAll = computed(() => filteredRequests.value);
const reqPage = ref(0);
@@ -493,9 +498,9 @@ async function handleUserFormSubmit(data) {
<span class="flex-1"></span>
<Badge v-if="layoutMode==='user'">Página {{ userPage+1 }} / {{ Math.max(1, Math.ceil(filteredUsersAll.length / pageSize)) }}</Badge>
<Badge v-else-if="layoutMode==='device'">Página {{ devicePage+1 }} / {{ Math.max(1, Math.ceil(devicesAll.length / pageSize)) }}</Badge>
<Badge v-else>Página {{ sessionPage+1 }} / {{ Math.max(1, Math.ceil(sessions.length / pageSize)) }}</Badge>
<Badge v-else>Página {{ sessionPage+1 }} / {{ Math.max(1, Math.ceil(filteredSessions.length / pageSize)) }}</Badge>
<Button size="sm" @click="layoutMode==='user' ? (userPage=Math.max(0,userPage-1)) : layoutMode==='device' ? (devicePage=Math.max(0,devicePage-1)) : (sessionPage=Math.max(0,sessionPage-1))">Anterior</Button>
<Button size="sm" @click="layoutMode==='user' ? (userPage=Math.min(Math.ceil(filteredUsersAll.length/pageSize)-1,userPage+1)) : layoutMode==='device' ? (devicePage=Math.min(Math.ceil(devicesAll.length/pageSize)-1,devicePage+1)) : (sessionPage=Math.min(Math.ceil(sessions.length/pageSize)-1,sessionPage+1))">Siguiente</Button>
<Button size="sm" @click="layoutMode==='user' ? (userPage=Math.min(Math.ceil(filteredUsersAll.length/pageSize)-1,userPage+1)) : layoutMode==='device' ? (devicePage=Math.min(Math.ceil(devicesAll.length/pageSize)-1,devicePage+1)) : (sessionPage=Math.min(Math.ceil(filteredSessions.length/pageSize)-1,sessionPage+1))">Siguiente</Button>
<Button size="sm" :variant="layoutMode==='user' ? 'default' : 'ghost'" title="Vista usuarios" @click="layoutMode='user'">
<img class="size-4 opacity-90" src="/icons/layout-users.svg" alt="usuarios"/> Usuarios
</Button>
@@ -547,7 +552,23 @@ async function handleUserFormSubmit(data) {
Activas: {{ sessionStats.active }}
</Badge>
<Badge variant="secondary" class="text-sm py-1.5 px-3">Finalizadas: {{ sessionStats.stopped }}</Badge>
<Badge variant="warning" class="text-sm py-1.5 px-3">Stale: {{ sessionStats.stale }}</Badge>
<Badge
:variant="showStaleSessions ? 'warning' : 'secondary'"
class="text-sm py-1.5 px-3 cursor-pointer select-none transition-all"
:class="showStaleSessions ? 'ring-2 ring-yellow-400/50' : 'opacity-60 hover:opacity-100'"
@click="showStaleSessions = !showStaleSessions"
:title="showStaleSessions ? 'Click para ocultar sesiones stale' : 'Click para mostrar sesiones stale'"
>
<svg v-if="showStaleSessions" class="size-3.5 mr-1.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
<svg v-else class="size-3.5 mr-1.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>
<line x1="1" y1="1" x2="23" y2="23"></line>
</svg>
Stale: {{ sessionStats.stale }}
</Badge>
<span class="flex-1"></span>
<Button size="sm" @click="fetchSessions">
<svg class="size-4 mr-1" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@@ -567,7 +588,9 @@ async function handleUserFormSubmit(data) {
</div>
<!-- Sessions Grid -->
<div v-if="loading.sessions" class="text-muted">Cargando sesiones...</div>
<div v-else-if="!sessions.length" class="text-muted text-center py-8">No hay sesiones activas</div>
<div v-else-if="!filteredSessions.length" class="text-muted text-center py-8">
{{ sessions.length ? 'No hay sesiones activas (hay ' + sessionStats.stale + ' stale ocultas)' : 'No hay sesiones' }}
</div>
<div v-else class="grid grid-cols-[repeat(auto-fill,minmax(320px,1fr))] gap-3">
<Card v-for="s in pagedSessions" :key="s.id" :class="['p-4', s.status === 'active' && 'border-pink-400/30 bg-pink-400/5']">
<div class="flex flex-wrap items-center gap-2 mb-3">