feat: track user login and logout timestamps in session management
AS Talange CI/CD Pipeline / Build & Run Unit Tests (push) Successful in 4m15s
AS Talange CI/CD Pipeline / Deploy to Test Environment (push) Successful in 5m55s

This commit is contained in:
2026-07-31 10:26:14 +02:00
parent df217bc923
commit 1737087062
5 changed files with 221 additions and 20 deletions
@@ -2,7 +2,7 @@
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Utilisateurs Connectés - AS Talange</title>
<title>Utilisateurs & Sessions - AS Talange</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/htmx.org@1.9.11"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
@@ -19,41 +19,99 @@
<main class="flex-1 flex flex-col h-screen overflow-hidden">
<!-- Header -->
<header class="h-16 bg-white border-b border-gray-200 flex items-center justify-between px-6">
<h2 class="text-lg font-semibold text-gray-800">Utilisateurs Connectés</h2>
<h2 class="text-lg font-semibold text-gray-800">Suivi des Connectivité & Sessions</h2>
</header>
<!-- Main section -->
<div class="flex-1 overflow-auto p-6">
<!-- Cards summary -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<div class="bg-white p-5 rounded-xl shadow-sm border border-gray-100 flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500">Total Utilisateurs</p>
<p class="text-2xl font-bold text-gray-900 mt-1" th:text="${totalCount}">0</p>
</div>
<div class="w-10 h-10 rounded-lg bg-blue-50 text-blue-600 flex items-center justify-center font-bold">
👥
</div>
</div>
<div class="bg-white p-5 rounded-xl shadow-sm border border-gray-100 flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500">Sessions Actives (En ligne)</p>
<p class="text-2xl font-bold text-green-600 mt-1" th:text="${activeCount}">0</p>
</div>
<div class="w-10 h-10 rounded-lg bg-green-50 text-green-600 flex items-center justify-center font-bold">
🟢
</div>
</div>
<div class="bg-white p-5 rounded-xl shadow-sm border border-gray-100 flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500">Hors Ligne</p>
<p class="text-2xl font-bold text-gray-600 mt-1" th:text="${offlineCount}">0</p>
</div>
<div class="w-10 h-10 rounded-lg bg-gray-100 text-gray-500 flex items-center justify-center font-bold">
</div>
</div>
</div>
<!-- Header & Action -->
<div class="mb-6 flex justify-between items-center">
<h3 class="text-xl font-bold text-gray-900">
Sessions Actives (<span th:text="${activeCount}">0</span>)
Liste des Utilisateurs
</h3>
<button hx-get="/admin/sessions" hx-target="body" hx-swap="outerHTML" class="bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors">
Rafraîchir
<button hx-get="/admin/sessions" hx-target="body" hx-swap="outerHTML" class="bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors flex items-center space-x-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
<span>Rafraîchir</span>
</button>
</div>
<!-- User Table -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<table class="w-full text-left border-collapse">
<thead>
<tr class="bg-gray-50 text-gray-500 text-sm uppercase tracking-wider border-b border-gray-200">
<th class="py-3 px-6 font-medium text-left">Nom d'utilisateur</th>
<tr class="bg-gray-50 text-gray-500 text-xs uppercase tracking-wider border-b border-gray-200">
<th class="py-3 px-6 font-medium text-left">Utilisateur</th>
<th class="py-3 px-6 font-medium text-left">Rôle(s)</th>
<th class="py-3 px-6 font-medium text-left">Statut</th>
<th class="py-3 px-6 font-medium text-left">Dernière Connexion</th>
<th class="py-3 px-6 font-medium text-left">Dernière Déconnexion</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 text-sm">
<tr th:if="${#lists.isEmpty(activeUsers)}">
<td colspan="2" class="py-8 text-center text-gray-500">Aucun utilisateur connecté pour le moment.</td>
<tr th:if="${#lists.isEmpty(userSessions)}">
<td colspan="5" class="py-8 text-center text-gray-500">Aucun utilisateur enregistré.</td>
</tr>
<tr th:each="username : ${activeUsers}" class="hover:bg-gray-50 transition-colors">
<tr th:each="userSession : ${userSessions}" class="hover:bg-gray-50 transition-colors">
<td class="py-4 px-6 font-medium text-gray-900 flex items-center">
<div class="w-8 h-8 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center font-bold mr-3">
<span th:text="${#strings.substring(username, 0, 1).toUpperCase()}">U</span>
<div class="w-8 h-8 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center font-bold mr-3 text-xs">
<span th:text="${#strings.substring(userSession.username, 0, 1).toUpperCase()}">U</span>
</div>
<span th:text="${username}">username</span>
<span th:text="${userSession.username}">username</span>
</td>
<td class="py-4 px-6">
<span class="px-2 py-1 bg-green-100 text-green-800 text-xs font-semibold rounded-full">En ligne</span>
<span class="px-2.5 py-0.5 bg-gray-100 text-gray-700 text-xs font-medium rounded border border-gray-200" th:text="${userSession.roles}">ADMIN</span>
</td>
<td class="py-4 px-6">
<span th:if="${userSession.online}" class="inline-flex items-center px-2.5 py-1 bg-green-100 text-green-800 text-xs font-semibold rounded-full">
<span class="w-2 h-2 mr-1.5 bg-green-500 rounded-full animate-pulse"></span>
En ligne
</span>
<span th:unless="${userSession.online}" class="inline-flex items-center px-2.5 py-1 bg-gray-100 text-gray-600 text-xs font-medium rounded-full">
<span class="w-2 h-2 mr-1.5 bg-gray-400 rounded-full"></span>
Hors ligne
</span>
</td>
<td class="py-4 px-6 text-gray-600 font-mono text-xs">
<span th:text="${userSession.lastLoginAt != null ? #temporals.format(userSession.lastLoginAt, 'dd/MM/yyyy HH:mm:ss') : 'Jamais'}">01/01/2026 10:00:00</span>
</td>
<td class="py-4 px-6 text-gray-600 font-mono text-xs">
<span th:text="${userSession.lastLogoutAt != null ? #temporals.format(userSession.lastLogoutAt, 'dd/MM/yyyy HH:mm:ss') : '-'}">01/01/2026 12:00:00</span>
</td>
</tr>
</tbody>