From 38d25864668e2f57ba6fcd5dac309bc994a254fb Mon Sep 17 00:00:00 2001 From: Schalli Date: Mon, 21 Sep 2026 07:29:51 +0200 Subject: [PATCH] fix(web): Loeschweg der Benutzerverwaltung meldet abgewiesene Server-Antworten MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WINDOWS #36, Aufgabe 1/3: handleDelete verschluckte einen 403 bisher komplett (nur res.ok geprueft, Fang-Zweig ohne Wirkung). readApiMessage liest jetzt gezielt das Feld message aus dem Antwortrumpf; der Loeschdialog zeigt den Servertext, eine uebersetzte Ersatzmeldung ohne verwertbaren Rumpf oder bei Verbindungsfehler — und bleibt in allen drei Faellen offen. Neue Texte unter admin.users.errors in de.json/en.json, Umlaut-Waechter gruen. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J --- .../web/src/app/(portal)/admin/users/page.tsx | 42 ++- .../(portal)/admin/users/users-page.test.tsx | 287 ++++++++++++++++++ apps/web/src/messages/de.json | 6 + apps/web/src/messages/en.json | 6 + 4 files changed, 339 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/app/(portal)/admin/users/users-page.test.tsx diff --git a/apps/web/src/app/(portal)/admin/users/page.tsx b/apps/web/src/app/(portal)/admin/users/page.tsx index 6dd252e..1b6053e 100644 --- a/apps/web/src/app/(portal)/admin/users/page.tsx +++ b/apps/web/src/app/(portal)/admin/users/page.tsx @@ -29,6 +29,26 @@ interface UserFormData { role: 'SUPER_ADMIN' | 'ADMIN' | 'USER'; } +/** + * Extracts the backend's error message from a non-2xx JSON error body + * (Nest's default exception filter shape: `{ statusCode, message, error }`). + * Bewusst lokal kopiert statt aus `apps/web/src/lib/tender-radar-api.ts` + * importiert (jene Datei gehoert zum Modul Ausschreibungs-Radar) — nur das + * Feld `message` wird gelesen, nie der ganze Rumpf (T-A1D-01). + */ +async function readApiMessage(res: Response): Promise { + try { + const body = (await res.json()) as { message?: unknown }; + if (typeof body.message === 'string' && body.message) return body.message; + if (Array.isArray(body.message) && body.message.length) { + return body.message.join(', '); + } + } catch { + /* Rumpf war kein JSON — Ersatzmeldung greift beim Aufrufer */ + } + return null; +} + /** * Admin users page -- User CRUD management (D-12). * ADMIN and SUPER_ADMIN can access. ADMIN sees only own-tenant users. @@ -44,6 +64,7 @@ export default function AdminUsersPage() { const [showForm, setShowForm] = useState(false); const [editingUser, setEditingUser] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState(null); + const [deleteError, setDeleteError] = useState(null); const [detailsUser, setDetailsUser] = useState(null); const [formData, setFormData] = useState({ username: '', @@ -142,6 +163,7 @@ export default function AdminUsersPage() { }; const handleDelete = async (id: string) => { + setDeleteError(null); try { const res = await fetch(`${API_URL}/users/${id}`, { method: 'DELETE', @@ -150,9 +172,14 @@ export default function AdminUsersPage() { if (res.ok) { setDeleteConfirm(null); fetchUsers(); + } else { + const detail = await readApiMessage(res); + setDeleteError( + detail ? t('errors.serverRejected', { detail }) : t('errors.generic'), + ); } } catch { - // silently fail + setDeleteError(t('errors.network')); } }; @@ -263,7 +290,10 @@ export default function AdminUsersPage() { {tCommon('edit')}