瀏覽代碼

improve admin page

Daniel Bohry 2 周之前
父節點
當前提交
378a4ddee2

+ 5 - 1
src/main/java/com/danielbohry/authservice/service/user/UserService.java

@@ -9,10 +9,12 @@ import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.stereotype.Service;
 
+import java.util.Comparator;
 import java.util.List;
 import java.util.UUID;
 
 import static com.danielbohry.authservice.domain.Role.USER;
+import static java.util.Comparator.comparing;
 
 @Service
 @AllArgsConstructor
@@ -26,7 +28,9 @@ public class UserService {
     }
 
     public List<ApplicationUser> findAll() {
-        return repository.findAll();
+        return repository.findAll().stream()
+                .sorted(comparing(ApplicationUser::getUsername))
+                .toList();
     }
 
     public ApplicationUser findByUsername(String username) {

+ 13 - 6
src/main/resources/static/admin.html

@@ -8,7 +8,7 @@
     <link rel="stylesheet" href="css/admin.css">
     <link rel="icon" href="img/favicon.png" type="image/png">
 </head>
-<body>
+<body class="admin-page-body">
     <div class="admin-container">
         <div class="header">
             <h1>Admin Panel</h1>
@@ -55,11 +55,18 @@
         <div class="users-section">
             <div class="section-header">
                 <h2>Registered Users</h2>
-                <div class="search-container">
-                    <input type="text" id="searchInput" placeholder="Search users..." oninput="filterUsers()">
-                    <svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
-                        <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
-                    </svg>
+                <div class="section-controls">
+                    <label class="toggle-switch inactive-toggle">
+                        <input type="checkbox" id="showInactiveToggle" onchange="toggleInactiveUsers()">
+                        <span class="toggle-slider"></span>
+                        <span class="toggle-label">Show Inactive</span>
+                    </label>
+                    <div class="search-container">
+                        <input type="text" id="searchInput" placeholder="Search users..." oninput="filterUsers()">
+                        <svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
+                            <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
+                        </svg>
+                    </div>
                 </div>
             </div>
 

+ 39 - 2
src/main/resources/static/css/admin.css

@@ -1,5 +1,17 @@
 /* Admin Panel Specific Styles */
 
+/* Override body flexbox centering for admin panel */
+body:has(.admin-container) {
+    align-items: flex-start;
+    padding-top: 20px;
+}
+
+/* Fallback for browsers that don't support :has() */
+.admin-page-body {
+    align-items: flex-start !important;
+    padding-top: 20px !important;
+}
+
 /* Extend the container for admin panel to accommodate wider content */
 .admin-container {
     background: var(--container-bg);
@@ -8,9 +20,8 @@
     overflow: hidden;
     width: 100%;
     max-width: 1200px;
-    margin: 20px;
+    margin: 20px auto;
     transition: background 0.3s ease;
-    min-height: 600px;
 }
 
 /* Header controls */
@@ -53,6 +64,7 @@
     background: var(--section-bg);
     border-bottom: 1px solid var(--border-color);
     transition: all 0.3s ease;
+    position: relative;
 }
 
 /* Stats cards */
@@ -128,6 +140,7 @@
     margin-bottom: 20px;
     flex-wrap: wrap;
     gap: 15px;
+    position: relative;
 }
 
 .section-header h2 {
@@ -137,6 +150,23 @@
     transition: color 0.3s ease;
 }
 
+/* Section controls (toggle + search) */
+.section-controls {
+    display: flex;
+    align-items: center;
+    gap: 20px;
+    flex-wrap: wrap;
+}
+
+/* Inactive toggle styling */
+.inactive-toggle {
+    white-space: nowrap;
+}
+
+.inactive-toggle .toggle-label {
+    font-size: 0.9rem;
+}
+
 /* Search container */
 .search-container {
     position: relative;
@@ -177,6 +207,7 @@
     overflow: hidden;
     box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
     transition: background 0.3s ease;
+    position: relative;
 }
 
 .users-table {
@@ -411,6 +442,12 @@
         align-items: stretch;
     }
 
+    .section-controls {
+        flex-direction: column;
+        gap: 15px;
+        align-items: stretch;
+    }
+
     .search-container {
         min-width: auto;
     }

+ 30 - 8
src/main/resources/static/js/admin.js

@@ -1,5 +1,6 @@
 let currentUsers = [];
 let filteredUsers = [];
+let showInactiveUsers = false;
 
 document.addEventListener('DOMContentLoaded', function() {
     initializeTheme();
@@ -76,7 +77,7 @@ async function loadUsers() {
         }));
 
         currentUsers = transformedUsers;
-        filteredUsers = [...currentUsers];
+        applyFilters();
         displayUsers(filteredUsers);
         updateStats(transformedUsers);
         showMessage('Users loaded successfully', 'success');
@@ -153,14 +154,35 @@ function updateStats(users) {
 }
 
 function filterUsers() {
+    applyFilters();
+    displayUsers(filteredUsers);
+}
+
+function applyFilters() {
     const searchTerm = document.getElementById('searchInput').value.toLowerCase();
-    filteredUsers = currentUsers.filter(user =>
-        user.username.toLowerCase().includes(searchTerm) ||
-        user.id.toLowerCase().includes(searchTerm) ||
-        (user.email && user.email.toLowerCase().includes(searchTerm)) ||
-        user.roles.some(role => role.toLowerCase().includes(searchTerm)) ||
-        user.status.toLowerCase().includes(searchTerm)
-    );
+
+    filteredUsers = currentUsers.filter(user => {
+        // Apply inactive user filter
+        if (!showInactiveUsers && user.status === 'inactive') {
+            return false;
+        }
+
+        // Apply search filter
+        if (searchTerm) {
+            return user.username.toLowerCase().includes(searchTerm) ||
+                   user.id.toLowerCase().includes(searchTerm) ||
+                   (user.email && user.email.toLowerCase().includes(searchTerm)) ||
+                   user.roles.some(role => role.toLowerCase().includes(searchTerm)) ||
+                   user.status.toLowerCase().includes(searchTerm);
+        }
+
+        return true;
+    });
+}
+
+function toggleInactiveUsers() {
+    showInactiveUsers = document.getElementById('showInactiveToggle').checked;
+    applyFilters();
     displayUsers(filteredUsers);
 }