From a171943f98e06e6f669b57f03a91ca7926341c49 Mon Sep 17 00:00:00 2001 From: Daniel Arroyo Date: Fri, 14 Aug 2026 20:07:12 -0400 Subject: [PATCH] feat(auth): admin UI for user management + change-password frontend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backend additions: - User.lastLoginAt column (updated on each successful login) - AdminUserSummary DTO (id, username, createdAt, lastLoginAt, isAdmin, mustChangePassword) - GET /api/auth/admin/users (RolesAllowed("admin")) -> array of summaries - AuthService.listUsersForAdmin() + AuthService.authenticate() now @Transactional and bumps lastLoginAt Frontend (Phase 1): - User type extended with mustChangePassword + isAdmin - api.changePassword() / api.adminResetPassword() / api.adminListUsers() - AuthContext exposes changePassword - ChangePasswordPage.tsx (full-page, current + new + confirm, errors inline) - App.tsx routes LoginPage -> ChangePasswordPage -> AuthenticatedApp Frontend (Phase 2): - TabBar supports optional 'usuarios' tab (shown only if user.isAdmin) - UsersAdminPage.tsx: lista todos los usuarios con badges de rol y estado, botón "Resetear contraseña" con modal inline que llama adminResetPassword - Header de AuthenticatedApp muestra un badge 'admin' al lado del username Both mvn compile and npm tsc + vite build pass clean. --- src/frontend/src/App.tsx | 13 +- src/frontend/src/api/client.ts | 29 +++ src/frontend/src/auth/AuthContext.tsx | 8 +- src/frontend/src/auth/ChangePasswordPage.tsx | 119 +++++++++++ src/frontend/src/components/TabBar.tsx | 7 +- .../src/components/UsersAdminPage.tsx | 195 ++++++++++++++++++ .../com/l2/shots/auth/AdminUserSummary.java | 24 +++ .../java/com/l2/shots/auth/AuthResource.java | 7 + .../java/com/l2/shots/auth/AuthService.java | 9 + src/main/java/com/l2/shots/auth/User.java | 3 + 10 files changed, 410 insertions(+), 4 deletions(-) create mode 100644 src/frontend/src/auth/ChangePasswordPage.tsx create mode 100644 src/frontend/src/components/UsersAdminPage.tsx create mode 100644 src/main/java/com/l2/shots/auth/AdminUserSummary.java diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 8a2d3cd..a0ef013 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -1,11 +1,13 @@ import { useState } from 'react' import { AuthProvider, useAuth } from './auth/AuthContext' import { LoginPage } from './auth/LoginPage' +import { ChangePasswordPage } from './auth/ChangePasswordPage' import { TabBar, type TabKey } from './components/TabBar' import { InsumosSection } from './components/InsumosSection' import { FormulasSection } from './components/FormulasSection' import { CalculadoraSection } from './components/CalculadoraSection' import { HistorySection } from './components/HistorySection' +import { UsersAdminPage } from './components/UsersAdminPage' import { SaveIndicator } from './components/SaveIndicator' import { usePersistedState } from './hooks/usePersistedState' import { makeDefaultAppState, makeEmptyAppState } from './data/defaults' @@ -22,6 +24,7 @@ function AppRouter() { const { user, loading } = useAuth() if (loading) return if (!user) return + if (user.mustChangePassword) return return } @@ -46,6 +49,8 @@ function AuthenticatedApp() { } } + const isAdmin = user?.isAdmin ?? false + return (
@@ -56,6 +61,11 @@ function AuthenticatedApp() {

{user?.username ?? ''} + {isAdmin && ( + + admin + + )} {' · '} Lineage 2 — Interlude / Clásico

@@ -93,7 +103,7 @@ function AuthenticatedApp() {
- +
@@ -107,6 +117,7 @@ function AuthenticatedApp() { )} {tab === 'historial' && } + {tab === 'usuarios' && isAdmin && }