- Add activeTab signal ('pending' | 'all') with tab navigation UI
- Add allUsers signal with loadAllUsers() using generated AdminIAMService.getAllUsers()
- Add getStatusLabel() helper for German status labels (Ausstehend/Aktiv/Freigeschaltet/Inaktiv/Abgelehnt)
- Add getStatusBadgeClass() for color-coded status badges (yellow/green/red)
- Fix duplicate 'Status' column header bug in pending users table
- All-users tab shows Name, Email, Status badge, Rolle columns (no action buttons)
- Add 22 passing tests covering signals, helpers, tab switching, approve/reject, and template
168 lines
6.6 KiB
HTML
168 lines
6.6 KiB
HTML
<div class="container mx-auto p-6 max-w-6xl">
|
|
<div class="mb-8">
|
|
<h1 class="text-3xl font-bold text-gray-900">User Freigabe</h1>
|
|
<p class="text-gray-600 mt-2">Prüfung der ausstehenden Anmeldungen und EDA-Netz-Checks.</p>
|
|
</div>
|
|
|
|
<!-- Tab Navigation -->
|
|
<div class="border-b border-gray-200 mb-6">
|
|
<nav class="flex gap-8" aria-label="Tabs">
|
|
<button
|
|
role="tab"
|
|
[attr.aria-selected]="activeTab() === 'pending'"
|
|
[class]="
|
|
activeTab() === 'pending'
|
|
? 'border-b-2 border-indigo-500 text-indigo-600 py-3 px-1 text-sm font-medium'
|
|
: 'border-b-2 border-transparent text-gray-500 hover:text-gray-700 py-3 px-1 text-sm font-medium'
|
|
"
|
|
(click)="switchTab('pending')"
|
|
>
|
|
Ausstehende Anträge
|
|
</button>
|
|
<button
|
|
role="tab"
|
|
[attr.aria-selected]="activeTab() === 'all'"
|
|
[class]="
|
|
activeTab() === 'all'
|
|
? 'border-b-2 border-indigo-500 text-indigo-600 py-3 px-1 text-sm font-medium'
|
|
: 'border-b-2 border-transparent text-gray-500 hover:text-gray-700 py-3 px-1 text-sm font-medium'
|
|
"
|
|
(click)="switchTab('all')"
|
|
>
|
|
Alle Benutzer
|
|
</button>
|
|
</nav>
|
|
</div>
|
|
|
|
@if (errorMessage()) {
|
|
<div class="bg-red-50 border-l-4 border-red-500 text-red-700 p-4 mb-6" role="alert">
|
|
{{ errorMessage() }}
|
|
</div>
|
|
}
|
|
|
|
@if (isLoading()) {
|
|
<div class="flex items-center justify-center py-12">
|
|
<div class="text-gray-500 text-lg">Lade Daten...</div>
|
|
</div>
|
|
}
|
|
|
|
@if (!isLoading() && activeTab() === 'pending') {
|
|
@if (pendingUsers().length === 0) {
|
|
<div class="bg-gray-50 border border-gray-200 rounded-lg p-8 text-center">
|
|
<p class="text-gray-600 text-lg">Aktuell gibt es keine neuen User im Status PENDING.</p>
|
|
</div>
|
|
} @else {
|
|
<div class="bg-white shadow-sm border border-gray-200 rounded-lg overflow-hidden">
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full text-left border-collapse">
|
|
<thead>
|
|
<tr class="bg-gray-50 border-b border-gray-200">
|
|
<th class="p-4 text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
|
Benutzer
|
|
</th>
|
|
<th class="p-4 text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
|
Status
|
|
</th>
|
|
<th
|
|
class="p-4 text-sm font-semibold text-gray-700 uppercase tracking-wider text-right"
|
|
>
|
|
Aktionen
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-gray-200">
|
|
@for (user of pendingUsers(); track user.id) {
|
|
<tr class="hover:bg-gray-50 transition-colors">
|
|
<td class="p-4">
|
|
<div class="font-medium text-gray-900">
|
|
{{ user.firstName }} {{ user.lastName }}
|
|
</div>
|
|
<div class="text-sm text-gray-500">{{ user.email }}</div>
|
|
</td>
|
|
<td class="p-4">
|
|
<span
|
|
[class]="
|
|
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ' +
|
|
getStatusBadgeClass(user.status || 'PENDING')
|
|
"
|
|
>
|
|
{{ getStatusLabel(user.status || 'PENDING') }}
|
|
</span>
|
|
</td>
|
|
<td class="p-4 flex justify-end gap-3">
|
|
<button
|
|
(click)="approveUser(user.id)"
|
|
class="inline-flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 transition"
|
|
>
|
|
Freigeben
|
|
</button>
|
|
<button
|
|
(click)="rejectUser(user)"
|
|
class="inline-flex items-center justify-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-red-700 bg-white hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition"
|
|
>
|
|
Ablehnen
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
@if (!isLoading() && activeTab() === 'all') {
|
|
@if (allUsers().length === 0) {
|
|
<div class="bg-gray-50 border border-gray-200 rounded-lg p-8 text-center">
|
|
<p class="text-gray-600 text-lg">Keine Benutzer vorhanden.</p>
|
|
</div>
|
|
} @else {
|
|
<div class="bg-white shadow-sm border border-gray-200 rounded-lg overflow-hidden">
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full text-left border-collapse">
|
|
<thead>
|
|
<tr class="bg-gray-50 border-b border-gray-200">
|
|
<th class="p-4 text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
|
Benutzer
|
|
</th>
|
|
<th class="p-4 text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
|
Status
|
|
</th>
|
|
<th class="p-4 text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
|
Rolle
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-gray-200">
|
|
@for (user of allUsers(); track user.id) {
|
|
<tr class="hover:bg-gray-50 transition-colors">
|
|
<td class="p-4">
|
|
<div class="font-medium text-gray-900">
|
|
{{ user.firstName }} {{ user.lastName }}
|
|
</div>
|
|
<div class="text-sm text-gray-500">{{ user.email }}</div>
|
|
</td>
|
|
<td class="p-4">
|
|
<span
|
|
[class]="
|
|
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ' +
|
|
getStatusBadgeClass(user.status || 'UNKNOWN')
|
|
"
|
|
>
|
|
{{ getStatusLabel(user.status || 'UNKNOWN') }}
|
|
</span>
|
|
</td>
|
|
<td class="p-4 text-sm text-gray-700">
|
|
{{ user.participantType === 'COMPANY' ? 'Unternehmen' : 'Privatperson' }}
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
</div>
|