feat(15-07): Benutzer-Detail mit geerbten Rechten und Direkt-Freigaben
- Neue UserAccessModal.tsx: laedt einmal GET /module-grants/users/:userId und rendert daraus zwei Abschnitte -- Gruppenmitgliedschaften (read-only Chip-Liste, dedupliziert aus allen viaGroups-Namen; Bearbeitung bleibt ausschliesslich unter /admin/groups, D-16) und Modul-Zugriff (Modul | erbende Gruppen als Chips oder "–" | Direkt-Checkbox) - Direkt-Checkbox verhaelt sich identisch zur Matrix-Zelle: optimistisches Toggle via POST/DELETE /module-grants mit moduleId+userId, Rollback samt sichtbarer Fehlermeldung bei Fehlschlag (T-15-25), aria-label pro Zeile aus admin.users.grants.directCheckboxLabel - admin/users/page.tsx: vierter Aktionsbutton "Details" je Zeile oeffnet das Modal - user-access-modal.test.tsx: 5 Tests (Chip-Liste + Leerzustand, Modultabelle mit geerbtem/nicht-geerbtem Modul, Rollback bei Fehler, Hinweistext ohne aktive Module, aria-label je Checkbox)
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useAuthStore } from '@/lib/stores/auth-store';
|
||||
import { UserAccessModal } from './components/UserAccessModal';
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
||||
|
||||
@@ -40,6 +41,7 @@ export default function AdminUsersPage() {
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [editingUser, setEditingUser] = useState<User | null>(null);
|
||||
const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);
|
||||
const [detailsUser, setDetailsUser] = useState<User | null>(null);
|
||||
const [formData, setFormData] = useState<UserFormData>({
|
||||
username: '',
|
||||
email: '',
|
||||
@@ -245,6 +247,12 @@ export default function AdminUsersPage() {
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<button
|
||||
onClick={() => setDetailsUser(user)}
|
||||
className="rounded px-2 py-1 text-xs text-foreground hover:bg-muted transition-colors"
|
||||
>
|
||||
{t('grants.detailsButton')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => openEdit(user)}
|
||||
className="rounded px-2 py-1 text-xs text-foreground hover:bg-muted transition-colors"
|
||||
@@ -403,6 +411,15 @@ export default function AdminUsersPage() {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* User-Detail-Zugriff (D-16): geerbte + direkte Modulfreigaben */}
|
||||
{detailsUser && (
|
||||
<UserAccessModal
|
||||
userId={detailsUser.id}
|
||||
username={detailsUser.displayName ?? detailsUser.username}
|
||||
onClose={() => setDetailsUser(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user