diff --git a/myteamwallet_frontend_modern/src/app/features/users/users.html b/myteamwallet_frontend_modern/src/app/features/users/users.html
index c04da1c..ef1e9a2 100644
--- a/myteamwallet_frontend_modern/src/app/features/users/users.html
+++ b/myteamwallet_frontend_modern/src/app/features/users/users.html
@@ -15,10 +15,11 @@
type="search"
name="directorySearch"
[value]="searchDraft()"
+ [disabled]="assignmentBusyUserId() !== null"
(input)="searchDraft.set($any($event.target).value)"
/>
-
+
@if (mutationError()) {
@@ -138,11 +139,21 @@
diff --git a/myteamwallet_frontend_modern/src/app/features/users/users.spec.ts b/myteamwallet_frontend_modern/src/app/features/users/users.spec.ts
index fa7e367..867217d 100644
--- a/myteamwallet_frontend_modern/src/app/features/users/users.spec.ts
+++ b/myteamwallet_frontend_modern/src/app/features/users/users.spec.ts
@@ -474,6 +474,80 @@ describe('Users directory', () => {
http.expectOne(`${adminApi}/players?assignment=all&page=1&limit=20`).flush(playersPage());
});
+ it('blocks directory search and paging while an assignment mutation is pending, then restores them', () => {
+ isAdmin.set(true);
+ create();
+ http.expectOne(`${api}?page=1&limit=20`).flush(directoryPage([ada], 1, 60, true));
+ fixture.detectChanges();
+ button('Weiter').click();
+ http.expectOne(`${api}?page=2&limit=20`).flush(directoryPage([ada], 2, 60, true));
+ fixture.detectChanges();
+ button('Zuordnungen verwalten').click();
+ fixture.detectChanges();
+ http.expectOne(`${adminApi}/players?assignment=all&page=1&limit=20`).flush(
+ playersPage({
+ data: [
+ {
+ id: 101,
+ firstName: 'Ada',
+ lastName: 'Lovelace',
+ active: true,
+ team: { id: 5, name: 'First Team', alias: 'first' },
+ currentUser: { id: 7, firstName: 'Ada', lastName: 'Lovelace', status: { id: 1, name: 'Active' } },
+ },
+ ],
+ }),
+ );
+ fixture.detectChanges();
+ childButton(
+ (fixture.nativeElement as HTMLElement).querySelector('app-player-assignments')!,
+ 'Verknüpfung lösen',
+ ).click();
+ closeDialog.next(true);
+ fixture.detectChanges();
+ const unlink = http.expectOne(`${adminApi}/7/players/101`);
+
+ const directoryForm = (fixture.nativeElement as HTMLElement).querySelector('.directory-search')!;
+ const directorySearch = directoryForm.querySelector('input[type="search"]')!;
+ const directorySearchButton = directoryForm.querySelector('button[type="submit"]')!;
+ const paging = (fixture.nativeElement as HTMLElement).querySelector('nav.pagination')!;
+ const [previous, next] = [...paging.querySelectorAll('button')];
+ const controlsWereBlocked = directorySearch.disabled && directorySearchButton.disabled && previous.disabled && next.disabled;
+
+ directorySearch.value = 'Other';
+ directorySearch.dispatchEvent(new Event('input'));
+ directoryForm.dispatchEvent(new Event('submit'));
+ previous.click();
+ next.click();
+ fixture.detectChanges();
+ const unexpectedQueries = http.match((request) => request.url.startsWith(api));
+ const mutationStayedActive = !unlink.cancelled;
+
+ if (controlsWereBlocked && unexpectedQueries.length === 0 && mutationStayedActive) {
+ unlink.flush(ada);
+ http.expectOne(`${api}?page=2&limit=20`).flush(directoryPage([ada], 2, 60, true));
+ http.expectOne(`${adminApi}/players?assignment=all&page=1&limit=20`).flush(playersPage());
+ fixture.detectChanges();
+ expect(directorySearch.disabled).toBe(false);
+ expect(directorySearchButton.disabled).toBe(false);
+ expect(previous.disabled).toBe(false);
+ expect(next.disabled).toBe(false);
+ directoryForm.dispatchEvent(new Event('submit'));
+ http.expectOne(`${api}?page=1&limit=20&search=Other`).flush(directoryPage([]));
+ } else {
+ for (const query of unexpectedQueries) query.flush(directoryPage([ada], 1, 60, true));
+ fixture.detectChanges();
+ if (!unlink.cancelled) unlink.flush(ada);
+ for (const playerLoad of http.match((request) => request.url.startsWith(`${adminApi}/players`))) {
+ playerLoad.flush(playersPage());
+ }
+ }
+
+ expect(controlsWereBlocked).toBe(true);
+ expect(unexpectedQueries).toHaveLength(0);
+ expect(mutationStayedActive).toBe(true);
+ });
+
it('shows a persistent explanation for disabled self-deactivation', () => {
isAdmin.set(true);
currentUser.set({ id: 1, firstName: 'Grace', lastName: 'Admin', role: { id: 1 } });
diff --git a/myteamwallet_frontend_modern/src/app/features/users/users.ts b/myteamwallet_frontend_modern/src/app/features/users/users.ts
index 47e8278..ab30355 100644
--- a/myteamwallet_frontend_modern/src/app/features/users/users.ts
+++ b/myteamwallet_frontend_modern/src/app/features/users/users.ts
@@ -90,6 +90,7 @@ export class Users {
}
protected submitSearch(): void {
+ if (this.assignmentBusyUserId() !== null) return;
const search = this.searchDraft().trim();
if (this.loading() && search === this.search() && this.page() === 1) return;
this.search.set(search);
@@ -98,13 +99,13 @@ export class Users {
}
protected previousPage(): void {
- if (this.page() <= 1 || this.loading()) return;
+ if (this.page() <= 1 || this.loading() || this.assignmentBusyUserId() !== null) return;
this.page.update((value) => value - 1);
this.loadDirectory();
}
protected nextPage(): void {
- if (!this.directory()?.hasNextPage || this.loading()) return;
+ if (!this.directory()?.hasNextPage || this.loading() || this.assignmentBusyUserId() !== null) return;
this.page.update((value) => value + 1);
this.loadDirectory();
}