From 9a4f19b200c3f6199717492bb39d84c6527ec1b2 Mon Sep 17 00:00:00 2001 From: Florent POITTEVIN <poittevin.florent@gmail.com> Date: Wed, 20 May 2020 08:59:53 +0200 Subject: [PATCH] feat: 1381 Notification action join OrgUnit --- src/app/app.component.ts | 4 +- .../components/dialogs/user/user.dialog.ts | 8 ++- .../avatar/avatar.presentational.ts | 8 +-- .../abstract-main-toolbar.presentational.ts | 4 +- .../main-toolbar.presentational.ts | 4 +- .../presentationals/user-form/user-form.ts | 8 +-- .../admin-orgunit-create.routable.ts | 3 +- .../admin-user-delete.dialog.ts | 4 +- .../admin-user-form.presentational.ts | 7 +-- .../admin-user-create.routable.ts | 10 +-- .../admin-user-detail-edit.routable.ts | 24 ++++---- .../admin-user-list.routable.ts | 16 ++--- .../admin/user/stores/admin-user.action.ts | 30 ++++----- .../admin/user/stores/admin-user.state.ts | 8 +-- ...space-notification-detail-edit.routable.ts | 61 ++++++++++++++----- .../helper/notification.helper.ts | 2 + .../orgunit-request-access.dialog.html | 13 ++++ .../orgunit-request-access.dialog.ts | 8 +++ .../orgunit-form.presentational.html | 1 + .../orgunit-form.presentational.ts | 33 ++++++++++ .../orgunit-detail-edit.routable.html | 1 + src/app/models/index.ts | 5 +- ...ed-person-orgunit-role.presentational.html | 4 +- ...ed-person-orgunit-role.presentational.scss | 4 ++ ...ared-person-orgunit-role.presentational.ts | 5 +- ...ed-abstract-detail-edit-common.routable.ts | 3 +- .../shared-abstract-detail-edit.routable.ts | 12 +++- .../shared/helpers/url-query-param.helper.ts | 18 ++++++ .../models/business/user-extended.model.ts | 10 --- src/app/stores/app.state.ts | 4 +- .../app-system-property.state.ts | 6 +- src/app/stores/user/app-user.action.ts | 8 ++- src/app/stores/user/app-user.state.ts | 12 ++-- src/assets/i18n/de.json | 15 ++++- src/assets/i18n/en.json | 15 ++++- src/assets/i18n/fr.json | 17 +++++- 36 files changed, 281 insertions(+), 114 deletions(-) delete mode 100644 src/app/shared/models/business/user-extended.model.ts diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5f23cbb94..0d1ad42c5 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -26,6 +26,7 @@ import {AppAction} from "@app/stores/app.action"; import {AppCartArchiveState} from "@app/stores/cart/archive/app-cart-archive.state"; import {environment} from "@environments/environment"; import {HomeHelper} from "@home/helpers/home.helper"; +import {User} from "@models"; import {TranslateService} from "@ngx-translate/core"; import {Navigate} from "@ngxs/router-plugin"; import { @@ -37,7 +38,6 @@ import {ChemicalMoleculeVisualizationEnum} from "@shared/enums/chemical-molecule import {SessionStorageEnum} from "@shared/enums/session-storage.enum"; import {SessionStorageHelper} from "@shared/helpers/session-storage.helper"; import {UrlQueryParamHelper} from "@shared/helpers/url-query-param.helper"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import {AppStatusService} from "@shared/services/app-status.service"; import {BreakpointService} from "@shared/services/breakpoint.service"; import {PermissionUtil} from "@shared/utils/permission.util"; @@ -84,7 +84,7 @@ export class AppComponent extends SharedAbstractPresentational { @Select((state: LocalStateModel) => state.application.isApplicationInitialized) isApplicationInitializedObs: Observable<boolean>; @Select((state: LocalStateModel) => state.application.theme) themeObs: Observable<ThemeEnum>; @Select((state: LocalStateModel) => state.application.userRoles) userRolesObs: Observable<ApplicationRoleEnum[]>; - @Select((state: LocalStateModel) => state.application.application_user.current) currentUserObs: Observable<UserExtended>; + @Select((state: LocalStateModel) => state.application.application_user.current) currentUserObs: Observable<User>; numberArchiveInCartObs: Observable<number> = ResourceState.total(this.store, AppCartArchiveState); diff --git a/src/app/components/dialogs/user/user.dialog.ts b/src/app/components/dialogs/user/user.dialog.ts index 648351307..b86c31b88 100644 --- a/src/app/components/dialogs/user/user.dialog.ts +++ b/src/app/components/dialogs/user/user.dialog.ts @@ -11,14 +11,16 @@ import { MatDialogRef, } from "@angular/material/dialog"; import {AppPersonAction} from "@app/stores/person/app-person.action"; -import {Person} from "@models"; +import { + Person, + User, +} from "@models"; import { Select, Store, } from "@ngxs/store"; import {SharedAbstractContainer} from "@shared/components/containers/shared-abstract/shared-abstract.container"; import {SharedPersonFormPresentational} from "@shared/components/presentationals/shared-person-form/shared-person-form.presentational"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import {LocalStateModel} from "@shared/models/local-state.model"; import {Observable} from "rxjs"; import { @@ -41,7 +43,7 @@ export class UserDialog extends SharedAbstractContainer implements OnInit { constructor(protected store: Store, protected dialogRef: MatDialogRef<UserDialog>, - @Inject(MAT_DIALOG_DATA) public user: UserExtended) { + @Inject(MAT_DIALOG_DATA) public user: User) { super(); } diff --git a/src/app/components/presentationals/avatar/avatar.presentational.ts b/src/app/components/presentationals/avatar/avatar.presentational.ts index 1827c18ec..aab54675b 100644 --- a/src/app/components/presentationals/avatar/avatar.presentational.ts +++ b/src/app/components/presentationals/avatar/avatar.presentational.ts @@ -9,8 +9,8 @@ import {TokenDialog} from "@app/components/dialogs/token/token.dialog"; import {UserDialog} from "@app/components/dialogs/user/user.dialog"; import {MenuToolbar} from "@app/components/presentationals/main-toolbar/abstract-main-toolbar/abstract-main-toolbar.presentational"; import {SharedAbstractPresentational} from "@app/shared/components/presentationals/shared-abstract/shared-abstract.presentational"; +import {User} from "@models"; import {ThemeEnum} from "@shared/enums/theme.enum"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import { BehaviorSubject, Observable, @@ -27,7 +27,7 @@ import { changeDetection: ChangeDetectionStrategy.OnPush, }) export class AvatarPresentational extends SharedAbstractPresentational { - private _user: UserExtended; + private _user: User; @Input() mode: "horizontal" | "vertical" = "vertical"; @@ -50,7 +50,7 @@ export class AvatarPresentational extends SharedAbstractPresentational { } @Input() - set user(value: UserExtended) { + set user(value: User) { this._user = value; this.computeInitial(); } @@ -58,7 +58,7 @@ export class AvatarPresentational extends SharedAbstractPresentational { @Input() listMenuAdmin: MenuToolbar[]; - get user(): UserExtended { + get user(): User { return this._user; } diff --git a/src/app/components/presentationals/main-toolbar/abstract-main-toolbar/abstract-main-toolbar.presentational.ts b/src/app/components/presentationals/main-toolbar/abstract-main-toolbar/abstract-main-toolbar.presentational.ts index 1e9a6d90a..120606ea4 100644 --- a/src/app/components/presentationals/main-toolbar/abstract-main-toolbar/abstract-main-toolbar.presentational.ts +++ b/src/app/components/presentationals/main-toolbar/abstract-main-toolbar/abstract-main-toolbar.presentational.ts @@ -16,8 +16,8 @@ import { import {ThemeEnum} from "@app/shared/enums/theme.enum"; import {PermissionUtil} from "@app/shared/utils/permission.util"; import {environment} from "@environments/environment"; +import {User} from "@models"; import {IconNameEnum} from "@shared/enums/icon-name.enum"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import { BehaviorSubject, Observable, @@ -40,7 +40,7 @@ export abstract class AbstractMainToolbarPresentational extends SharedAbstractPr currentLanguage: LanguagesEnum; @Input() - user: UserExtended; + user: User; institutionUrl: string = environment.institutionUrl; diff --git a/src/app/components/presentationals/main-toolbar/main-toolbar/main-toolbar.presentational.ts b/src/app/components/presentationals/main-toolbar/main-toolbar/main-toolbar.presentational.ts index b4ca8637f..d327a68a4 100644 --- a/src/app/components/presentationals/main-toolbar/main-toolbar/main-toolbar.presentational.ts +++ b/src/app/components/presentationals/main-toolbar/main-toolbar/main-toolbar.presentational.ts @@ -4,11 +4,11 @@ import { Input, Output, } from "@angular/core"; +import {User} from "@models"; import {SharedAbstractPresentational} from "@shared/components/presentationals/shared-abstract/shared-abstract.presentational"; import {ApplicationRoleEnum} from "@shared/enums/application-role.enum"; import {LanguagesEnum} from "@shared/enums/languages.enum"; import {ThemeEnum} from "@shared/enums/theme.enum"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import { BehaviorSubject, Observable, @@ -35,7 +35,7 @@ export class MainToolbarPresentational extends SharedAbstractPresentational { userRoles: ApplicationRoleEnum[]; @Input() - user: UserExtended; + user: User; @Input() numberArchiveInCart: number; diff --git a/src/app/components/presentationals/user-form/user-form.ts b/src/app/components/presentationals/user-form/user-form.ts index fa4bf859c..740f9f372 100644 --- a/src/app/components/presentationals/user-form/user-form.ts +++ b/src/app/components/presentationals/user-form/user-form.ts @@ -16,8 +16,8 @@ import {BaseFormDefinition} from "@app/shared/models/base-form-definition.model" import { AccessOrganizationalUnit, ApplicationRole, + User, } from "@models"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import { EnumUtil, isNullOrUndefined, @@ -32,7 +32,7 @@ import { styleUrls: ["../../../shared/components/presentationals/shared-abstract-form/shared-abstract-form.presentational.scss"], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class UserForm extends SharedAbstractFormPresentational<UserExtended> { +export class UserForm extends SharedAbstractFormPresentational<User> { formDefinition: FormComponentFormDefinition = new FormComponentFormDefinition(); applicationRolesNames: string[] = EnumUtil.convertToArray(UserApplicationRoleEnum, [UserApplicationRoleEnum.trusted_client]); @@ -45,7 +45,7 @@ export class UserForm extends SharedAbstractFormPresentational<UserExtended> { super(_changeDetectorRef, _elementRef); } - protected bindFormTo(user: UserExtended): void { + protected bindFormTo(user: User): void { this.form = this._fb.group({ [this.formDefinition.externalUid]: [user.externalUid, [Validators.required, SolidifyValidator]], [this.formDefinition.targetedUid]: [user.targetedUid], @@ -75,7 +75,7 @@ export class UserForm extends SharedAbstractFormPresentational<UserExtended> { }); } - protected treatmentBeforeSubmit(user: UserExtended): UserExtended { + protected treatmentBeforeSubmit(user: User): User { return user; } diff --git a/src/app/features/admin/orgunit/components/routables/admin-orgunit-create/admin-orgunit-create.routable.ts b/src/app/features/admin/orgunit/components/routables/admin-orgunit-create/admin-orgunit-create.routable.ts index ae1acddaf..8730b81bc 100644 --- a/src/app/features/admin/orgunit/components/routables/admin-orgunit-create/admin-orgunit-create.routable.ts +++ b/src/app/features/admin/orgunit/components/routables/admin-orgunit-create/admin-orgunit-create.routable.ts @@ -33,6 +33,7 @@ import {RoleEnum} from "@shared/enums/role.enum"; import {LocalStateModel} from "@shared/models/local-state.model"; import {SharedResearchDomainAction} from "@shared/stores/research-domain/shared-research-domain.action"; import {SharedResearchDomainState} from "@shared/stores/research-domain/shared-research-domain.state"; +import {SharedRoleState} from "@shared/stores/role/shared-role.state"; import {SharedSubmissionPolicyState} from "@shared/stores/submission-policy/shared-submission-policy.state"; import {Observable} from "rxjs"; import { @@ -52,7 +53,7 @@ export class AdminOrgunitCreateRoutable extends SharedAbstractCreateRoutable<Org @Select(AdminOrganizationalUnitState.isReadyToBeDisplayedInCreateMode) isReadyToBeDisplayedInCreateModeObs: Observable<boolean>; @Select((state: LocalStateModel) => state.shared.shared_preservationPolicy.list) listPreservationPoliciesObs: Observable<PreservationPolicy[]>; @Select((state: LocalStateModel) => state.shared.shared_disseminationPolicy.list) listDisseminationPoliciesObs: Observable<DisseminationPolicy[]>; - @Select((state: LocalStateModel) => state.shared.shared_role.list) listRoleObs: Observable<Role[]>; + listRoleObs: Observable<Role[]> = ResourceState.list(this._store, SharedRoleState); @Select((state: LocalStateModel) => state.admin.admin_organizationalUnit.admin_organizationalUnit_fundingAgency.selected) selectedFundingAgenciesObs: Observable<FundingAgency[]>; @Select((state: LocalStateModel) => state.admin.admin_organizationalUnit.admin_organizationalUnit_institution.selected) selectedInstitutionsObs: Observable<Institution[]>; researchDomainSourcesObs: Observable<string[]> = MemoizedUtil.select(this._store, SharedResearchDomainState, state => state.sources); diff --git a/src/app/features/admin/user/components/dialogs/admin-user-delete/admin-user-delete.dialog.ts b/src/app/features/admin/user/components/dialogs/admin-user-delete/admin-user-delete.dialog.ts index 214478f34..3f84ed44d 100644 --- a/src/app/features/admin/user/components/dialogs/admin-user-delete/admin-user-delete.dialog.ts +++ b/src/app/features/admin/user/components/dialogs/admin-user-delete/admin-user-delete.dialog.ts @@ -9,10 +9,10 @@ import { MAT_DIALOG_DATA, MatDialogRef, } from "@angular/material/dialog"; +import {User} from "@models"; import {Store} from "@ngxs/store"; import {SharedAbstractDeleteDialog} from "@shared/components/dialogs/shared-abstract-delete/shared-abstract-delete.dialog"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import {DeleteDialog} from "@shared/models/delete-dialog.model"; import {MARK_AS_TRANSLATABLE} from "solidify-frontend"; @@ -22,7 +22,7 @@ import {MARK_AS_TRANSLATABLE} from "solidify-frontend"; styleUrls: ["../../../../../../shared/components/dialogs/shared-abstract-delete/shared-abstract-delete.dialog.scss"], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AdminUserDeleteDialog extends SharedAbstractDeleteDialog<UserExtended, AdminUserStateModel> { +export class AdminUserDeleteDialog extends SharedAbstractDeleteDialog<User, AdminUserStateModel> { public readonly KEY_TITLE: string = MARK_AS_TRANSLATABLE("admin.user.dialog.delete.title"); public readonly KEY_MESSAGE: string = MARK_AS_TRANSLATABLE("admin.user.dialog.delete.message"); public readonly KEY_CONFIRM_BUTTON: string = MARK_AS_TRANSLATABLE("admin.user.dialog.delete.confirm"); diff --git a/src/app/features/admin/user/components/presentationals/admin-user-form/admin-user-form.presentational.ts b/src/app/features/admin/user/components/presentationals/admin-user-form/admin-user-form.presentational.ts index d6666519a..d2dc442f9 100644 --- a/src/app/features/admin/user/components/presentationals/admin-user-form/admin-user-form.presentational.ts +++ b/src/app/features/admin/user/components/presentationals/admin-user-form/admin-user-form.presentational.ts @@ -20,7 +20,6 @@ import {SharedAbstractFormPresentational} from "@shared/components/presentationa import {RoutesEnum} from "@shared/enums/routes.enum"; import {UserApplicationRoleEnum} from "@shared/enums/user-application-role.enum"; import {BaseFormDefinition} from "@shared/models/base-form-definition.model"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import { isEmptyString, isNullOrUndefined, @@ -34,7 +33,7 @@ import { styleUrls: ["./admin-user-form.presentational.scss"], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AdminUserFormPresentational extends SharedAbstractFormPresentational<UserExtended> { +export class AdminUserFormPresentational extends SharedAbstractFormPresentational<User> { formDefinition: FormComponentFormDefinition = new FormComponentFormDefinition(); applicationRolesNames: string[] = [...Object.values(UserApplicationRoleEnum)]; @@ -54,7 +53,7 @@ export class AdminUserFormPresentational extends SharedAbstractFormPresentationa super(_changeDetectorRef, _elementRef); } - protected bindFormTo(user: UserExtended): void { + protected bindFormTo(user: User): void { this.form = this._fb.group({ [this.formDefinition.externalUid]: [user.externalUid, [Validators.required, SolidifyValidator]], [this.formDefinition.targetedUid]: [user.targetedUid, [Validators.required, SolidifyValidator]], @@ -87,7 +86,7 @@ export class AdminUserFormPresentational extends SharedAbstractFormPresentationa protected disableSpecificField(): void { } - protected treatmentBeforeSubmit(user: UserExtended): UserExtended { + protected treatmentBeforeSubmit(user: User): User { const userRoles = this.form.controls.applicationRoles.value.controls as FormArray[]; const selectedArray: string [] = []; userRoles.map((formCtrl) => { diff --git a/src/app/features/admin/user/components/routables/admin-user-create/admin-user-create.routable.ts b/src/app/features/admin/user/components/routables/admin-user-create/admin-user-create.routable.ts index ff207a23b..563b8ae03 100644 --- a/src/app/features/admin/user/components/routables/admin-user-create/admin-user-create.routable.ts +++ b/src/app/features/admin/user/components/routables/admin-user-create/admin-user-create.routable.ts @@ -10,7 +10,10 @@ import { Component, ViewChild, } from "@angular/core"; -import {AccessOrganizationalUnit} from "@models"; +import { + AccessOrganizationalUnit, + User, +} from "@models"; import { Actions, Select, @@ -19,7 +22,6 @@ import { import {SharedAbstractFormPresentational} from "@shared/components/presentationals/shared-abstract-form/shared-abstract-form.presentational"; import {SharedAbstractCreateRoutable} from "@shared/components/routables/shared-abstract-create/shared-abstract-create.routable"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import {LocalStateModel} from "@shared/models/local-state.model"; import {Observable} from "rxjs"; @@ -29,14 +31,14 @@ import {Observable} from "rxjs"; styleUrls: ["../../../../../../shared/components/routables/shared-abstract-create/shared-abstract-create.routable.scss"], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AdminUserCreateRoutable extends SharedAbstractCreateRoutable<UserExtended, AdminUserStateModel> { +export class AdminUserCreateRoutable extends SharedAbstractCreateRoutable<User, AdminUserStateModel> { @Select(AdminUserState.isLoadingWithDependency) isLoadingWithDependencyObs: Observable<boolean>; @Select(AdminUserState.isReadyToBeDisplayedInCreateMode) isReadyToBeDisplayedInCreateModeObs: Observable<boolean>; @Select((state: LocalStateModel) => state.shared.shared_person.list) listPersonObs: Observable<PersonExtended[]>; @Select((state: LocalStateModel) => state.shared.shared_organizationalUnit.list) listOrgUnitObs: Observable<AccessOrganizationalUnit[]>; @ViewChild("formPresentational") - readonly formPresentational: SharedAbstractFormPresentational<UserExtended>; + readonly formPresentational: SharedAbstractFormPresentational<User>; constructor(protected readonly _store: Store, protected readonly _actions$: Actions, diff --git a/src/app/features/admin/user/components/routables/admin-user-detail-edit/admin-user-detail-edit.routable.ts b/src/app/features/admin/user/components/routables/admin-user-detail-edit/admin-user-detail-edit.routable.ts index 0e86a8268..9f3c28120 100644 --- a/src/app/features/admin/user/components/routables/admin-user-detail-edit/admin-user-detail-edit.routable.ts +++ b/src/app/features/admin/user/components/routables/admin-user-detail-edit/admin-user-detail-edit.routable.ts @@ -20,7 +20,10 @@ import {MatDialog} from "@angular/material/dialog"; import {ActivatedRoute} from "@angular/router"; import {AppAction} from "@app/stores/app.action"; import {AppState} from "@app/stores/app.state"; -import {AccessOrganizationalUnit} from "@models"; +import { + AccessOrganizationalUnit, + User, +} from "@models"; import {Navigate} from "@ngxs/router-plugin"; import { Actions, @@ -30,7 +33,6 @@ import { } from "@ngxs/store"; import {SharedAbstractDetailEditCommonRoutable} from "@shared/components/routables/shared-abstract-detail-edit-common/shared-abstract-detail-edit-common.routable"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import {LocalStateModel} from "@shared/models/local-state.model"; import { Observable, @@ -52,14 +54,14 @@ import { styleUrls: ["./admin-user-detail-edit.routable.scss"], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AdminUserDetailEditRoutable extends SharedAbstractDetailEditCommonRoutable<UserExtended, AdminUserStateModel> { +export class AdminUserDetailEditRoutable extends SharedAbstractDetailEditCommonRoutable<User, AdminUserStateModel> { @Select(AdminUserState.isLoadingWithDependency) isLoadingWithDependencyObs: Observable<boolean>; @Select(AdminUserState.isReadyToBeDisplayed) isReadyToBeDisplayedObs: Observable<boolean>; @Select((state: LocalStateModel) => state.shared.shared_person.list) listPersonObs: Observable<PersonExtended[]>; @Select((state: LocalStateModel) => state.shared.shared_organizationalUnit.list) listOrgUnitObs: Observable<AccessOrganizationalUnit[]>; - @Select(AppState.currentUser) currentUser: Observable<UserExtended>; + @Select(AppState.currentUser) currentUser: Observable<User>; - readonly KEY_PARAM_NAME: keyof UserExtended & string = "externalUid"; + readonly KEY_PARAM_NAME: keyof User & string = "externalUid"; constructor(protected _store: Store, protected _route: ActivatedRoute, @@ -72,7 +74,7 @@ export class AdminUserDetailEditRoutable extends SharedAbstractDetailEditCommonR getSubResourceWithParentId(id: string): void { } - update(model: ModelFormControlEvent<UserExtended>): Observable<any> { + update(model: ModelFormControlEvent<User>): Observable<any> { this.subscribe(this.currentUser.pipe( take(1), tap(currentUser => { @@ -102,7 +104,7 @@ export class AdminUserDetailEditRoutable extends SharedAbstractDetailEditCommonR return of(); } - private openModalConfirmEditRole(data: AdminUserEditRoleCurrentUserDialogData, model: ModelFormControlEvent<UserExtended>): void { + private openModalConfirmEditRole(data: AdminUserEditRoleCurrentUserDialogData, model: ModelFormControlEvent<User>): void { this.subscribe(this._dialog.open(AdminUserEditRoleCurrentUserDialog, { data: data, }).afterClosed().pipe( @@ -117,7 +119,7 @@ export class AdminUserDetailEditRoutable extends SharedAbstractDetailEditCommonR )); } - private updateAndLogout(model: ModelFormControlEvent<UserExtended>): void { + private updateAndLogout(model: ModelFormControlEvent<User>): void { super.update(model); this.subscribe(this._actions$.pipe( ofActionCompleted(AdminUserAction.UpdateSuccess), @@ -129,11 +131,11 @@ export class AdminUserDetailEditRoutable extends SharedAbstractDetailEditCommonR )); } - isCurrentUser(userExtended: UserExtended): boolean { - if (isNullOrUndefined(userExtended)) { + isCurrentUser(user: User): boolean { + if (isNullOrUndefined(user)) { return false; } - return userExtended.resId === this._resId; + return user.resId === this._resId; } navigate($event: string[]): void { diff --git a/src/app/features/admin/user/components/routables/admin-user-list/admin-user-list.routable.ts b/src/app/features/admin/user/components/routables/admin-user-list/admin-user-list.routable.ts index b1add781e..5f8e950f3 100644 --- a/src/app/features/admin/user/components/routables/admin-user-list/admin-user-list.routable.ts +++ b/src/app/features/admin/user/components/routables/admin-user-list/admin-user-list.routable.ts @@ -8,6 +8,8 @@ import { } from "@angular/core"; import {MatDialog} from "@angular/material/dialog"; import {ActivatedRoute} from "@angular/router"; +import {AppState} from "@app/stores/app.state"; +import {User} from "@models"; import { Actions, Store, @@ -15,13 +17,11 @@ import { import {SharedAbstractListRoutable} from "@shared/components/routables/shared-abstract-list/shared-abstract-list.routable"; import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import {RouterExtService} from "@shared/services/router-ext.service"; import { - OrderEnum, MARK_AS_TRANSLATABLE, + OrderEnum, } from "solidify-frontend"; -import {AppState} from "@app/stores/app.state"; @Component({ selector: "dlcm-admin-user-list-routable", @@ -29,11 +29,11 @@ import {AppState} from "@app/stores/app.state"; styleUrls: ["../../../../../../shared/components/routables/shared-abstract-list/shared-abstract-list.routable.scss"], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AdminUserListRoutable extends SharedAbstractListRoutable<UserExtended, AdminUserStateModel> implements OnInit { +export class AdminUserListRoutable extends SharedAbstractListRoutable<User, AdminUserStateModel> implements OnInit { readonly KEY_CREATE_BUTTON: string = MARK_AS_TRANSLATABLE("admin.user.button.new"); readonly KEY_REFRESH_BUTTON: string = MARK_AS_TRANSLATABLE("admin.user.button.refresh"); readonly KEY_BACK_BUTTON: string | undefined = MARK_AS_TRANSLATABLE("admin.button.goBackToAdminHome"); - readonly KEY_PARAM_NAME: keyof UserExtended & string = "externalUid"; + readonly KEY_PARAM_NAME: keyof User & string = "externalUid"; constructor(protected readonly _store: Store, protected readonly _changeDetector: ChangeDetectorRef, @@ -46,15 +46,15 @@ export class AdminUserListRoutable extends SharedAbstractListRoutable<UserExtend ngOnInit(): void { super.ngOnInit(); - const currentUser: UserExtended = this._store.selectSnapshot(AppState.currentUser); + const currentUser: User = this._store.selectSnapshot(AppState.currentUser); this.listNewId = [currentUser.resId]; } - conditionDisplayEditButton(model: UserExtended | undefined): boolean { + conditionDisplayEditButton(model: User | undefined): boolean { return true; } - conditionDisplayDeleteButton(model: UserExtended | undefined): boolean { + conditionDisplayDeleteButton(model: User | undefined): boolean { return true; } diff --git a/src/app/features/admin/user/stores/admin-user.action.ts b/src/app/features/admin/user/stores/admin-user.action.ts index 41c95bdd2..169a0b006 100644 --- a/src/app/features/admin/user/stores/admin-user.action.ts +++ b/src/app/features/admin/user/stores/admin-user.action.ts @@ -1,5 +1,5 @@ +import {User} from "@models"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import { ResourceAction, ResourceNameSpace, @@ -30,11 +30,11 @@ export namespace AdminUserAction { } @TypeDefaultAction(state) - export class GetAllSuccess extends ResourceAction.GetAllSuccess<UserExtended> { + export class GetAllSuccess extends ResourceAction.GetAllSuccess<User> { } @TypeDefaultAction(state) - export class GetAllFail extends ResourceAction.GetAllFail<UserExtended> { + export class GetAllFail extends ResourceAction.GetAllFail<User> { } @TypeDefaultAction(state) @@ -54,35 +54,35 @@ export namespace AdminUserAction { } @TypeDefaultAction(state) - export class GetByIdSuccess extends ResourceAction.GetByIdSuccess<UserExtended> { + export class GetByIdSuccess extends ResourceAction.GetByIdSuccess<User> { } @TypeDefaultAction(state) - export class GetByIdFail extends ResourceAction.GetByIdFail<UserExtended> { + export class GetByIdFail extends ResourceAction.GetByIdFail<User> { } @TypeDefaultAction(state) - export class Create extends ResourceAction.Create<UserExtended> { + export class Create extends ResourceAction.Create<User> { } @TypeDefaultAction(state) - export class CreateSuccess extends ResourceAction.CreateSuccess<UserExtended> { + export class CreateSuccess extends ResourceAction.CreateSuccess<User> { } @TypeDefaultAction(state) - export class CreateFail extends ResourceAction.CreateFail<UserExtended> { + export class CreateFail extends ResourceAction.CreateFail<User> { } @TypeDefaultAction(state) - export class Update extends ResourceAction.Update<UserExtended> { + export class Update extends ResourceAction.Update<User> { } @TypeDefaultAction(state) - export class UpdateSuccess extends ResourceAction.UpdateSuccess<UserExtended> { + export class UpdateSuccess extends ResourceAction.UpdateSuccess<User> { } @TypeDefaultAction(state) - export class UpdateFail extends ResourceAction.UpdateFail<UserExtended> { + export class UpdateFail extends ResourceAction.UpdateFail<User> { } @TypeDefaultAction(state) @@ -98,7 +98,7 @@ export namespace AdminUserAction { } @TypeDefaultAction(state) - export class AddInList extends ResourceAction.AddInList<UserExtended> { + export class AddInList extends ResourceAction.AddInList<User> { } @TypeDefaultAction(state) @@ -106,11 +106,11 @@ export namespace AdminUserAction { } @TypeDefaultAction(state) - export class AddInListByIdSuccess extends ResourceAction.AddInListByIdSuccess<UserExtended> { + export class AddInListByIdSuccess extends ResourceAction.AddInListByIdSuccess<User> { } @TypeDefaultAction(state) - export class AddInListByIdFail extends ResourceAction.AddInListByIdFail<UserExtended> { + export class AddInListByIdFail extends ResourceAction.AddInListByIdFail<User> { } @TypeDefaultAction(state) @@ -126,7 +126,7 @@ export namespace AdminUserAction { } @TypeDefaultAction(state) - export class LoadNextChunkListSuccess extends ResourceAction.LoadNextChunkListSuccess<UserExtended> { + export class LoadNextChunkListSuccess extends ResourceAction.LoadNextChunkListSuccess<User> { } @TypeDefaultAction(state) diff --git a/src/app/features/admin/user/stores/admin-user.state.ts b/src/app/features/admin/user/stores/admin-user.state.ts index f611dbe52..4d5d5a12f 100644 --- a/src/app/features/admin/user/stores/admin-user.state.ts +++ b/src/app/features/admin/user/stores/admin-user.state.ts @@ -3,6 +3,7 @@ import { adminUserActionNameSpace, } from "@admin/user/stores/admin-user.action"; import {Injectable} from "@angular/core"; +import {User} from "@models"; import { Action, Actions, @@ -15,21 +16,20 @@ import { import {AdminResourceApiEnum} from "@shared/enums/api.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; import {RoutesEnum} from "@shared/enums/routes.enum"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import {SharedPersonAction} from "@shared/stores/person/shared-person.action"; import { ApiService, defaultResourceStateInitValue, isNullOrUndefined, + MARK_AS_TRANSLATABLE, NotificationService, ResourceState, ResourceStateModel, StoreUtil, - MARK_AS_TRANSLATABLE, urlSeparator, } from "solidify-frontend"; -export interface AdminUserStateModel extends ResourceStateModel<UserExtended> { +export interface AdminUserStateModel extends ResourceStateModel<User> { } @Injectable() @@ -40,7 +40,7 @@ export interface AdminUserStateModel extends ResourceStateModel<UserExtended> { }, }) -export class AdminUserState extends ResourceState<AdminUserStateModel, UserExtended> { +export class AdminUserState extends ResourceState<AdminUserStateModel, User> { constructor(protected apiService: ApiService, protected store: Store, protected notificationService: NotificationService, diff --git a/src/app/features/preservation-space/notification/components/routables/preservation-space-notification-detail-edit/preservation-space-notification-detail-edit.routable.ts b/src/app/features/preservation-space/notification/components/routables/preservation-space-notification-detail-edit/preservation-space-notification-detail-edit.routable.ts index 04c557e19..9d2af2bff 100644 --- a/src/app/features/preservation-space/notification/components/routables/preservation-space-notification-detail-edit/preservation-space-notification-detail-edit.routable.ts +++ b/src/app/features/preservation-space/notification/components/routables/preservation-space-notification-detail-edit/preservation-space-notification-detail-edit.routable.ts @@ -6,16 +6,19 @@ import { } from "@angular/core"; import {MatDialog} from "@angular/material/dialog"; import {ActivatedRoute} from "@angular/router"; -import {environment} from "@environments/environment"; import {NotificationModeEnum} from "@app/features/preservation-space/notification/enums/notification-mode.enum"; import {NotificationHelper} from "@app/features/preservation-space/notification/helper/notification.helper"; import { PreservationSpaceNotificationState, PreservationSpaceNotificationStateModel, } from "@app/features/preservation-space/notification/stores/preservation-space-notification.state"; +import {PreservationSpaceNotificationStatusHistoryAction} from "@app/features/preservation-space/notification/stores/status-history/preservation-space-notification-status-history.action"; +import {PreservationSpaceNotificationStatusHistoryState} from "@app/features/preservation-space/notification/stores/status-history/preservation-space-notification-status-history.state"; +import {environment} from "@environments/environment"; import { NotificationDlcm, NotificationStatus, + NotificationType, } from "@models"; import {Navigate} from "@ngxs/router-plugin"; import { @@ -23,34 +26,34 @@ import { Select, Store, } from "@ngxs/store"; +import {SharedHistoryDialog} from "@shared/components/dialogs/shared-history/shared-history.dialog"; import {SharedAbstractDetailEditCommonRoutable} from "@shared/components/routables/shared-abstract-detail-edit-common/shared-abstract-detail-edit-common.routable"; import {IconNameEnum} from "@shared/enums/icon-name.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; import { + OrganizationalUnitRoutesEnum, PreservationSpaceRoutesEnum, RoutesEnum, } from "@shared/enums/routes.enum"; import {ExtraButtonToolbar} from "@shared/models/extra-button-toolbar.model"; +import {StatusHistoryDialog} from "@shared/models/status-history-dialog.model"; +import {StatusHistory} from "@shared/models/status-history.model"; import {Observable} from "rxjs"; import { isNullOrUndefined, + MARK_AS_TRANSLATABLE, MemoizedUtil, + NotificationService, Override, OverrideProperty, - MARK_AS_TRANSLATABLE, } from "solidify-frontend"; +import {QueryParameters} from "solidify-frontend/lib/models"; import { PreservationSpaceNotificationAction, preservationSpaceNotificationActionNameSpace, } from "../../../stores/preservation-space-notification.action"; import NotificationStatusEnum = NotificationStatus.NotificationStatusEnum; -import {SharedHistoryDialog} from "@shared/components/dialogs/shared-history/shared-history.dialog"; -import {PreservationSipStatusHistoryAction} from "@preservation/sip/stores/status-history/sip-status-history.action"; -import {StatusHistoryDialog} from "@shared/models/status-history-dialog.model"; -import {PreservationSpaceNotificationStatusHistoryState} from "@app/features/preservation-space/notification/stores/status-history/preservation-space-notification-status-history.state"; -import {StatusHistory} from "@shared/models/status-history.model"; -import {QueryParameters} from "solidify-frontend/lib/models"; -import {PreservationSpaceNotificationStatusHistoryAction} from "@app/features/preservation-space/notification/stores/status-history/preservation-space-notification-status-history.action"; +import NotificationTypeEnum = NotificationType.NotificationTypeEnum; @Component({ selector: "dlcm-preservation-space-notification-detail-edit-routable", @@ -76,11 +79,19 @@ export class PreservationSpaceNotificationDetailEditRoutable extends SharedAbstr readonly KEY_PARAM_NAME: keyof NotificationDlcm & string = "notificationType"; listExtraButtons: ExtraButtonToolbar<NotificationDlcm>[] = [ + { + color: "primary", + icon: IconNameEnum.navigate, + displayCondition: current => !isNullOrUndefined(current) && this.mode === NotificationModeEnum.inbox && current.notificationStatus !== NotificationStatusEnum.ACCEPTED, + callback: (current) => this._process(current), + labelToTranslate: MARK_AS_TRANSLATABLE("preservationSpace.notifications.navigation.button.process"), + order: 40, + }, { color: "primary", icon: IconNameEnum.approve, displayCondition: current => !isNullOrUndefined(current) && this.mode === NotificationModeEnum.inbox && current.notificationStatus !== NotificationStatusEnum.ACCEPTED, - callback: () => this.accept(), + callback: () => this._accept(), labelToTranslate: MARK_AS_TRANSLATABLE("preservationSpace.notifications.navigation.button.accept"), order: 40, }, @@ -88,7 +99,7 @@ export class PreservationSpaceNotificationDetailEditRoutable extends SharedAbstr color: "primary", icon: IconNameEnum.unapprove, displayCondition: current => !isNullOrUndefined(current) && this.mode === NotificationModeEnum.inbox && current.notificationStatus !== NotificationStatusEnum.REFUSED, - callback: () => this.refuse(), + callback: () => this._refuse(), labelToTranslate: MARK_AS_TRANSLATABLE("preservationSpace.notifications.navigation.button.refuse"), order: 40, }, @@ -96,7 +107,7 @@ export class PreservationSpaceNotificationDetailEditRoutable extends SharedAbstr color: "primary", icon: IconNameEnum.wait, displayCondition: current => !isNullOrUndefined(current) && this.mode === NotificationModeEnum.inbox && current.notificationStatus !== NotificationStatusEnum.WAITING, - callback: () => this.wait(), + callback: () => this._wait(), labelToTranslate: MARK_AS_TRANSLATABLE("preservationSpace.notifications.navigation.button.wait"), order: 40, }, @@ -106,6 +117,7 @@ export class PreservationSpaceNotificationDetailEditRoutable extends SharedAbstr protected _route: ActivatedRoute, protected readonly _actions$: Actions, protected readonly _changeDetector: ChangeDetectorRef, + private readonly _notificationService: NotificationService, public _dialog: MatDialog) { super(_store, _route, _actions$, _changeDetector, _dialog, LocalStateEnum.preservationSpace_notification, preservationSpaceNotificationActionNameSpace, LocalStateEnum.preservationSpace); } @@ -136,18 +148,37 @@ export class PreservationSpaceNotificationDetailEditRoutable extends SharedAbstr this._store.dispatch(new Navigate([route], NotificationHelper.getUrlQueryParam(orgUnitId))); } - accept(): void { + private _accept(): void { this._store.dispatch(new PreservationSpaceNotificationAction.Accept(this._resId)); } - refuse(): void { + private _refuse(): void { this._store.dispatch(new PreservationSpaceNotificationAction.Refuse(this._resId)); } - wait(): void { + private _wait(): void { this._store.dispatch(new PreservationSpaceNotificationAction.Wait(this._resId)); } + private _process(notification: NotificationDlcm): void { + switch (notification.notificationType) { + case NotificationTypeEnum.JOIN_ORG_UNIT_REQUEST: + this._processJoinOrgUnitRequest(notification); + break; + case NotificationTypeEnum.ACCESS_DATASET_REQUEST: + default: + this._notificationService.showInformation(MARK_AS_TRANSLATABLE("preservationSpace.notifications.notification.processActionNotAvailableForThisType"), {type: notification.notificationType}); + break; + } + } + + private _processJoinOrgUnitRequest(notification: NotificationDlcm): void { + this._store.dispatch(new Navigate([RoutesEnum.preservationSpaceOrganizationalUnitDetail, notification.notifiedOrgUnit.resId, OrganizationalUnitRoutesEnum.edit], { + [NotificationHelper.KEY_ROLE_ID]: "APPROVER", // TODO Change when backend store this info + [NotificationHelper.KEY_PERSON_ID]: notification.emitter.person.resId, + })); + } + showHistory(): void { const dialogRef = this._dialog.open(SharedHistoryDialog, { width: environment.modalWidth, diff --git a/src/app/features/preservation-space/notification/helper/notification.helper.ts b/src/app/features/preservation-space/notification/helper/notification.helper.ts index c0d43f892..fd87432fa 100644 --- a/src/app/features/preservation-space/notification/helper/notification.helper.ts +++ b/src/app/features/preservation-space/notification/helper/notification.helper.ts @@ -6,6 +6,8 @@ import {isNotNullNorUndefined} from "solidify-frontend"; export class NotificationHelper { static readonly KEY_ORGUNIT_ID: keyof NotificationDlcm = "notifiedOrgUnit"; + static readonly KEY_ROLE_ID: string = "roleId"; + static readonly KEY_PERSON_ID: string = "personId"; static getUrlQueryParam(orgUnitId: string): object { if (isNotNullNorUndefined(orgUnitId)) { diff --git a/src/app/features/preservation-space/organizational-unit/components/dialogs/orgunit-request-access/orgunit-request-access.dialog.html b/src/app/features/preservation-space/organizational-unit/components/dialogs/orgunit-request-access/orgunit-request-access.dialog.html index 3da41e778..f18355a5e 100644 --- a/src/app/features/preservation-space/organizational-unit/components/dialogs/orgunit-request-access/orgunit-request-access.dialog.html +++ b/src/app/features/preservation-space/organizational-unit/components/dialogs/orgunit-request-access/orgunit-request-access.dialog.html @@ -3,6 +3,19 @@ class="form" (ngSubmit)="onSubmit()" > + <mat-form-field *ngIf="getFormControl(formDefinition.roleId) as fd"> + <mat-label>{{'sorganizationalUnit.modal.requestAccess.form.desiredRole' | translate}}</mat-label> + <mat-select [formControl]="fd" + [required]="formValidationHelper.hasRequiredField(fd)" + > + <mat-option *ngFor="let role of (listRoleObs | async)" + [value]="role.resId" + > + {{role.name}} + </mat-option> + </mat-select> + </mat-form-field> + <mat-form-field *ngIf="getFormControl(formDefinition.message) as fd"> <mat-label>{{'organizationalUnit.modal.requestAccess.form.message' | translate }}</mat-label> <textarea [formControl]="fd" diff --git a/src/app/features/preservation-space/organizational-unit/components/dialogs/orgunit-request-access/orgunit-request-access.dialog.ts b/src/app/features/preservation-space/organizational-unit/components/dialogs/orgunit-request-access/orgunit-request-access.dialog.ts index e4bdb5597..e3636eab9 100644 --- a/src/app/features/preservation-space/organizational-unit/components/dialogs/orgunit-request-access/orgunit-request-access.dialog.ts +++ b/src/app/features/preservation-space/organizational-unit/components/dialogs/orgunit-request-access/orgunit-request-access.dialog.ts @@ -15,6 +15,7 @@ import { MatDialogRef, } from "@angular/material/dialog"; import {DepositDataFile} from "@deposit/models/deposit-data-file.model"; +import {Role} from "@models"; import { Actions, Store, @@ -22,11 +23,14 @@ import { import {SharedAbstractContainer} from "@shared/components/containers/shared-abstract/shared-abstract.container"; import {BaseFormDefinition} from "@shared/models/base-form-definition.model"; import {SharedNotificationState} from "@shared/stores/notification/shared-notification.state"; +import {SharedRoleAction} from "@shared/stores/role/shared-role.action"; +import {SharedRoleState} from "@shared/stores/role/shared-role.state"; import {Observable} from "rxjs"; import { FormValidationHelper, MemoizedUtil, PropertyName, + ResourceState, SolidifyValidator, } from "solidify-frontend"; @@ -38,6 +42,7 @@ import { }) export class OrgunitRequestAccessDialog extends SharedAbstractContainer implements OnInit { isLoadingObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, SharedNotificationState); + listRoleObs: Observable<Role[]> = ResourceState.list(this._store, SharedRoleState); form: FormGroup; formDefinition: FormComponentFormDefinition = new FormComponentFormDefinition(); @@ -52,7 +57,9 @@ export class OrgunitRequestAccessDialog extends SharedAbstractContainer implemen ngOnInit(): void { super.ngOnInit(); + this._store.dispatch(new SharedRoleAction.GetAll()); this.form = this._fb.group({ + [this.formDefinition.roleId]: ["", [Validators.required, SolidifyValidator]], [this.formDefinition.message]: ["", [Validators.required, SolidifyValidator]], }); } @@ -75,6 +82,7 @@ export class OrgunitRequestAccessDialog extends SharedAbstractContainer implemen } class FormComponentFormDefinition extends BaseFormDefinition { + @PropertyName() roleId: string; @PropertyName() message: string; } diff --git a/src/app/features/preservation-space/organizational-unit/components/presentationals/orgunit-form/orgunit-form.presentational.html b/src/app/features/preservation-space/organizational-unit/components/presentationals/orgunit-form/orgunit-form.presentational.html index cdfc755ac..e181a5d75 100644 --- a/src/app/features/preservation-space/organizational-unit/components/presentationals/orgunit-form/orgunit-form.presentational.html +++ b/src/app/features/preservation-space/organizational-unit/components/presentationals/orgunit-form/orgunit-form.presentational.html @@ -121,6 +121,7 @@ <dlcm-shared-person-orgunit-role [formControl]="form.get(formDefinition.personRole)" [mode]="personOrgUnitRoleMode.person" [selectedPersonOrOrgUnitRole]="selectedPersonRole" + [highlightedPersonOrOrgUnitId]="personIdAutomaticallyAdded" [readonly]="readonly" [listRole]="listRole" (navigate)="_navigateBS.next($event)" diff --git a/src/app/features/preservation-space/organizational-unit/components/presentationals/orgunit-form/orgunit-form.presentational.ts b/src/app/features/preservation-space/organizational-unit/components/presentationals/orgunit-form/orgunit-form.presentational.ts index f2f1d3748..5cbce95a8 100644 --- a/src/app/features/preservation-space/organizational-unit/components/presentationals/orgunit-form/orgunit-form.presentational.ts +++ b/src/app/features/preservation-space/organizational-unit/components/presentationals/orgunit-form/orgunit-form.presentational.ts @@ -45,6 +45,8 @@ import { isNullOrUndefined, MappingObject, MappingObjectUtil, + MARK_AS_TRANSLATABLE, + NotificationService, ObservableUtil, PropertyName, ResourceNameSpace, @@ -80,6 +82,9 @@ export class OrgunitFormPresentational extends SharedAbstractFormPresentational< @Input() isManager: boolean; + @Input() + urlQueryParameters: MappingObject<string | undefined>; + researchDomainLabelCallback: (value: ResearchDomain) => string = (value: ResearchDomain) => `[${value.source}] ${value.name}`; protected readonly _requestToBeMemberBS: BehaviorSubject<void> = new BehaviorSubject<void>(undefined); @@ -90,6 +95,7 @@ export class OrgunitFormPresentational extends SharedAbstractFormPresentational< sharedResearchDomainState: typeof SharedResearchDomainState = SharedResearchDomainState; extraSearchParameterSource: MappingObject = {}; + personIdAutomaticallyAdded: string | undefined = undefined; get personOrgUnitRoleMode(): typeof PersonOrgUnitRoleMode { return PersonOrgUnitRoleMode; @@ -99,6 +105,7 @@ export class OrgunitFormPresentational extends SharedAbstractFormPresentational< protected readonly _elementRef: ElementRef, private readonly _fb: FormBuilder, public readonly breakpointService: BreakpointService, + private readonly _notificationService: NotificationService, public readonly securityService: SecurityService) { super(_changeDetectorRef, _elementRef); } @@ -127,6 +134,32 @@ export class OrgunitFormPresentational extends SharedAbstractFormPresentational< [this.formDefinition.researchDomains]: [_.map(organizationalUnit.researchDomains, LocalModelAttributeEnum.resId), [SolidifyValidator]], [this.formDefinition.keywords]: [isNullOrUndefined(organizationalUnit.keywords) ? [] : [...organizationalUnit.keywords], [SolidifyValidator]], }); + + this._addRoleAskingInNotificationRequest(); + } + + private _addRoleAskingInNotificationRequest(): void { + if (isNullOrUndefined(this.urlQueryParameters) || MappingObjectUtil.size(this.urlQueryParameters) === 0) { + return; + } + const roleId = MappingObjectUtil.get(this.urlQueryParameters, NotificationHelper.KEY_ROLE_ID); + const personId = MappingObjectUtil.get(this.urlQueryParameters, NotificationHelper.KEY_PERSON_ID); + if (isNullOrUndefined(roleId) || isNullOrUndefined(personId)) { + return; + } + this.personIdAutomaticallyAdded = personId; + const existingPerson = this.selectedPersonRole.find(p => p.resId === personId); + if (isNullOrUndefined(existingPerson)) { + this.selectedPersonRole = [...this.selectedPersonRole, { + resId: personId, + roles: [{ + resId: roleId, + }], + }]; + this._notificationService.showInformation(MARK_AS_TRANSLATABLE("organizationalUnit.notification.personAddedToOrgUnit")); + } else { + this._notificationService.showInformation(MARK_AS_TRANSLATABLE("organizationalUnit.notification.personAlreadyInOrgUnit")); + } } protected treatmentBeforeSubmit(organizationalUnit: OrganizationalUnit): OrganizationalUnit { diff --git a/src/app/features/preservation-space/organizational-unit/components/routables/orgunit-detail-edit/orgunit-detail-edit.routable.html b/src/app/features/preservation-space/organizational-unit/components/routables/orgunit-detail-edit/orgunit-detail-edit.routable.html index cc2616dc8..6c50e50e6 100644 --- a/src/app/features/preservation-space/organizational-unit/components/routables/orgunit-detail-edit/orgunit-detail-edit.routable.html +++ b/src/app/features/preservation-space/organizational-unit/components/routables/orgunit-detail-edit/orgunit-detail-edit.routable.html @@ -29,6 +29,7 @@ [readonly]="!isEdit" [editAvailable]="editAvailable" [researchDomainSources]="researchDomainSourcesObs | async" + [urlQueryParameters]="urlQueryParameters" (submitChange)="update($event)" (checkAvailableChange)="checkAvailable($event)" (dirtyChange)="updateCanDeactivate($event)" diff --git a/src/app/models/index.ts b/src/app/models/index.ts index b2d3cbfb3..42f92fa8f 100644 --- a/src/app/models/index.ts +++ b/src/app/models/index.ts @@ -42,7 +42,6 @@ import {Sip as SipPartial} from "../generated-api/model/sip.partial.model"; import {SubmissionAgreement as SubmissionAgreementPartial} from "../generated-api/model/submission-agreement.partial.model"; import {SubmissionPolicy as SubmissionPolicyPartial} from "../generated-api/model/submission-policy.partial.model"; import {User as UserPartial} from "../generated-api/model/user.partial.model"; -import FormDescriptionTypeEnum = FormDescriptionType.FormDescriptionTypeEnum; export interface AccessOrganizationalUnit extends AccessOrganizationalUnitPartial { } @@ -108,6 +107,7 @@ export namespace FormDescriptionType { FORMLY: "FORMLY" as FormDescriptionTypeEnum, }; } + export interface AdditionalFieldsForm { resId?: string; type: FormDescriptionType.FormDescriptionTypeEnum; @@ -301,4 +301,7 @@ export interface SubmissionPolicy extends SubmissionPolicyPartial { } export interface User extends UserPartial { + person?: Person; + accessToken?: string; + refreshToken?: string; } diff --git a/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.html b/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.html index 44963c9c7..7e83632ad 100644 --- a/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.html +++ b/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.html @@ -7,7 +7,9 @@ <ul *ngIf="formArray?.controls?.length > 0" class="list-person-role" > - <li *ngFor="let f of formArray.controls; let i = index"> + <li *ngFor="let f of formArray.controls; let i = index" + [class.is-highlighted]="highlightedPersonOrOrgUnitId === getFormControl(f, formDefinition.id).value" + > <div class="left-part"> <dlcm-shared-searchable-single-select *ngIf="mode === modeEnum.person && getFormControl(f, formDefinition.id) as fd" solidifyValidation diff --git a/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.scss b/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.scss index 7b1bee39a..41ce5d520 100644 --- a/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.scss +++ b/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.scss @@ -25,6 +25,10 @@ flex-direction: row; padding: 10px 10px; + &.is-highlighted { + background-color: $primary-color-extra-lighter !important; + } + &:not(:last-child) { border-bottom: 1px solid $medium-light-grey; } diff --git a/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.ts b/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.ts index 66acbd1e5..4bfc89966 100644 --- a/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.ts +++ b/src/app/shared/components/presentationals/shared-person-orgunit-role/shared-person-orgunit-role.presentational.ts @@ -47,11 +47,11 @@ import { isEmptyArray, isNullOrUndefined, isUndefined, + MARK_AS_TRANSLATABLE, ObservableUtil, PropertyName, ResourceNameSpace, StringUtil, - MARK_AS_TRANSLATABLE, } from "solidify-frontend"; @Component({ @@ -85,6 +85,9 @@ export class SharedPersonOrgunitRolePresentational extends SharedAbstractPresent @Input() listRole: Role[]; + @Input() + highlightedPersonOrOrgUnitId: string; + sharedPersonActionNameSpace: ResourceNameSpace = sharedPersonActionNameSpace; sharedPersonState: typeof SharedPersonState = SharedPersonState; diff --git a/src/app/shared/components/routables/shared-abstract-detail-edit-common/shared-abstract-detail-edit-common.routable.ts b/src/app/shared/components/routables/shared-abstract-detail-edit-common/shared-abstract-detail-edit-common.routable.ts index d74b43b23..9598ac83b 100644 --- a/src/app/shared/components/routables/shared-abstract-detail-edit-common/shared-abstract-detail-edit-common.routable.ts +++ b/src/app/shared/components/routables/shared-abstract-detail-edit-common/shared-abstract-detail-edit-common.routable.ts @@ -24,7 +24,8 @@ import { ResourceStateModel, } from "solidify-frontend"; -export abstract class SharedAbstractDetailEditCommonRoutable<TResourceModel extends BaseResourceType, UResourceStateModel extends ResourceStateModel<TResourceModel>> extends SharedAbstractDetailEditRoutable<TResourceModel, UResourceStateModel> implements OnInit, OnDestroy { +export abstract class SharedAbstractDetailEditCommonRoutable<TResourceModel extends BaseResourceType, UResourceStateModel extends ResourceStateModel<TResourceModel>> + extends SharedAbstractDetailEditRoutable<TResourceModel, UResourceStateModel> implements OnInit, OnDestroy { abstract readonly KEY_PARAM_NAME: keyof TResourceModel & string; protected constructor(protected readonly _store: Store, diff --git a/src/app/shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable.ts b/src/app/shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable.ts index 653246592..204ff6f7d 100644 --- a/src/app/shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable.ts +++ b/src/app/shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable.ts @@ -25,6 +25,7 @@ import { import {SharedAbstractFormPresentational} from "@shared/components/presentationals/shared-abstract-form/shared-abstract-form.presentational"; import {AppRoutesEnum} from "@shared/enums/routes.enum"; import {CrudHelper} from "@shared/helpers/crud.helper"; +import {UrlQueryParamHelper} from "@shared/helpers/url-query-param.helper"; import {FormControlKey} from "@shared/models/form-control-key.model"; import {LocalStateModel} from "@shared/models/local-state.model"; import {Observable} from "rxjs"; @@ -37,6 +38,7 @@ import { import { BaseResourceType, isNullOrUndefined, + MappingObject, ModelFormControlEvent, QueryParameters, ResourceActionHelper, @@ -66,6 +68,7 @@ export abstract class SharedAbstractDetailEditRoutable<TResourceModel extends Ba isEdit: boolean; isEditObs: Observable<boolean>; + urlQueryParameters: MappingObject<string | undefined>; readonly editAvailable: boolean = true; readonly deleteAvailable: boolean = true; @@ -127,7 +130,10 @@ export abstract class SharedAbstractDetailEditRoutable<TResourceModel extends Ba this.isEditObs = this.urlStateObs.pipe( map(url => url.url), distinctUntilChanged(), - map(url => { + map(urlRaw => { + const urlSplitted = urlRaw.split("?"); + const url = urlSplitted[0]; + this._extractQueryParams(urlSplitted.length > 1 ? urlSplitted[1] : undefined); this.isEdit = url.endsWith(AppRoutesEnum.separator + AppRoutesEnum.edit); this._changeDetector.detectChanges(); return this.isEdit; @@ -187,4 +193,8 @@ export abstract class SharedAbstractDetailEditRoutable<TResourceModel extends Ba checkAvailable(formControlKey: FormControlKey): void { this.subscribe(CrudHelper.checkAvailable(formControlKey, this._store, this._actions$, this._resourceNameSpace, this._resId)); } + + private _extractQueryParams(rawQueryParameters: string): void { + this.urlQueryParameters = UrlQueryParamHelper.getQueryParamMappingObject(rawQueryParameters); + } } diff --git a/src/app/shared/helpers/url-query-param.helper.ts b/src/app/shared/helpers/url-query-param.helper.ts index ddb4e958a..32174a6da 100644 --- a/src/app/shared/helpers/url-query-param.helper.ts +++ b/src/app/shared/helpers/url-query-param.helper.ts @@ -4,6 +4,7 @@ import { isEmptyString, isNotNullNorUndefined, isNullOrUndefined, + MappingObject, MappingObjectUtil, StringUtil, } from "solidify-frontend"; @@ -16,6 +17,23 @@ export class UrlQueryParamHelper { private static readonly _QUERY_PARAM_SEPARATOR_CHAR: string = "&"; private static readonly _QUERY_PARAM_VALUE_AFFECTION_CHAR: string = "="; + static getQueryParamMappingObject(rawQueryParam: string): MappingObject<string | undefined> { + const mappingObject = new Map<string, string>(); + if (isNullOrUndefined(rawQueryParam)) { + return mappingObject; + } + const listQueryParam = this._getListQueryParameters(rawQueryParam); + if (listQueryParam.length === 0) { + return mappingObject; + } + listQueryParam.forEach(queryParam => { + const queryParamSplitted = queryParam.split(this._QUERY_PARAM_VALUE_AFFECTION_CHAR); + mappingObject.set(queryParamSplitted[0], queryParamSplitted.length > 1 ? queryParamSplitted[1] : undefined); + }); + + return mappingObject; + } + static getQueryParamOAuth2(): string { // NEED TO PRESERVE QUERY PARAM WHEN ROOTING USING HASH STRATEGY IS INITIALIZED // NECESSARY FOR OAUTH 2 PROCESS (SHOULD PRESERVED CODE AND STATE IN QUERY PARAM) diff --git a/src/app/shared/models/business/user-extended.model.ts b/src/app/shared/models/business/user-extended.model.ts deleted file mode 100644 index e47632349..000000000 --- a/src/app/shared/models/business/user-extended.model.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { - Person, - User, -} from "@models"; - -export interface UserExtended extends User { - person?: Person; - accessToken?: string; - refreshToken?: string; -} diff --git a/src/app/stores/app.state.ts b/src/app/stores/app.state.ts index fed448e23..6f547009f 100644 --- a/src/app/stores/app.state.ts +++ b/src/app/stores/app.state.ts @@ -52,6 +52,7 @@ import { OrganizationalUnit, Person, Role, + User, } from "@models"; import {TranslateService} from "@ngx-translate/core"; import {Navigate} from "@ngxs/router-plugin"; @@ -74,7 +75,6 @@ import {LocalStateEnum} from "@shared/enums/local-state.enum"; import {PollingHelper} from "@shared/helpers/polling.helper"; import {SessionStorageHelper} from "@shared/helpers/session-storage.helper"; import {UrlQueryParamHelper} from "@shared/helpers/url-query-param.helper"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import {ShortDoi} from "@shared/models/short-doi.model"; import {Token} from "@shared/models/token.model"; import * as moment from "moment"; @@ -189,7 +189,7 @@ export class AppState extends BasicState<AppStateModel> { } @Selector() - static currentUser(state: AppStateModel): UserExtended { + static currentUser(state: AppStateModel): User { return state.application_user.current; } diff --git a/src/app/stores/system-property/app-system-property.state.ts b/src/app/stores/system-property/app-system-property.state.ts index f88c40791..d8410500a 100644 --- a/src/app/stores/system-property/app-system-property.state.ts +++ b/src/app/stores/system-property/app-system-property.state.ts @@ -7,6 +7,7 @@ import { appSystemPropertyActionNameSpace, } from "@app/stores/system-property/app-system-property.action"; import {environment} from "@environments/environment"; +import {User} from "@models"; import { Action, Actions, @@ -14,7 +15,6 @@ import { StateContext, Store, } from "@ngxs/store"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import {Observable} from "rxjs"; import { catchError, @@ -30,7 +30,7 @@ import { SolidifyStateError, } from "solidify-frontend"; -export interface AppSystemPropertyStateModel extends ResourceStateModel<UserExtended> { +export interface AppSystemPropertyStateModel extends ResourceStateModel<User> { } @Injectable() @@ -63,7 +63,7 @@ export class AppSystemPropertyState extends ResourceState<AppSystemPropertyState return this.apiService.getByIdInPath<SystemProperty>(AdminResourceApiEnum.systemProperties) .pipe( - tap((user: UserExtended) => { + tap((user: User) => { ctx.dispatch(new AppSystemPropertyAction.GetSystemPropertiesSuccess(action, user)); }), catchError(error => { diff --git a/src/app/stores/user/app-user.action.ts b/src/app/stores/user/app-user.action.ts index 158434c52..e9727aef0 100644 --- a/src/app/stores/user/app-user.action.ts +++ b/src/app/stores/user/app-user.action.ts @@ -1,6 +1,8 @@ -import {Person} from "@models"; +import { + Person, + User, +} from "@models"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import { BaseAction, BaseSubAction, @@ -147,7 +149,7 @@ export namespace AppUserAction { export class GetCurrentUserSuccess extends BaseSubAction<GetCurrentUser> { static readonly type: string = `[${state}] Get Current User Success`; - constructor(public parentAction: GetCurrentUser, public user: UserExtended) { + constructor(public parentAction: GetCurrentUser, public user: User) { super(parentAction); } } diff --git a/src/app/stores/user/app-user.state.ts b/src/app/stores/user/app-user.state.ts index a1f2a103c..68c6a3d83 100644 --- a/src/app/stores/user/app-user.state.ts +++ b/src/app/stores/user/app-user.state.ts @@ -6,6 +6,7 @@ import { AppUserAction, appUserActionNameSpace, } from "@app/stores/user/app-user.action"; +import {User} from "@models"; import { Action, Actions, @@ -14,7 +15,6 @@ import { Store, } from "@ngxs/store"; import {ApiActionEnum} from "@shared/enums/api-action.enum"; -import {UserExtended} from "@shared/models/business/user-extended.model"; import {Observable} from "rxjs"; import { catchError, @@ -33,7 +33,7 @@ import { } from "solidify-frontend"; import {environment} from "../../../environments/environment"; -export interface AppUserStateModel extends ResourceStateModel<UserExtended> { +export interface AppUserStateModel extends ResourceStateModel<User> { } @Injectable() @@ -44,7 +44,7 @@ export interface AppUserStateModel extends ResourceStateModel<UserExtended> { queryParameters: new QueryParameters(environment.defaultEnumValuePageSizeOption), }, }) -export class AppUserState extends ResourceState<AppUserStateModel, UserExtended> { +export class AppUserState extends ResourceState<AppUserStateModel, User> { constructor(protected apiService: ApiService, protected store: Store, protected notificationService: NotificationService, @@ -59,10 +59,10 @@ export class AppUserState extends ResourceState<AppUserStateModel, UserExtended> } @Action(AppUserAction.GetCurrentUser) - getCurrentUser(ctx: StateContext<AppUserStateModel>, action: AppUserAction.GetCurrentUser): Observable<CollectionTyped<UserExtended>> { - return this.apiService.get<UserExtended>(AdminResourceApiEnum.users + urlSeparator + ApiActionEnum.AUTHENTICATED, null) + getCurrentUser(ctx: StateContext<AppUserStateModel>, action: AppUserAction.GetCurrentUser): Observable<CollectionTyped<User>> { + return this.apiService.get<User>(AdminResourceApiEnum.users + urlSeparator + ApiActionEnum.AUTHENTICATED, null) .pipe( - tap((user: UserExtended) => { + tap((user: User) => { ctx.dispatch(new AppUserAction.GetCurrentUserSuccess(action, user)); }), catchError(error => { diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index b31281192..6bf44112e 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -893,7 +893,7 @@ "button": { "back": "Zurück" }, - "title": "Titel" + "title": "Aip nicht gefunden" } }, "buttonInfoCreation": { @@ -2119,6 +2119,8 @@ "name": "Name", "new": "Neue Organisationseinheit anlegen", "notification": { + "personAddedToOrgUnit": "Die Person wurde der Organisationseinheit hinzugefügt", + "personAlreadyInOrgUnit": "Die Person ist bereits Teil der Organisationseinheit", "resource": { "create": "Organisatorische Einheit erstellt", "delete": "Organisationseinheit gelöscht", @@ -2537,11 +2539,13 @@ "navigation": { "button": { "accept": "Akzeptieren Sie", + "process": "Prozess", "refuse": "ablehnen", "wait": "Warten" } }, "notification": { + "processActionNotAvailableForThisType": "Halbautomatische Verarbeitung von Abfragen des Typs {{type}} sind noch nicht verfügbar", "resource": { "create": "Benachrichtigung erstellt", "delete": "Benachrichtigung gelöscht", @@ -2618,6 +2622,15 @@ } } }, + "sorganizationalUnit": { + "modal": { + "requestAccess": { + "form": { + "desiredRole": "Gewünschte Rolle" + } + } + } + }, "table": { "cleanAllFilter": "Alle Filter entfernen", "filter": { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index f1c41bf1b..819e460cd 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -893,7 +893,7 @@ "button": { "back": "Back" }, - "title": "Title" + "title": "Aip not found" } }, "buttonInfoCreation": { @@ -2119,6 +2119,8 @@ "name": "Name", "new": "Create new organizational unit", "notification": { + "personAddedToOrgUnit": "The person has been added to the organizational unit", + "personAlreadyInOrgUnit": "The person is already part of the organizational unit", "resource": { "create": "Organizational Unit created", "delete": "Organizational Unit deleted", @@ -2537,11 +2539,13 @@ "navigation": { "button": { "accept": "Accept", + "process": "Process", "refuse": "Refuse", "wait": "Wait" } }, "notification": { + "processActionNotAvailableForThisType": "Semi-automatic processing of {{type}} type queries are not yet available", "resource": { "create": "Notification created", "delete": "Notification deleted", @@ -2618,6 +2622,15 @@ } } }, + "sorganizationalUnit": { + "modal": { + "requestAccess": { + "form": { + "desiredRole": "Desired role" + } + } + } + }, "table": { "cleanAllFilter": "Remove all filters", "filter": { diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 92c935c2e..3d7c3216a 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -891,9 +891,9 @@ "aipDownloaded": { "notFound": { "button": { - "back": "app.aipDownloaded.notFound.button.back" + "back": "Retour" }, - "title": "app.aipDownloaded.notFound.title" + "title": "AIP introuvable" } }, "buttonInfoCreation": { @@ -2119,6 +2119,8 @@ "name": "Nom", "new": "Créer nouvelle unité organisationnelle", "notification": { + "personAddedToOrgUnit": "La personne a été ajouté à l'unité organisationnelle", + "personAlreadyInOrgUnit": "La personne fait déjà partie de l'unité organisationnelle", "resource": { "create": "Unité Organisationnelle créée avec succès", "delete": "Unité Organisationnelle supprimée avec succès", @@ -2537,11 +2539,13 @@ "navigation": { "button": { "accept": "Accepter", + "process": "Traiter", "refuse": "Refuser", "wait": "En attente" } }, "notification": { + "processActionNotAvailableForThisType": "Le traitement semi-automatique des requêtes de type {{type}} ne sont pas encore disponible", "resource": { "create": "Notification créée avec succès", "delete": "Notification supprimée avec succès", @@ -2618,6 +2622,15 @@ } } }, + "sorganizationalUnit": { + "modal": { + "requestAccess": { + "form": { + "desiredRole": "Rôle désiré" + } + } + } + }, "table": { "cleanAllFilter": "Supprimer tous les filtres", "filter": { -- GitLab