diff --git a/src/main/webapp/app/admin/user-management/user-management-update.component.html b/src/main/webapp/app/admin/user-management/user-management-update.component.html index 71f70e3752db..bc55e3a13acb 100644 --- a/src/main/webapp/app/admin/user-management/user-management-update.component.html +++ b/src/main/webapp/app/admin/user-management/user-management-update.component.html @@ -1,7 +1,11 @@
-

+ @if (user.id === undefined) { +

+ } @else { +

+ }
@@ -83,7 +87,7 @@

@@ -255,15 +259,7 @@

diff --git a/src/main/webapp/app/admin/user-management/user-management-update.component.ts b/src/main/webapp/app/admin/user-management/user-management-update.component.ts index 2c4760d74f09..2d7e318f9038 100644 --- a/src/main/webapp/app/admin/user-management/user-management-update.component.ts +++ b/src/main/webapp/app/admin/user-management/user-management-update.component.ts @@ -16,7 +16,6 @@ import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { AlertService, AlertType } from 'app/core/util/alert.service'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { AdminUserService } from 'app/core/user/admin-user.service'; -import { CourseManagementService } from 'app/course/manage/course-management.service'; import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; import { CourseAdminService } from 'app/course/manage/course-admin.service'; @@ -59,7 +58,6 @@ export class UserManagementUpdateComponent implements OnInit { constructor( private languageHelper: JhiLanguageHelper, private userService: AdminUserService, - private courseManagementService: CourseManagementService, private courseAdminService: CourseAdminService, private route: ActivatedRoute, private organizationService: OrganizationManagementService, @@ -232,11 +230,17 @@ export class UserManagementUpdateComponent implements OnInit { passwordInput: ['', [Validators.minLength(PASSWORD_MIN_LENGTH), Validators.maxLength(PASSWORD_MAX_LENGTH)]], emailInput: ['', [Validators.required, Validators.minLength(this.EMAIL_MIN_LENGTH), Validators.maxLength(this.EMAIL_MAX_LENGTH)]], registrationNumberInput: ['', [Validators.maxLength(this.REGISTRATION_NUMBER_MAX_LENGTH)]], - activatedInput: ['', []], + activatedInput: [{ value: this.user.activated }], langKeyInput: ['', []], authorityInput: ['', []], - internalInput: [{ value: this.user.internal, disabled: true }], + internalInput: [{ value: this.user.internal, disabled: true }], // initially disabled, will be enabled if user.id is undefined }); + // Conditionally enable or disable 'internalInput' based on user.id + if (this.user.id !== undefined) { + this.editForm.get('internalInput')?.disable(); // Artemis does not support to edit the internal flag for existing users + } else { + this.editForm.get('internalInput')?.enable(); // New users can either be internal or external + } } /** diff --git a/src/main/webapp/app/admin/user-management/user-management.component.html b/src/main/webapp/app/admin/user-management/user-management.component.html index 129f30d67feb..c8f49ab96a2e 100644 --- a/src/main/webapp/app/admin/user-management/user-management.component.html +++ b/src/main/webapp/app/admin/user-management/user-management.component.html @@ -27,13 +27,13 @@

name="searchTerm" id="field_searchTerm" formControlName="searchControl" - [(ngModel)]="searchTerm" - (focusout)="loadAll()" + (blur)="loadAll()" + (keydown)="onKeydown($event)" /> - @if (searchControl.invalid && (searchControl.dirty || searchControl.touched)) { + @if (searchInvalid) {
diff --git a/src/main/webapp/app/admin/user-management/user-management.component.ts b/src/main/webapp/app/admin/user-management/user-management.component.ts index 5ce57b8d4ed1..f7870f5369cf 100644 --- a/src/main/webapp/app/admin/user-management/user-management.component.ts +++ b/src/main/webapp/app/admin/user-management/user-management.component.ts @@ -7,8 +7,8 @@ import { User } from 'app/core/user/user.model'; import { AccountService } from 'app/core/auth/account.service'; import { AlertService } from 'app/core/util/alert.service'; import { SortingOrder } from 'app/shared/table/pageable-table'; -import { debounceTime, switchMap, tap } from 'rxjs/operators'; -import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; +import { switchMap, tap } from 'rxjs/operators'; +import { FormControl, FormGroup } from '@angular/forms'; import { EventManager } from 'app/core/util/event-manager.service'; import { ASC, DESC, ITEMS_PER_PAGE, SORT } from 'app/shared/constants/pagination.constants'; import { faEye, faFilter, faPlus, faSort, faTimes, faWrench } from '@fortawesome/free-solid-svg-icons'; @@ -17,7 +17,6 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { AdminUserService } from 'app/core/user/admin-user.service'; -import { UserService } from 'app/core/user/user.service'; export class UserFilter { authorityFilter: Set = new Set(); @@ -103,6 +102,7 @@ export class UserManagementComponent implements OnInit, OnDestroy { predicate!: string; ascending!: boolean; searchTermString = ''; + searchInvalid = false; isLdapProfileActive: boolean; // filters @@ -129,7 +129,6 @@ export class UserManagementComponent implements OnInit, OnDestroy { constructor( private adminUserService: AdminUserService, - private userService: UserService, private alertService: AlertService, private accountService: AccountService, private activatedRoute: ActivatedRoute, @@ -148,7 +147,6 @@ export class UserManagementComponent implements OnInit, OnDestroy { this.search .pipe( tap(() => (this.loadingSearchResult = true)), - debounceTime(1000), switchMap(() => this.adminUserService.query( { @@ -175,7 +173,7 @@ export class UserManagementComponent implements OnInit, OnDestroy { }); this.userSearchForm = new FormGroup({ - searchControl: new FormControl('', { validators: [this.validateUserSearch], updateOn: 'blur' }), + searchControl: new FormControl('', { updateOn: 'change' }), }); this.accountService.identity().then((user) => { this.currentAccount = user!; @@ -443,17 +441,21 @@ export class UserManagementComponent implements OnInit, OnDestroy { * Retrieve the list of users from the user service for a single page in the user management based on the page, size and sort configuration */ loadAll() { + this.searchTerm = this.searchControl.value; if (this.searchTerm.length >= 3 || this.searchTerm.length === 0) { + this.searchInvalid = false; this.search.next(); + } else { + this.searchInvalid = true; } } /** * Returns the unique identifier for items in the collection - * @param index of a user in the collection + * @param _index of a user in the collection * @param item current user */ - trackIdentity(index: number, item: User) { + trackIdentity(_index: number, item: User) { return item.id ?? -1; } @@ -520,14 +522,14 @@ export class UserManagementComponent implements OnInit, OnDestroy { return this.searchTermString; } - validateUserSearch(control: AbstractControl) { - if (control.value.length >= 1 && control.value.length <= 2) { - return { searchControl: true }; - } - return null; - } - get searchControl() { return this.userSearchForm.get('searchControl')!; } + + onKeydown(event: KeyboardEvent) { + if (event.key === 'Enter') { + event.preventDefault(); // Prevent the default form submission behavior + this.loadAll(); // Trigger the search logic + } + } } diff --git a/src/main/webapp/app/admin/user-management/user-management.route.ts b/src/main/webapp/app/admin/user-management/user-management.route.ts index e98972a5c3f0..df2178c4a68b 100644 --- a/src/main/webapp/app/admin/user-management/user-management.route.ts +++ b/src/main/webapp/app/admin/user-management/user-management.route.ts @@ -51,7 +51,7 @@ export const userManagementRoute: Route[] = [ path: 'edit', component: UserManagementUpdateComponent, data: { - pageTitle: 'artemisApp.userManagement.home.createOrEditLabel', + pageTitle: 'artemisApp.userManagement.home.editLabel', }, }, ], diff --git a/src/main/webapp/i18n/de/user-management.json b/src/main/webapp/i18n/de/user-management.json index 365abf875684..6be2470bd4fd 100644 --- a/src/main/webapp/i18n/de/user-management.json +++ b/src/main/webapp/i18n/de/user-management.json @@ -64,7 +64,7 @@ "home": { "title": "Nutzer:in", "createLabel": "Nutzer:in erstellen", - "createOrEditLabel": "Nutzer:in erstellen oder bearbeiten" + "editLabel": "Nutzer:in bearbeiten" }, "created": "Nutzer:in wurde mit ID {{ param }} erstellt", "updated": "Nutzer:in mit ID {{ param }} wurde geändert", @@ -93,7 +93,7 @@ "profiles": "Profile", "langKey": "Sprache", "internal": "Intern", - "passwordTooltip": "Du kannst nur Passwörter für interne Nutzer:innen ändern.", + "passwordTooltip": "Du kannst diese Einstellung nur für neue Nutzer:innen ändern. Bitte beachte, dass du nur Passwörter für interne Nutzer:innen ändern kannst.", "createdBy": "Erstellt von", "createdDate": "Erstellt am", "lastModifiedBy": "Bearbeitet von", diff --git a/src/main/webapp/i18n/en/user-management.json b/src/main/webapp/i18n/en/user-management.json index 689da224ef2d..f730d94045d4 100644 --- a/src/main/webapp/i18n/en/user-management.json +++ b/src/main/webapp/i18n/en/user-management.json @@ -64,7 +64,7 @@ "home": { "title": "Users", "createLabel": "Create a new user", - "createOrEditLabel": "Create or edit a user" + "editLabel": "Edit user" }, "created": "Created new user with identifier {{ param }}", "updated": "Updated User with identifier {{ param }}", @@ -92,7 +92,7 @@ "deactivated": "Deactivated", "profiles": "Profiles", "langKey": "Language", - "passwordTooltip": "You can only change passwords for internal users.", + "passwordTooltip": "This setting can only be changed for new users. Notice, that you can only change passwords for internal users.", "internal": "Internal", "createdBy": "Created by", "createdDate": "Created date", diff --git a/src/test/javascript/spec/component/admin/user-management.component.spec.ts b/src/test/javascript/spec/component/admin/user-management.component.spec.ts index 4b2ee62e13a4..a4c5dda88562 100644 --- a/src/test/javascript/spec/component/admin/user-management.component.spec.ts +++ b/src/test/javascript/spec/component/admin/user-management.component.spec.ts @@ -14,7 +14,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { HttpHeaders, HttpParams, HttpResponse, provideHttpClient } from '@angular/common/http'; import { User } from 'app/core/user/user.model'; import { Subscription, of } from 'rxjs'; -import { AbstractControl, ReactiveFormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; import { ItemCountComponent } from 'app/shared/pagination/item-count.component'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @@ -168,7 +168,7 @@ describe('UserManagementComponent', () => { // THEN expect(userService.update).toHaveBeenCalledWith({ ...user, activated: true }); - expect(userService.query).toHaveBeenCalledOnce(); + expect(userService.query).toHaveBeenCalledTimes(2); expect(comp.users && comp.users[0]).toEqual(expect.objectContaining({ id: 123 })); expect(profileSpy).toHaveBeenCalledOnce(); }), @@ -243,14 +243,17 @@ describe('UserManagementComponent', () => { jest.restoreAllMocks(); }); - it('should validate user search correctly', () => { - expect(comp.validateUserSearch({ value: [] } as AbstractControl)).toBeNull(); - expect(comp.validateUserSearch({ value: [0] } as AbstractControl)).toEqual({ searchControl: true }); - expect(comp.validateUserSearch({ value: [0, 0] } as AbstractControl)).toEqual({ searchControl: true }); - expect(comp.validateUserSearch({ value: [0, 0, 0] } as AbstractControl)).toBeNull(); - }); - it('should call initFilters', () => { + const headers = new HttpHeaders().append('link', 'link;link'); + const user = new User(123); + jest.spyOn(userService, 'query').mockReturnValue( + of( + new HttpResponse({ + body: [user], + headers, + }), + ), + ); const spy = jest.spyOn(comp, 'initFilters'); const initSpy = jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(new ProfileInfo())); @@ -258,6 +261,7 @@ describe('UserManagementComponent', () => { expect(spy).toHaveBeenCalledOnce(); expect(initSpy).toHaveBeenCalledOnce(); + expect(userService.query).toHaveBeenCalledTimes(0); }); it.each`