diff --git a/src/app/features/admin/notification/components/routables/admin-notification-detail-edit/admin-notification-detail-edit.routable.ts b/src/app/features/admin/notification/components/routables/admin-notification-detail-edit/admin-notification-detail-edit.routable.ts index 2f27b7eef49d14c92eaa9339eba220e3418866d4..292e65114b7eea4f4f439e59a9938d26ac51866a 100644 --- a/src/app/features/admin/notification/components/routables/admin-notification-detail-edit/admin-notification-detail-edit.routable.ts +++ b/src/app/features/admin/notification/components/routables/admin-notification-detail-edit/admin-notification-detail-edit.routable.ts @@ -81,6 +81,7 @@ import {NotificationHelper} from "@preservation-space/notification/helper/notifi import { AdminNotificationArchiveAclExternalDuaDialog, } from "@admin/notification/components/dialogs/admin-notification-archive-acl-external-dua/admin-notification-archive-acl-external-dua.dialog"; +import {SharedNotificationHelper} from "@shared/helpers/shared-notification.helper"; @Component({ selector: "dlcm-admin-role-detail-edit-routable", @@ -158,7 +159,7 @@ export class AdminNotificationDetailEditRoutable extends AbstractDetailEditCommo } private _setRefuse(notification: NotificationDlcm): void { - this._store.dispatch(new SharedNotificationAction.SetRefuse(notification.resId, notification.notificationType.notificationCategory, ViewModeEnum.detail)); + this.subscribe(SharedNotificationHelper.getRefuseDialog(this._dialog, this._store, ViewModeEnum.detail, notification)); } private _setUnread(notification: NotificationDlcm): void { diff --git a/src/app/features/admin/notification/components/routables/admin-notification-list/admin-notification-list.routable.ts b/src/app/features/admin/notification/components/routables/admin-notification-list/admin-notification-list.routable.ts index a3ec49495920a992726bfca3093a15f9f81b7966..4fa6f87bc19e8351cdda67f25dccca3435d2edb5 100644 --- a/src/app/features/admin/notification/components/routables/admin-notification-list/admin-notification-list.routable.ts +++ b/src/app/features/admin/notification/components/routables/admin-notification-list/admin-notification-list.routable.ts @@ -76,6 +76,7 @@ import { } from "solidify-frontend"; import {RoutesEnum} from "@shared/enums/routes.enum"; import {Navigate} from "@ngxs/router-plugin"; +import {SharedNotificationHelper} from "@shared/helpers/shared-notification.helper"; @Component({ selector: "dlcm-admin-notification-list-routable", @@ -190,7 +191,7 @@ export class AdminNotificationListRoutable extends AbstractListRoutable<Notifica } private _setRefuse(notification: NotificationDlcm, mode: ViewModeEnum | undefined): void { - this._store.dispatch(new SharedNotificationAction.SetRefuse(notification.resId, notification.notificationType.notificationCategory, mode)); + this.subscribe(SharedNotificationHelper.getRefuseDialog(this._dialog, this._store, ViewModeEnum.list, notification)); } private _setRead(notification: NotificationDlcm): void { 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 5f7859f0235b763f63e6be433a1031bcf811338a..4b35cf93f4ed08c7b536369f9de6a7efa0594376 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 @@ -85,6 +85,7 @@ import { PreservationSpaceNotificationAction, preservationSpaceNotificationActionNameSpace, } from "../../../stores/preservation-space-notification.action"; +import {SharedNotificationHelper} from "@shared/helpers/shared-notification.helper"; @Component({ selector: "dlcm-preservation-space-notification-detail-edit-routable", @@ -189,7 +190,7 @@ export class PreservationSpaceNotificationDetailEditRoutable extends AbstractDet } private _setRefuse(notification: NotificationDlcm): void { - this._store.dispatch(new SharedNotificationAction.SetRefuse(notification.resId, notification.notificationType.notificationCategory, ViewModeEnum.detail)); + this.subscribe(SharedNotificationHelper.getRefuseDialog(this._dialog, this._store, ViewModeEnum.detail, notification)); } private _setUnread(notification: NotificationDlcm): void { diff --git a/src/app/features/preservation-space/notification/components/routables/preservation-space-notification-list/preservation-space-notification-list.routable.ts b/src/app/features/preservation-space/notification/components/routables/preservation-space-notification-list/preservation-space-notification-list.routable.ts index e47c15a993b54fe93a6fb0dd9bae8834d2bfcb93..34d001357088bf65190e5852f7812c682fe82abf 100644 --- a/src/app/features/preservation-space/notification/components/routables/preservation-space-notification-list/preservation-space-notification-list.routable.ts +++ b/src/app/features/preservation-space/notification/components/routables/preservation-space-notification-list/preservation-space-notification-list.routable.ts @@ -85,6 +85,7 @@ import { PreservationSpaceNotificationAction, preservationSpaceNotificationActionNameSpace, } from "../../../stores/preservation-space-notification.action"; +import {SharedNotificationHelper} from "@shared/helpers/shared-notification.helper"; @Component({ selector: "dlcm-preservation-space-notification-list-routable", @@ -207,7 +208,7 @@ export class PreservationSpaceNotificationListRoutable extends AbstractListRouta logo: IconNameEnum.unapprove, displayOnCondition: current => this.mode === NotificationModeEnum.inbox && current?.notificationStatus === Enums.Notification.StatusEnum.PENDING && current?.notificationType.notificationCategory === Enums.Notification.CategoryEnum.REQUEST, - callback: (current) => this._setRefuse(current, ViewModeEnum.list), + callback: (current) => this._setRefuse(current), placeholder: (current) => LabelTranslateEnum.markAsRefused, isWrapped: true, }, @@ -232,8 +233,8 @@ export class PreservationSpaceNotificationListRoutable extends AbstractListRouta this._store.dispatch(new SharedNotificationAction.SetApproved(notification.resId, notification.notificationType.notificationCategory, mode)); } - private _setRefuse(notification: NotificationDlcm, mode: ViewModeEnum | undefined): void { - this._store.dispatch(new SharedNotificationAction.SetRefuse(notification.resId, notification.notificationType.notificationCategory, mode)); + private _setRefuse(notification: NotificationDlcm): void { + this.subscribe(SharedNotificationHelper.getRefuseDialog(this._dialog, this._store, ViewModeEnum.list, notification)); } private _setRead(notification: NotificationDlcm): void { diff --git a/src/app/shared/components/presentationals/shared-notification-form/shared-notification-form.presentational.html b/src/app/shared/components/presentationals/shared-notification-form/shared-notification-form.presentational.html index bcf27ce48bc3aa93c383e089b77ae969a6fba596..96f091f587e719ba1a65a45f634052de6996b096 100644 --- a/src/app/shared/components/presentationals/shared-notification-form/shared-notification-form.presentational.html +++ b/src/app/shared/components/presentationals/shared-notification-form/shared-notification-form.presentational.html @@ -116,6 +116,28 @@ ></mat-error> </mat-form-field> + <ng-container *ngIf="getFormControl(formDefinition.responseMessage) as fd"> + <mat-form-field + *ngIf="fd.value | isNotNullNorUndefinedNorWhiteString" + [appearance]="appearanceInputMaterial" + [class.mat-form-field-invalid]="formValidationHelper.displayInvalidWhenRequired(fd, displayEmptyRequiredFieldInError)" + solidifyTooltipOnEllipsis + > + <mat-label>{{labelTranslateEnum.reason | translate }}</mat-label> + <textarea [formControl]="fd" + [required]="formValidationHelper.hasRequiredField(fd)" + [solidifyValidation]="errors" + cdkAutosizeMaxRows="10" + cdkAutosizeMinRows="5" + cdkTextareaAutosize + matInput + ></textarea> + <mat-error #errors + solidifyTooltipOnEllipsis + ></mat-error> + </mat-form-field> + </ng-container> + </ng-template> <ng-template #rightTemplate> diff --git a/src/app/shared/components/presentationals/shared-notification-form/shared-notification-form.presentational.ts b/src/app/shared/components/presentationals/shared-notification-form/shared-notification-form.presentational.ts index d631a7c9a29997da02b257a0ed5b8848e9403bcc..0abaa08cdc98116cf16f129797a55a8c1192d5b6 100644 --- a/src/app/shared/components/presentationals/shared-notification-form/shared-notification-form.presentational.ts +++ b/src/app/shared/components/presentationals/shared-notification-form/shared-notification-form.presentational.ts @@ -149,6 +149,7 @@ export class SharedNotificationFormPresentational extends AbstractFormPresentati [this.formDefinition.emitterId]: [notificationDlcm.emitter?.person?.resId, [Validators.required, SolidifyValidator]], [this.formDefinition.emitterEmail]: [notificationDlcm.emitter?.email, [SolidifyValidator]], [this.formDefinition.message]: [notificationDlcm.message, [Validators.required, SolidifyValidator]], + [this.formDefinition.responseMessage]: [notificationDlcm.responseMessage, [SolidifyValidator]], [this.formDefinition.objectId]: [notificationDlcm.objectId, [Validators.required, SolidifyValidator]], [this.formDefinition.creation]: [DateUtil.convertDateToDateTimeString(new Date(notificationDlcm.creation.when)), [Validators.required, SolidifyValidator]], [this.formDefinition.lastUpdate]: [DateUtil.convertDateToDateTimeString(new Date(notificationDlcm.lastUpdate.when)), [Validators.required, SolidifyValidator]], @@ -164,6 +165,7 @@ export class SharedNotificationFormPresentational extends AbstractFormPresentati [this.formDefinition.emitterId]: ["", [Validators.required, SolidifyValidator]], [this.formDefinition.emitterEmail]: ["", [SolidifyValidator]], [this.formDefinition.message]: ["", [Validators.required, SolidifyValidator]], + [this.formDefinition.responseMessage]: ["", [SolidifyValidator]], [this.formDefinition.objectId]: ["", [Validators.required, SolidifyValidator]], [this.formDefinition.creation]: ["", [Validators.required, SolidifyValidator]], [this.formDefinition.lastUpdate]: ["", [Validators.required, SolidifyValidator]], @@ -230,6 +232,7 @@ class FormComponentFormDefinition extends BaseFormDefinition { @PropertyName() emitterId: string; @PropertyName() emitterEmail: string; @PropertyName() message: string; + @PropertyName() responseMessage: string; @PropertyName() objectId: string; @PropertyName() creation: string; @PropertyName() lastUpdate: string; diff --git a/src/app/shared/helpers/shared-notification.helper.ts b/src/app/shared/helpers/shared-notification.helper.ts new file mode 100644 index 0000000000000000000000000000000000000000..6c46d83f216cec802928e927cc0ac64d7fef8047 --- /dev/null +++ b/src/app/shared/helpers/shared-notification.helper.ts @@ -0,0 +1,69 @@ +/*- + * %%---------------------------------------------------------------------------------------------- + * DLCM Technology - DLCM Portal - shared-notification.helper.ts + * SPDX-License-Identifier: GPL-2.0-or-later + * %----------------------------------------------------------------------------------------------% + * Copyright (C) 2017 - 2024 University of Geneva + * %----------------------------------------------------------------------------------------------% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-2.0.html>. + * ----------------------------------------------------------------------------------------------%% + */ + + +import {NotificationDlcm} from "@models"; +import {ViewModeEnum} from "@shared/enums/view-mode.enum"; +import { + ButtonColorEnum, + ConfirmDialog, + ConfirmDialogInputEnum, + DialogUtil, + EnumUtil, + isEmptyString, + isString, + MARK_AS_TRANSLATABLE, +} from "solidify-frontend"; +import {LabelTranslateEnum} from "@shared/enums/label-translate.enum"; +import {SharedNotificationAction} from "@shared/stores/notification/shared-notification.action"; +import {MatDialog} from "@angular/material/dialog"; +import {Store} from "@ngxs/store"; +import {Observable} from "rxjs"; +import {Enums} from "@enums"; + +export class SharedNotificationHelper { + + public static getRefuseDialog(dialog: MatDialog, store: Store, mode: ViewModeEnum | undefined, notification: NotificationDlcm): Observable<any> { + + return DialogUtil.open(dialog, ConfirmDialog, { + titleToTranslate: EnumUtil.getLabel(Enums.Notification.TypeEnumTranslate, notification.notificationType.resId), + messageToTranslate: MARK_AS_TRANSLATABLE("notification.notification.pleaseExplainRefusalReason"), + confirmButtonToTranslate: LabelTranslateEnum.yesImSure, + cancelButtonToTranslate: LabelTranslateEnum.cancel, + withInput: ConfirmDialogInputEnum.TEXTAREA, + inputRequired: true, + inputLabelToTranslate: LabelTranslateEnum.reason, + colorConfirm: ButtonColorEnum.warn, + colorCancel: ButtonColorEnum.primary, + }, { + minWidth: "500px", + maxWidth: "90vw", + takeOne: true, + }, responseMessage => { + if (!isString(responseMessage) || isEmptyString(responseMessage)) { + return; + } + store.dispatch(new SharedNotificationAction.SetRefuse(notification.resId, notification.notificationType.notificationCategory, mode, responseMessage)); + }); + } +} diff --git a/src/app/shared/stores/notification/shared-notification.action.ts b/src/app/shared/stores/notification/shared-notification.action.ts index b1e64d222bf82bcc27dbb585a718ca0616cf7cb8..1bbceeb3c9b9569dfade5b3100a02416ff0b376a 100644 --- a/src/app/shared/stores/notification/shared-notification.action.ts +++ b/src/app/shared/stores/notification/shared-notification.action.ts @@ -281,7 +281,7 @@ export namespace SharedNotificationAction { export class SetRefuse extends BaseAction { static readonly type: string = `[${state}] Set Refuse`; - constructor(public notificationId: string, public notificationCategory: Enums.Notification.CategoryEnum, public mode: ViewModeEnum | undefined) { + constructor(public notificationId: string, public notificationCategory: Enums.Notification.CategoryEnum, public mode: ViewModeEnum | undefined, public responseMessage: string) { super(); } } @@ -361,7 +361,7 @@ export namespace SharedNotificationAction { export class BulkSetRefuse extends BaseAction { static readonly type: string = `[${state}] Bulk Set Refuse`; - constructor(public listNotification: NotificationDlcm[]) { + constructor(public listNotification: NotificationDlcm[], public responseMessage: string) { super(); } } diff --git a/src/app/shared/stores/notification/shared-notification.state.ts b/src/app/shared/stores/notification/shared-notification.state.ts index 73dc9aa4d1d8918cb994952eeca506bdbf34c55d..20934e4c0802b53926de629178031a7b7ea717ec 100644 --- a/src/app/shared/stores/notification/shared-notification.state.ts +++ b/src/app/shared/stores/notification/shared-notification.state.ts @@ -106,7 +106,7 @@ export class SharedNotificationState extends ResourceFileState<SharedNotificatio private readonly _ATTRIBUTE_ORG_UNIT_ID: keyof NotificationDlcm | string = "notifiedOrgUnit.resId"; private readonly _ATTRIBUTE_NOTIFICATION_TYPE: keyof NotificationDlcm = "notificationType"; private readonly _ATTRIBUTE_NOTIFICATION_STATUS: keyof NotificationDlcm = "notificationStatus"; - + private readonly _ATTRIBUTE_NOTIFICATION_REJECT_REASON: string = "reason"; constructor(protected readonly _apiService: ApiService, protected readonly _store: Store, protected readonly _notificationService: NotificationService, @@ -294,7 +294,12 @@ export class SharedNotificationState extends ResourceFileState<SharedNotificatio @Action(SharedNotificationAction.SetRefuse) setRefuse(ctx: SolidifyStateContext<SharedNotificationStateModel>, action: SharedNotificationAction.SetRefuse): Observable<Result> { - return this._apiService.post<Result>(this._urlResource + urlSeparator + action.notificationId + urlSeparator + ApiActionNameEnum.SET_REFUSED) + + const customParams = { + [this._ATTRIBUTE_NOTIFICATION_REJECT_REASON]: action.responseMessage, + }; + + return this._apiService.post<Result>(this._urlResource + urlSeparator + action.notificationId + urlSeparator + ApiActionNameEnum.SET_REFUSED, null, customParams) .pipe( tap(result => ctx.dispatch(new SharedNotificationAction.SetRefuseSuccess(action))), catchError((error: SolidifyHttpErrorResponseModel) => { @@ -562,7 +567,7 @@ export class SharedNotificationState extends ResourceFileState<SharedNotificatio .filter(n => n?.notificationStatus === Enums.Notification.StatusEnum.PENDING) .map(notification => ({ - action: new SharedNotificationAction.SetRefuse(notification.resId, notification.notificationType.notificationCategory, undefined), + action: new SharedNotificationAction.SetRefuse(notification.resId, notification.notificationType.notificationCategory, undefined, action.responseMessage), subActionCompletions: [ this._actions$.pipe(ofSolidifyActionCompleted(SharedNotificationAction.SetRefuseSuccess)), this._actions$.pipe(ofSolidifyActionCompleted(SharedNotificationAction.SetRefuseFail)), diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 75d5738f17a462c0e54ee967f7bbc3c9a9724034..96668a15f9a96d3fe72e092e58b7d427ac0662c3 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1944,7 +1944,8 @@ }, "notification": { "requestSendWithSuccess": "Request sent", - "unableToSendRequest": "Unable to send request" + "unableToSendRequest": "Unable to send request", + "pleaseExplainRefusalReason": "Please explain the reason for your decision." }, "orcid": { "associated": "ORCID '{{orcid}}' associated" @@ -2468,4 +2469,4 @@ "title": "User guide", "tooltipClose": "Close user guide" } -} \ No newline at end of file +} diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 3bd18484cdd080b17cd968836cfe1ca39f5cf6c2..dfdd245aa51a4e59627c174ee9adb2e87c826e30 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -1934,7 +1934,7 @@ "inErrorDipInfo": "DIP en erreur", "inErrorDownloadedAipInfo": "AIP téléchargé en erreur", "inErrorSipInfo": "SIP en erreur", - "joinOrgUnitRequest": "Demande pour rejoindre une unité organisationnelle", + "joinOrgUnitRequest": "Demande d'adhésion à une unité organisationnelle", "myApprovedDepositInfo": "Dépôt approuvé", "myCompletedArchiveInfo": "Archive issue d'un de mes dépôt complétée", "myCompletedDepositInfo": "Dépôt complété", @@ -1944,7 +1944,8 @@ }, "notification": { "requestSendWithSuccess": "Demande envoyée", - "unableToSendRequest": "Impossible d'envoyer la demande" + "unableToSendRequest": "Impossible d'envoyer la demande", + "pleaseExplainRefusalReason": "Veuillez expliquer la raison de votre décision." }, "orcid": { "associated": "ORCID '{{orcid}}' associé" @@ -2468,4 +2469,4 @@ "title": "Guide d'utilisation", "tooltipClose": "Fermer le guide d'utilisation" } -} \ No newline at end of file +} diff --git a/src/assets/openapi/dlcm-openapi-3.0.json b/src/assets/openapi/dlcm-openapi-3.0.json index e7de5edf40c3c11455959291ad0ce56e413688ec..86bd97925dd872236450a23981fbc27bc58cfefe 100644 --- a/src/assets/openapi/dlcm-openapi-3.0.json +++ b/src/assets/openapi/dlcm-openapi-3.0.json @@ -44128,6 +44128,11 @@ "type": "string", "description": "The message sent by the notification." }, + "responseMessage": { + "maxLength": 4096, + "type": "string", + "description": "Any message sent in response to the notification." + }, "notificationStatus": { "type": "string", "description": "The status of the notification.",