From eb2a2b7d6ea77da305b8443832de3f4647464965 Mon Sep 17 00:00:00 2001 From: Thibault Pouch Date: Mon, 2 Mar 2026 09:50:38 +0100 Subject: [PATCH] feat: add staff user creation functionality and enhance role badge display --- .../src/pages/intranet/IntranetUsers.tsx | 165 +++++++++++++++++- 1 file changed, 160 insertions(+), 5 deletions(-) diff --git a/nest-intra/src/pages/intranet/IntranetUsers.tsx b/nest-intra/src/pages/intranet/IntranetUsers.tsx index d1f0751..15ea75d 100644 --- a/nest-intra/src/pages/intranet/IntranetUsers.tsx +++ b/nest-intra/src/pages/intranet/IntranetUsers.tsx @@ -1,8 +1,35 @@ import { useState, useMemo, useCallback } from 'react'; import { useAuth } from '../../contexts/AuthContext'; +import { getToken } from '../../contexts/AuthContext'; import { formatDate } from '../../utils/format'; import type { User, UserRole } from '../../types'; +function RoleBadge({ role, isAdmin }: { role: UserRole; isAdmin: boolean }) { + if (isAdmin) { + return ( + + {role} + + ADMIN + + + ); + } + const cls = role === 'dev' ? 'badge-open' : role === 'com' ? 'badge-medium' : 'badge-closed'; + return {role}; +} + export default function IntranetUsers() { const { user: currentUser } = useAuth(); const [users, setUsers] = useState([]); @@ -10,6 +37,15 @@ export default function IntranetUsers() { const [roleFilter, setRoleFilter] = useState('all'); const [confirmAction, setConfirmAction] = useState<{ userId: string; action: 'promote' | 'ban' | 'unban' } | null>(null); + // Create staff form + const [showCreateForm, setShowCreateForm] = useState(false); + const [createUsername, setCreateUsername] = useState(''); + const [createEmail, setCreateEmail] = useState(''); + const [createPassword, setCreatePassword] = useState(''); + const [createRole, setCreateRole] = useState<'dev' | 'com'>('dev'); + const [createError, setCreateError] = useState(''); + const [creating, setCreating] = useState(false); + const filtered = useMemo(() => { return users.filter((u) => { const matchSearch = !search.trim() || u.username.toLowerCase().includes(search.toLowerCase()) || u.email.toLowerCase().includes(search.toLowerCase()); @@ -28,13 +64,54 @@ export default function IntranetUsers() { setConfirmAction(null); }, []); + const handleCreateStaff = useCallback(async () => { + setCreateError(''); + if (!createUsername.trim()) { setCreateError('Username is required.'); return; } + if (!createEmail.trim()) { setCreateError('Email is required.'); return; } + if (createPassword.length < 6) { setCreateError('Password must be at least 6 characters.'); return; } + + setCreating(true); + try { + const res = await fetch('/api/users', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${getToken()}`, + }, + body: JSON.stringify({ username: createUsername.trim(), email: createEmail.trim(), password: createPassword, role: createRole }), + }); + const data = await res.json(); + if (!res.ok) { + setCreateError(data.error ?? 'Failed to create user.'); + return; + } + setUsers((prev) => [...prev, data as User]); + setShowCreateForm(false); + setCreateUsername(''); + setCreateEmail(''); + setCreatePassword(''); + setCreateRole('dev'); + } catch { + setCreateError('Cannot reach the server.'); + } finally { + setCreating(false); + } + }, [createUsername, createEmail, createPassword, createRole]); + return (
INTRANET / USER MANAGEMENT
-

USERS

+
+

USERS

+ {currentUser?.isAdmin && ( + + )} +
{/* Filters */}
@@ -55,6 +132,87 @@ export default function IntranetUsers() {
+ {/* Create staff user modal */} + {showCreateForm && ( +
setShowCreateForm(false)} + > +
e.stopPropagation()} + > +

+ CREATE STAFF USER +

+ +
+
+ + setCreateUsername(e.target.value)} + style={{ fontSize: '0.85rem' }} + autoFocus + /> +
+
+ + setCreateEmail(e.target.value)} + style={{ fontSize: '0.85rem' }} + /> +
+
+ + setCreatePassword(e.target.value)} + style={{ fontSize: '0.85rem' }} + /> +
+
+ + +
+
+ + {createError && ( +
+ {createError} +
+ )} + +
+ + +
+
+
+ )} + {/* Confirm dialog */} {confirmAction && (
{u.username} {isSelf && (you)} - {u.isAdmin && [admin]} {u.email} - - {u.role} - + {formatDate(u.createdAt)} 0