From 51bff7564f86afb95243ba3e846bcc6929fdffba Mon Sep 17 00:00:00 2001 From: Schalli Date: Mon, 21 Sep 2026 07:31:47 +0200 Subject: [PATCH] fix(web): Formularweg und Listenladen der Benutzerverwaltung melden Fehler sichtbar WINDOWS #36, Aufgabe 2/3: handleSubmit und fetchUsers verschluckten abgewiesene Antworten und Verbindungsfehler ebenso wie der Loeschweg aus Aufgabe 1. formError zeigt jetzt den Servertext oder eine Ersatzmeldung im offenen Formulardialog; loadError verhindert die irrefuehrende Meldung "Keine Benutzer gefunden", wenn das Laden selbst gescheitert ist. Beide Zustaende werden beim Oeffnen eines neuen Dialogs zurueckgesetzt, damit eine alte Meldung nicht in den naechsten Aufruf hinueberwandert. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J --- .../web/src/app/(portal)/admin/users/page.tsx | 38 +++- .../(portal)/admin/users/users-page.test.tsx | 186 ++++++++++++++++++ 2 files changed, 220 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/(portal)/admin/users/page.tsx b/apps/web/src/app/(portal)/admin/users/page.tsx index 1b6053e..5a24f08 100644 --- a/apps/web/src/app/(portal)/admin/users/page.tsx +++ b/apps/web/src/app/(portal)/admin/users/page.tsx @@ -65,6 +65,8 @@ export default function AdminUsersPage() { const [editingUser, setEditingUser] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState(null); const [deleteError, setDeleteError] = useState(null); + const [formError, setFormError] = useState(null); + const [loadError, setLoadError] = useState(null); const [detailsUser, setDetailsUser] = useState(null); const [formData, setFormData] = useState({ username: '', @@ -79,19 +81,22 @@ export default function AdminUsersPage() { currentUser?.role === 'ADMIN' || currentUser?.role === 'SUPER_ADMIN'; const fetchUsers = useCallback(async () => { + setLoadError(null); try { const res = await fetch(`${API_URL}/users`, { credentials: 'include', }); if (res.ok) { setUsers(await res.json()); + } else { + setLoadError(t('errors.loadFailed')); } } catch { - // silently fail + setLoadError(t('errors.loadFailed')); } finally { setLoading(false); } - }, []); + }, [t]); useEffect(() => { if (hasAccess) { @@ -110,6 +115,7 @@ export default function AdminUsersPage() { displayName: '', role: 'USER', }); + setFormError(null); setShowForm(true); }; @@ -122,11 +128,13 @@ export default function AdminUsersPage() { displayName: user.displayName ?? '', role: user.role, }); + setFormError(null); setShowForm(true); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + setFormError(null); const url = editingUser ? `${API_URL}/users/${editingUser.id}` @@ -156,9 +164,14 @@ export default function AdminUsersPage() { if (res.ok) { setShowForm(false); fetchUsers(); + } else { + const detail = await readApiMessage(res); + setFormError( + detail ? t('errors.serverRejected', { detail }) : t('errors.generic'), + ); } } catch { - // silently fail + setFormError(t('errors.network')); } }; @@ -215,10 +228,19 @@ export default function AdminUsersPage() { + {loadError && ( +
+ {loadError} +
+ )} + {/* Users table */} {loading ? (

{tCommon('loading')}

- ) : users.length === 0 ? ( + ) : loadError ? null : users.length === 0 ? (

{t('noUsers')}

) : (
@@ -400,6 +422,14 @@ export default function AdminUsersPage() { )}
+ {formError && ( +
+ {formError} +
+ )}