+ arrow_backZurück
+
+
+
+
+ @if (mutationError()) {
+ {{ mutationError() }}
+ }
+
+ @if (loading()) {
+
+
+ Benutzer werden geladen …
+
+ } @else if (loadError()) {
+
+ error_outline
+ {{ loadError() }}
+
+
+ } @else if (directory()?.data?.length === 0) {
+
+ group_off
+ Keine Benutzer gefunden
+ Versuche einen anderen Suchbegriff.
+
+ } @else if (directory()) {
+
+ @for (user of directory()!.data; track user.id) {
+
+
+
{{ initials(user) }}
+
+
+
{{ fullName(user) }}
+
+ {{ statusName(user.status?.id) }}
+
+
+ @if (adminDetails(user); as details) {
+
{{ details.email ?? 'Keine E-Mail' }} · {{ roleName(details.role?.id) }}
+ }
+ @if (user.assignments.length === 0) {
+
Keine Spielerzuordnung sichtbar
+ } @else {
+
+ @for (assignment of user.assignments; track assignment.id) {
+
+ {{ assignment.team.name }} · {{ assignment.firstName }} {{ assignment.lastName }} ·
+ {{ teamRoleName(assignment.teamRole?.name) }}
+ @if (!assignment.active) { · Inaktiv }
+
+ }
+
+ }
+
+
+
+ @if (adminDetails(user); as details) {
+
+
+
+
+
+
+ @if (editingUserId() === user.id) {
+
+ }
+ @if (assignmentUserId() === user.id) {
+
+ }
+ }
+
+ }
+
+
+
+ }
+
diff --git a/myteamwallet_frontend_modern/src/app/features/users/users.scss b/myteamwallet_frontend_modern/src/app/features/users/users.scss
new file mode 100644
index 0000000..730f9ce
--- /dev/null
+++ b/myteamwallet_frontend_modern/src/app/features/users/users.scss
@@ -0,0 +1,191 @@
+:host {
+ display: block;
+ min-height: 100dvh;
+ background: var(--mat-sys-surface);
+}
+
+.users-page {
+ max-width: 960px;
+ margin: 0 auto;
+ padding: 24px 28px 40px;
+}
+
+.back-link {
+ margin-left: -12px;
+}
+
+.page-header {
+ margin: 20px 0 26px;
+}
+
+h1,
+h2,
+p {
+ margin-top: 0;
+}
+
+h1 {
+ margin-bottom: 8px;
+ font-size: clamp(2rem, 4vw, 3rem);
+}
+
+.page-header > p:last-child {
+ color: var(--mat-sys-on-surface-variant);
+}
+
+.eyebrow {
+ margin-bottom: 6px;
+ color: var(--mat-sys-primary);
+ font-size: 0.75rem;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.directory-search {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 22px;
+}
+
+.directory-search mat-form-field {
+ flex: 1;
+}
+
+.directory {
+ border-top: 1px solid var(--mat-sys-outline-variant);
+}
+
+.user-row {
+ display: grid;
+ gap: 16px;
+ padding: 20px 0;
+ border-bottom: 1px solid var(--mat-sys-outline-variant);
+}
+
+.user-row--inactive .avatar {
+ filter: grayscale(1);
+ opacity: 0.65;
+}
+
+.user-row__summary {
+ display: grid;
+ grid-template-columns: 48px minmax(0, 1fr);
+ gap: 14px;
+}
+
+.avatar {
+ width: 48px;
+ height: 48px;
+ display: grid;
+ place-items: center;
+ border-radius: 50%;
+ background: var(--mat-sys-primary-container);
+ color: var(--mat-sys-on-primary-container);
+ font-weight: 700;
+}
+
+.user-row__identity,
+.assignment-summary {
+ display: grid;
+ gap: 4px;
+ min-width: 0;
+}
+
+.user-row__identity > span,
+.assignment-summary span {
+ color: var(--mat-sys-on-surface-variant);
+}
+
+.user-row__name {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.user-row__name h2 {
+ margin-bottom: 0;
+ font-size: 1.08rem;
+}
+
+.status {
+ display: inline-flex;
+ align-items: center;
+ min-height: 24px;
+ padding: 0 9px;
+ border-radius: 999px;
+ background: var(--mat-sys-primary-container);
+ color: var(--mat-sys-on-primary-container);
+ font-size: 0.75rem;
+ font-weight: 700;
+}
+
+.status--inactive {
+ background: var(--mat-sys-surface-container-high);
+ color: var(--mat-sys-on-surface-variant);
+}
+
+.user-row__actions {
+ display: flex;
+ justify-content: flex-end;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+
+.page-state {
+ min-height: 240px;
+ display: grid;
+ place-content: center;
+ justify-items: center;
+ gap: 10px;
+ padding: 24px;
+ color: var(--mat-sys-on-surface-variant);
+ text-align: center;
+}
+
+.page-state--error {
+ color: var(--mat-sys-error);
+}
+
+.message {
+ margin-bottom: 16px;
+ padding: 12px 14px;
+ border-radius: 12px;
+}
+
+.message--error {
+ color: var(--mat-sys-on-error-container);
+ background: var(--mat-sys-error-container);
+}
+
+.pagination {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ margin-top: 18px;
+ color: var(--mat-sys-on-surface-variant);
+}
+
+@media (max-width: 700px) {
+ .users-page {
+ padding: 20px 16px 32px;
+ }
+
+ .directory-search,
+ .user-row__actions {
+ align-items: stretch;
+ flex-direction: column;
+ }
+
+ .directory-search button,
+ .user-row__actions button {
+ width: 100%;
+ }
+
+ .pagination span {
+ font-size: 0.8rem;
+ text-align: center;
+ }
+}
diff --git a/myteamwallet_frontend_modern/src/app/features/users/users.spec.ts b/myteamwallet_frontend_modern/src/app/features/users/users.spec.ts
new file mode 100644
index 0000000..c65211a
--- /dev/null
+++ b/myteamwallet_frontend_modern/src/app/features/users/users.spec.ts
@@ -0,0 +1,381 @@
+import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
+import { provideHttpClient } from '@angular/common/http';
+import { signal } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { MatDialog } from '@angular/material/dialog';
+import { provideRouter } from '@angular/router';
+import { Subject } from 'rxjs';
+import { environment } from '../../../environments/environment';
+import { AuthStore } from '../../core/auth/auth-store';
+import { AdminPlayerPage, AdminUserDirectorySummary, UserDirectoryPage } from '../../models/user-directory.model';
+import { Users } from './users';
+
+const api = `${environment.apiUrl}users/directory`;
+const adminApi = `${environment.apiUrl}admin/users`;
+
+const ada: AdminUserDirectorySummary = {
+ id: 7,
+ firstName: 'Ada',
+ lastName: 'Lovelace',
+ email: 'ada@example.test',
+ role: { id: 2, name: 'User' },
+ status: { id: 1, name: 'Active' },
+ assignments: [
+ {
+ id: 101,
+ firstName: 'Ada',
+ lastName: 'Lovelace',
+ active: true,
+ team: { id: 5, name: 'First Team', alias: 'first' },
+ teamRole: { id: 1, name: 'player' },
+ },
+ ],
+};
+
+const admin: AdminUserDirectorySummary = {
+ id: 1,
+ firstName: 'Grace',
+ lastName: 'Admin',
+ email: 'grace@example.test',
+ role: { id: 1, name: 'Admin' },
+ status: { id: 1, name: 'Active' },
+ assignments: [],
+};
+
+function directoryPage(data = [ada], page = 1, total = data.length, hasNextPage = false): UserDirectoryPage {
+ return { data, page, limit: 20, total, hasNextPage };
+}
+
+function playersPage(overrides: Partial