Skip to content

Commit

Permalink
Merge branch 'main' into IrKa/e2e
Browse files Browse the repository at this point in the history
* main:
  fix: project member resource edit permissions (#DEV-3479) (#1554)
  refactor(user-form): Refactor user form and make proper use of dialog (#1503)
  fix: resource tooltip fit content (#DEV-3383) (#1553)
  fix: filters out "has image file" property (DEV-3477) (#1548)
  feat: use mat label in mat-form-field (#1545)
  fix: color picker style (DEV-3476) (#1551)
  fix: removed mergeTableCells functionality from CKEditor (#DEV-3473) (#1552)
  fix: data-model-class e2e (DEV-) (#1547)
  • Loading branch information
irmastnt committed Apr 19, 2024
2 parents 5e2b53a + c0909cd commit ca7d85a
Show file tree
Hide file tree
Showing 41 changed files with 482 additions and 621 deletions.
4 changes: 4 additions & 0 deletions apps/dsp-app/src/app/app.module.ts
Expand Up @@ -121,6 +121,8 @@ import { SystemComponent } from './system/system.component';
import { UsersListComponent } from './system/users/users-list/users-list.component';
import { UsersComponent } from './system/users/users.component';
import { AccountComponent } from './user/account/account.component';
import { CreateUserPageComponent } from './user/create-user-page/create-user-page.component';
import { EditUserPageComponent } from './user/edit-user-page/edit-user-page.component';
import { MembershipComponent } from './user/membership/membership.component';
import { OverviewComponent } from './user/overview/overview.component';
import { ProfileComponent } from './user/profile/profile.component';
Expand Down Expand Up @@ -219,6 +221,7 @@ export function httpLoaderFactory(httpClient: HttpClient) {
ConfirmDialogComponent,
ConfirmationMessageComponent,
CookiePolicyComponent,
CreateUserPageComponent,
CreateLinkResourceComponent,
CreateResourceClassDialogComponent,
DateValueComponent,
Expand All @@ -231,6 +234,7 @@ export function httpLoaderFactory(httpClient: HttpClient) {
DocumentComponent,
DragDropDirective,
EditResourceClassDialogComponent,
EditUserPageComponent,
ExpertSearchComponent,
FooterComponent,
FormattedBooleanPipe,
Expand Down
19 changes: 1 addition & 18 deletions apps/dsp-app/src/app/main/dialog/dialog.component.html
@@ -1,21 +1,4 @@
<div [ngSwitch]="data.mode" class="dialog-content">
<!-- all about USER -->
<!-- Create new user profile -->
<div *ngSwitchCase="'createUser'">
<app-dialog-header
[title]="'appLabels.form.user.title.profile' | translate"
[subtitle]="'appLabels.form.user.title.new' | translate">
</app-dialog-header>
<app-user-form [name]="data.name" [projectUuid]="data.project" (closeDialog)="dialogRef.close()"> </app-user-form>
</div>

<!-- Modify user data -->
<div *ngSwitchCase="'editUser'">
<app-dialog-header [title]="data.user.username" [subtitle]="'appLabels.form.user.title.edit' | translate">
</app-dialog-header>
<app-user-form [user]="data.user" (closeDialog)="dialogRef.close()"></app-user-form>
</div>

<div [ngSwitch]="data.mode">
<!-- Update password -->
<div *ngSwitchCase="'editPassword'">
<app-dialog-header [title]="data.user.username" [subtitle]="'appLabels.form.user.title.password' | translate">
Expand Down
8 changes: 0 additions & 8 deletions apps/dsp-app/src/app/main/dialog/dialog.component.ts
Expand Up @@ -65,14 +65,6 @@ export class DialogComponent {
this.dialogRef.close();
}

replaceTitle(heading: { title: string; subtitle?: string }) {
this.data.title = heading.title;

if (heading.subtitle) {
this.data.subtitle = heading.subtitle;
}
}

onKey(event: KeyboardEvent) {
this.comment = (event.target as HTMLInputElement).value;
}
Expand Down
@@ -1,4 +1,6 @@
import { AbstractControl, ValidatorFn } from '@angular/forms';
import { AbstractControl, AsyncValidatorFn, ValidationErrors, ValidatorFn } from '@angular/forms';
import { Observable, of } from 'rxjs';
import { first, map } from 'rxjs/operators';

/**
* validation of existing name values. Array method (list of values)
Expand All @@ -25,3 +27,25 @@ export function existingNamesValidator(valArrayRegexp: [RegExp], isCaseSensitive
return no ? { existingName: { name } } : null;
};
}

export function existingNamesAsyncValidator(
regexObservable: Observable<RegExp[]>,
isCaseSensitive = false
): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
if (!control.value) {
return of(null); // consider valid
}

const name = isCaseSensitive ? control.value : control.value.toLowerCase();

return regexObservable.pipe(
first(), // Take the first emission of the observable and complete
map(regexArray => {
// Check if the control value matches any of the regex patterns
const isInvalid = regexArray.some(regex => regex.test(name));
return isInvalid ? { existingName: { name } } : null;
})
);
};
}
Expand Up @@ -7,6 +7,7 @@ import { MatChipInputEvent } from '@angular/material/chips';
selector: 'app-chip-list-input',
template: `
<mat-form-field style="width: 100%">
<mat-label>{{ ('appLabels.form.project.general.keywords' | translate) + (chipsRequired ? '' : '*') }}</mat-label>
<mat-chip-grid #chipList>
<mat-chip-row
*ngFor="let tag of formArray.value; let index = index; trackBy: trackByFn"
Expand All @@ -16,7 +17,6 @@ import { MatChipInputEvent } from '@angular/material/chips';
</mat-chip-row>
<input
[placeholder]="('appLabels.form.project.general.keywords' | translate) + (chipsRequired ? '' : '*')"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeyCodes"
[matChipInputAddOnBlur]="true"
Expand Down
@@ -1,6 +1,7 @@
<!-- select user to add to the team -->

<!-- header toolbar -->

<div class="app-toolbar transparent more-space-bottom">
<div class="app-toolbar-row toolbar-subtitle">
<h3 class="mat-body subtitle">Increase your team</h3>
Expand Down Expand Up @@ -53,16 +54,9 @@ <h2 class="mat-headline-6">{{ 'appLabels.form.user.title.add2project' | translat
color="primary"
[disabled]="!selectUserForm.valid"
class="add-new create-user-btn"
(click)="openDialog('createUser')">
(click)="createUser()">
New user
</button>
<button
mat-button
[disabled]="!selectUserForm.valid"
class="add-new-mobile create-user-btn"
(click)="openDialog('createUser')">
<mat-icon>add_circle_outline</mat-icon>
</button>
</span>
</div>
</div>
Expand Down
Expand Up @@ -9,7 +9,7 @@ import {
Output,
} from '@angular/core';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { MatDialog } from '@angular/material/dialog';
import {
ApiResponseData,
ApiResponseError,
Expand All @@ -18,14 +18,14 @@ import {
UserResponse,
UsersResponse,
} from '@dasch-swiss/dsp-js';
import { DspApiConnectionToken } from '@dasch-swiss/vre/shared/app-config';
import { DspApiConnectionToken, DspDialogConfig } from '@dasch-swiss/vre/shared/app-config';
import { ProjectService } from '@dasch-swiss/vre/shared/app-helper-services';
import { ProjectsSelectors } from '@dasch-swiss/vre/shared/app-state';
import { Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { DialogComponent } from '../../../main/dialog/dialog.component';
import { existingNamesValidator } from '../../../main/directive/existing-name/existing-names.validator';
import { CreateUserPageComponent } from '../../../user/create-user-page/create-user-page.component';
import { AutocompleteItem } from '../../../workspace/search/operator';

@Component({
Expand Down Expand Up @@ -306,25 +306,9 @@ export class AddUserComponent implements OnInit {
);
}

openDialog(mode: string): void {
const dialogConfig: MatDialogConfig = {
width: '560px',
maxHeight: '80vh',
position: {
top: '112px',
},
data: {
project: this.projectUuid,
name: this.selectUserForm.controls['username'].value,
mode,
},
};

const dialogRef = this._dialog.open(DialogComponent, dialogConfig);
dialogRef.afterClosed().subscribe(() => {
// update the view
this.refreshParent.emit();
});
createUser() {
const dialogConfig = DspDialogConfig.dialogDrawerConfig<string>(this.projectUuid);
this._dialog.open(CreateUserPageComponent, dialogConfig);
}

resetInput(ev: Event) {
Expand Down
Expand Up @@ -5,8 +5,9 @@ import { FormControl } from '@angular/forms';
selector: 'app-common-input',
template: `
<mat-form-field style="width: 100%">
<mat-label>{{ placeholder }}</mat-label>
<mat-icon matIconPrefix *ngIf="prefixIcon">{{ prefixIcon }}</mat-icon>
<input matInput [placeholder]="placeholder" [formControl]="control" />
<input matInput [formControl]="control" />
<mat-error *ngIf="control.errors as errors">
{{ errors | humanReadableError: validatorErrors }}
</mat-error>
Expand Down
Expand Up @@ -60,7 +60,7 @@ export class ReusableProjectFormComponent implements OnInit {
form: ProjectForm;
readonly shortcodePatternError = {
errorKey: 'pattern',
message: 'This field must contains letters from A to F and 0 to 9',
message: 'This field must contain letters from A to F and 0 to 9',
};
readonly shortCodeExistsError = { errorKey: 'shortcodeExists', message: 'This shortcode already exists' };
readonly keywordsValidators = [Validators.minLength(3), Validators.maxLength(64)];
Expand Down
@@ -1,4 +1,4 @@
<div *ngIf="list && (((isUsersLoading$ | async) === false))">
<div *ngIf="list">
<!-- header toolbar -->
<div class="app-toolbar" *ngIf="list.length > 0">
<div class="app-toolbar-row">
Expand Down Expand Up @@ -26,11 +26,10 @@ <h2 class="mat-headline-6">
</h2>
<span class="fill-remaining-space"></span>
<span class="app-toolbar-action button right" *ngIf="status && createNew && (isSysAdmin$ | async)">
<button mat-flat-button [color]="'primary'" (click)="openDialog('createUser')">Create new</button>
<button mat-flat-button [color]="'primary'" (click)="createUser()">Create new</button>
</span>
</div>
</div>

<!-- content: list -->
<table class="table more-space-bottom" [class.deactivated]="!status">
<tr class="table-entry" *ngFor="let user of list; trackBy:trackByFn; let last = last" [class.no-border]="last">
Expand Down Expand Up @@ -102,7 +101,6 @@ <h5 class="mat-subtitle-1 info-names">
mat-menu-item
*ngIf="
!projectUuid &&
(user$ | async)?.status &&
(isSysAdmin$ | async) &&
user.username !== (username$ | async)
"
Expand All @@ -117,7 +115,7 @@ <h5 class="mat-subtitle-1 info-names">
<span>as system admin</span>
</button>
<!-- update user's profile data; only for system admin -->
<button mat-menu-item *ngIf="(isSysAdmin$ | async) && user.status" (click)="openDialog('editUser', user)">
<button mat-menu-item *ngIf="(isSysAdmin$ | async) && user.status" (click)="editUser(user)">
{{ projectUuid ? 'Edit member' : 'Edit user' }}
</button>
<button mat-menu-item *ngIf="(isSysAdmin$ | async) && user.status" (click)="openDialog('editPassword', user)">
Expand Down
@@ -1,17 +1,10 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
EventEmitter,
Input,
OnInit,
Output,
} from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { Constants, Permissions, ReadProject, ReadUser } from '@dasch-swiss/dsp-js';
import { Constants, ReadProject, ReadUser } from '@dasch-swiss/dsp-js';
import { PermissionsData } from '@dasch-swiss/dsp-js/src/models/admin/permissions-data';
import { UserApiService } from '@dasch-swiss/vre/shared/app-api';
import { RouteConstants } from '@dasch-swiss/vre/shared/app-config';
import { DspDialogConfig, RouteConstants } from '@dasch-swiss/vre/shared/app-config';
import { ProjectService, SortingService } from '@dasch-swiss/vre/shared/app-helper-services';
import {
LoadProjectMembersAction,
Expand All @@ -27,19 +20,27 @@ import { Observable, combineLatest } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators';
import { DialogComponent } from '../../../main/dialog/dialog.component';
import { DialogService } from '../../../main/services/dialog.service';
import { CreateUserPageComponent } from '../../../user/create-user-page/create-user-page.component';
import { EditUserPageComponent } from '../../../user/edit-user-page/edit-user-page.component';

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-users-list',
templateUrl: './users-list.component.html',
styleUrls: ['./users-list.component.scss'],
})
export class UsersListComponent implements OnInit {
export class UsersListComponent {
// list of users: status active or inactive (deleted)
@Input() status: boolean;

// list of users: depending on the parent
@Input() list: ReadUser[];
_list: ReadUser[];
get list(): ReadUser[] {
return this._list;
}
@Input() set list(value: ReadUser[]) {
this._list = this._sortingService.keySortByAlphabetical(value, this.sortBy as keyof ReadUser);
}

// enable the button to create new user
@Input() createNew = false;
Expand Down Expand Up @@ -94,7 +95,7 @@ export class UsersListComponent implements OnInit {
];

// ... and sort by 'username'
sortBy = 'username';
sortBy = localStorage.getItem('sortUsersBy') || 'username';

disableMenu$: Observable<boolean> = combineLatest([
this._store.select(ProjectsSelectors.isCurrentProjectAdminOrSysAdmin),
Expand All @@ -111,9 +112,7 @@ export class UsersListComponent implements OnInit {
);

@Select(UserSelectors.isSysAdmin) isSysAdmin$: Observable<boolean>;
@Select(UserSelectors.user) user$: Observable<ReadUser>;
@Select(UserSelectors.username) username$: Observable<string>;
@Select(UserSelectors.userProjectAdminGroups) userProjectAdminGroups$: Observable<string[]>;
@Select(ProjectsSelectors.isCurrentProjectAdminOrSysAdmin) isCurrentProjectAdminOrSysAdmin$: Observable<boolean>;
@Select(ProjectsSelectors.currentProject) project$: Observable<ReadProject>;
@Select(UserSelectors.isLoading) isUsersLoading$: Observable<boolean>;
Expand All @@ -135,15 +134,6 @@ export class UsersListComponent implements OnInit {
});
}

ngOnInit() {
// sort list by defined key
if (localStorage.getItem('sortUsersBy')) {
this.sortBy = localStorage.getItem('sortUsersBy');
} else {
localStorage.setItem('sortUsersBy', this.sortBy);
}
}

trackByFn = (index: number, item: ReadUser) => `${index}-${item.id}`;

/**
Expand All @@ -155,7 +145,7 @@ export class UsersListComponent implements OnInit {
* @param [permissions] user's permissions
* @returns boolean
*/
userIsProjectAdmin(permissions?: Permissions): boolean {
userIsProjectAdmin(permissions?: PermissionsData): boolean {
if (!this.project) {
return false;
}
Expand All @@ -168,7 +158,7 @@ export class UsersListComponent implements OnInit {
*
* @param permissions PermissionData from user profile
*/
userIsSystemAdmin(permissions: Permissions): boolean {
userIsSystemAdmin(permissions: PermissionsData): boolean {
let admin = false;
const groupsPerProjectKeys: string[] = Object.keys(permissions.groupsPerProject);

Expand Down Expand Up @@ -230,7 +220,7 @@ export class UsersListComponent implements OnInit {
/**
* update user's admin-group membership
*/
updateProjectAdminMembership(id: string, permissions: Permissions): void {
updateProjectAdminMembership(id: string, permissions: PermissionsData): void {
const currentUser = this._store.selectSnapshot(UserSelectors.user);
const userIsProjectAdmin = this.userIsProjectAdmin(permissions);
if (userIsProjectAdmin) {
Expand Down Expand Up @@ -325,6 +315,21 @@ export class UsersListComponent implements OnInit {
.subscribe();
}

createUser() {
const dialogRef = this._matDialog.open(CreateUserPageComponent, DspDialogConfig.dialogDrawerConfig());
dialogRef.afterClosed().subscribe(() => {
this.refreshParent.emit();
});
}

editUser(user: ReadUser) {
const dialogConfig = DspDialogConfig.dialogDrawerConfig<ReadUser>(user);
const dialogRef = this._matDialog.open(EditUserPageComponent, dialogConfig);
dialogRef.afterClosed().subscribe(() => {
this.refreshParent.emit();
});
}

/**
* open dialog in every case of modification:
* edit user profile data, update user's password,
Expand Down

0 comments on commit ca7d85a

Please sign in to comment.