deposit.state.ts 40.1 KB
Newer Older
1
import {
2
3
  HttpClient,
  HttpHeaders,
4
} from "@angular/common/http";
5
import {Injectable} from "@angular/core";
6
import {DataFileUploadHelper} from "@app/features/deposit/helpers/data-file-upload.helper";
7
import {DepositDataFileAction} from "@app/features/deposit/stores/data-file/deposit-data-file.action";
8
import {
9
  defaultDepositDataFileValue,
10
11
12
13
14
15
16
17
18
19
20
  DepositDataFileState,
  DepositDataFileStateModel,
} from "@app/features/deposit/stores/data-file/deposit-data-file.state";
import {
  DepositAction,
  depositActionNameSpace,
} from "@app/features/deposit/stores/deposit.action";
import {
  DepositPeopleState,
  DepositPersonStateModel,
} from "@app/features/deposit/stores/people/deposit-people.state";
21
import {DepositPersonAction} from "@app/features/deposit/stores/people/deposit-person.action";
22
import {ApiEnum} from "@app/shared/enums/api.enum";
Florent Poittevin's avatar
Florent Poittevin committed
23
import {LocalModelAttributeEnum} from "@app/shared/enums/model-attribute.enum";
24
25
26
import {
  DepositRoutesEnum,
  RoutesEnum,
27
  urlSeparator,
28
} from "@app/shared/enums/routes.enum";
29
import {AppAuthorizedOrganizationalUnitState} from "@app/stores/authorized-organizational-unit/app-authorized-organizational-unit.state";
30
import {DepositFormPresentational} from "@deposit/components/presentationals/deposit-form/deposit-form.presentational";
31
import {DepositTabStatusEnum} from "@deposit/enums/deposit-tab-status.enum";
32
import {ModeDepositTabEnum} from "@deposit/enums/mode-deposit-tab.enum";
33
import {DlcmFileUploadWrapper} from "@deposit/models/dlcm-file-upload-wrapper.model";
34
35
36
37
import {
  DepositAipState,
  DepositAipStateModel,
} from "@deposit/stores/aip/deposit-aip.state";
38
39
40
41
42
import {
  defaultDepositAuthorizedOrgUnitValue,
  DepositAuthorizedOrganizationalUnitState,
  DepositAuthorizedOrganizationalUnitStateModel,
} from "@deposit/stores/authorized-organizational-unit/deposit-authorized-organizational-unit.state";
43
44
45
46
47
import {
  defaultDepositCollectionValue,
  DepositCollectionState,
  DepositCollectionStateModel,
} from "@deposit/stores/collection/deposit-collection.state";
48
import {DepositOrganizationalUnitAction} from "@deposit/stores/organizational-unit/deposit-organizational-unit.action";
49
import {
50
  defaultDepositOrganizationalUnitValue,
51
  DepositOrganizationalUnitState,
52
  DepositOrganizationalUnitStateModel,
53
} from "@deposit/stores/organizational-unit/deposit-organizational-unit.state";
54
55
56
57
import {
  DepositStatusHistoryState,
  DepositStatusHistoryStateModel,
} from "@deposit/stores/status-history/deposit-status-history.state";
58
59
60
61
62
import {DepositUploadAction} from "@deposit/stores/upload/deposit-upload.action";
import {
  DepositUploadState,
  DepositUploadStateModel,
} from "@deposit/stores/upload/deposit-upload.state";
Florent POITTEVIN's avatar
Florent POITTEVIN committed
63
import {Enums} from "@enums";
64
import {environment} from "@environments/environment";
65
import {Deposit} from "@models";
Florent Poittevin's avatar
Florent Poittevin committed
66
import {Navigate} from "@ngxs/router-plugin";
67
68
69
import {
  Action,
  Actions,
70
  Selector,
71
72
73
74
  State,
  StateContext,
  Store,
} from "@ngxs/store";
75
import {ApiActionNameEnum} from "@shared/enums/api-action-name.enum";
76
import {ApiResourceNameEnum} from "@shared/enums/api-resource-name.enum";
77
import {StateEnum} from "@shared/enums/state.enum";
78
import {DataFile} from "@shared/models/business/data-file.model";
79
import {FileListModel} from "@shared/models/business/file-list.model";
80
import {LocalStateModel} from "@shared/models/local-state.model";
81
import {SecurityService} from "@shared/services/security.service";
82
import {SharedLanguageAction} from "@shared/stores/language/shared-language.action";
83
84
85
86
87
88
import {ResourceLogoStateModel} from "@shared/stores/resource-logo/resource-logo-state.model";
import {
  defaultResourceLogoStateInitValue,
  ResourceLogoState,
  ResourceLogoStateModeEnum,
} from "@shared/stores/resource-logo/resource-logo.state";
89
import {defaultStatusHistoryInitValue} from "@shared/stores/status-history/status-history.state";
90
91
92
93
import {
  Observable,
  of,
} from "rxjs";
94
95
96
import {
  catchError,
  map,
97
  switchMap,
98
99
  tap,
} from "rxjs/operators";
Florent Poittevin's avatar
Florent Poittevin committed
100
101
import {
  ApiService,
102
  CollectionTyped,
103
  defaultAssociationRemoteStateInitValue,
104
  defaultResourceStateInitValue,
105
  defaultUploadStateInitValue,
106
  DownloadService,
107
  ErrorHelper,
108
  isEmptyArray,
109
  isNonEmptyString,
110
  isNotNullNorUndefined,
Florent Poittevin's avatar
Florent Poittevin committed
111
  isNullOrUndefined,
112
  isTrue,
113
114
  isUndefined,
  MappingObjectUtil,
115
  MARK_AS_TRANSLATABLE,
116
  MemoizedUtil,
117
  NotificationService,
118
  ofSolidifyActionCompleted,
Florent Poittevin's avatar
Florent Poittevin committed
119
  OverrideDefaultAction,
120
  PollingHelper,
121
122
  QueryParameters,
  QueryParametersUtil,
123
124
  Result,
  ResultActionStatusEnum,
125
  SolidifyHttpErrorResponseModel,
126
  SolidifyStateError,
Florent Poittevin's avatar
Florent Poittevin committed
127
  StoreUtil,
128
  StringUtil,
Florent Poittevin's avatar
Florent Poittevin committed
129
} from "solidify-frontend";
130

131
export interface DepositStateModel extends ResourceLogoStateModel<Deposit> {
132
  deposit_authorizedOrganizationalUnit: DepositAuthorizedOrganizationalUnitStateModel;
133
134
  deposit_dataFile: DepositDataFileStateModel;
  deposit_person: DepositPersonStateModel;
135
  deposit_upload: DepositUploadStateModel;
136
  deposit_collection: DepositCollectionStateModel;
137
  deposit_aip: DepositAipStateModel;
138
  deposit_organizationalUnit: DepositOrganizationalUnitStateModel;
Florent Poittevin's avatar
Florent Poittevin committed
139
  isLoadingDataFile: boolean;
140
  deposit_statusHistory: DepositStatusHistoryStateModel;
141
142
143
144
145
146
147
  counterTabInProgress: number | undefined;
  counterTabInValidation: number | undefined;
  counterTabApproved: number | undefined;
  counterTabCompleted: number | undefined;
  counterTabRejected: number | undefined;
  counterTabInError: number | undefined;
  counterTabAll: number | undefined;
148
149
  excludedFileList: FileListModel | undefined;
  ignoredFileList: FileListModel | undefined;
150
  canEdit: boolean;
151
  canCreate: boolean;
152
  formPresentational: DepositFormPresentational | undefined;
153
  depositModeTabEnum: ModeDepositTabEnum;
154
  dataFileLogo: DataFile | undefined;
155
  activeListTabStatus: DepositTabStatusEnum;
156
157
}

158
@Injectable()
159
@State<DepositStateModel>({
160
  name: StateEnum.deposit,
161
  defaults: {
162
    ...defaultResourceLogoStateInitValue(),
163
    deposit_authorizedOrganizationalUnit: defaultDepositAuthorizedOrgUnitValue(),
164
    queryParameters: new QueryParameters(environment.defaultPageSize),
165
    deposit_dataFile: defaultDepositDataFileValue(),
166
    deposit_person: {...defaultAssociationRemoteStateInitValue()},
167
    deposit_upload: {...defaultUploadStateInitValue()},
168
    deposit_collection: defaultDepositCollectionValue(),
169
    deposit_aip: {...defaultResourceStateInitValue()},
170
    deposit_organizationalUnit: defaultDepositOrganizationalUnitValue(),
Florent Poittevin's avatar
Florent Poittevin committed
171
    isLoadingDataFile: false,
172
    deposit_statusHistory: {...defaultStatusHistoryInitValue()},
173
174
175
176
177
178
179
    counterTabInProgress: undefined,
    counterTabInValidation: undefined,
    counterTabApproved: undefined,
    counterTabCompleted: undefined,
    counterTabRejected: undefined,
    counterTabInError: undefined,
    counterTabAll: undefined,
180
181
    excludedFileList: undefined,
    ignoredFileList: undefined,
182
    canEdit: false,
183
    canCreate: false,
184
    formPresentational: undefined,
185
    depositModeTabEnum: ModeDepositTabEnum.UNDEFINED,
186
    dataFileLogo: undefined,
187
    isLoadingLogo: false,
188
    activeListTabStatus: DepositTabStatusEnum.inProgress,
189
  },
190
  children: [
191
    DepositUploadState,
192
    DepositDataFileState,
Alicia.DeDiosFuente's avatar
Alicia.DeDiosFuente committed
193
    DepositPeopleState,
194
    DepositStatusHistoryState,
195
    DepositOrganizationalUnitState,
196
    DepositCollectionState,
197
    DepositAipState,
198
    DepositAuthorizedOrganizationalUnitState,
199
  ],
200
})
201
export class DepositState extends ResourceLogoState<DepositStateModel, Deposit> {
202
203
204
  private readonly _ZIP_EXTENSION: string = ".zip";
  private readonly _DEPOSIT_FILE_DOWNLOAD_PREFIX: string = "deposit_";
  private readonly _FOLDER_QUERY_PARAM: string = "folder";
205
  private readonly _DEPOSIT_REJECT_REASON: string = "reason";
206

207
  constructor(protected apiService: ApiService,
208
              protected store: Store,
209
              protected notificationService: NotificationService,
210
              protected actions$: Actions,
Homada.Boumedane's avatar
Homada.Boumedane committed
211
              protected readonly _securityService: SecurityService,
212
213
              private downloadService: DownloadService,
              protected _httpClient: HttpClient) {
214
215
    super(apiService, store, notificationService, actions$, {
      nameSpace: depositActionNameSpace,
216
      routeRedirectUrlAfterSuccessDeleteAction: RoutesEnum.deposit,
217
218
219
      notificationResourceCreateSuccessTextToTranslate: MARK_AS_TRANSLATABLE("deposit.notification.resource.create"),
      notificationResourceDeleteSuccessTextToTranslate: MARK_AS_TRANSLATABLE("deposit.notification.resource.delete"),
      notificationResourceUpdateSuccessTextToTranslate: MARK_AS_TRANSLATABLE("deposit.notification.resource.update"),
220
      keepCurrentStateAfterUpdate: true,
221
      keepCurrentStateBeforeCreate: true,
222
    }, _httpClient, ResourceLogoStateModeEnum.dataset);
Florent Poittevin's avatar
Florent Poittevin committed
223
224
  }

225
  protected get _urlResource(): string {
226
    return ApiEnum.preIngestDeposits;
227
228
  }

229
230
231
232
  protected get _urlLogoResource(): string {
    return this._urlResource;
  }

233
234
235
236
  @Selector()
  static isLoading(state: DepositStateModel): boolean {
    return StoreUtil.isLoadingState(state);
  }
237

238
239
  @Selector()
  static currentOrgUnitName(state: DepositStateModel): string | undefined {
240
    if (isNullOrUndefined(state.deposit_organizationalUnit) || isNullOrUndefined(state.deposit_organizationalUnit.current)) {
241
242
      return undefined;
    }
243
    return state.deposit_organizationalUnit.current.name;
244
245
  }

246
247
248
249
250
251
252
253
  @Selector()
  static currentTitle(state: DepositStateModel): string | undefined {
    if (isNullOrUndefined(state.current)) {
      return undefined;
    }
    return state.current.title;
  }

254
255
256
  @Selector()
  static isLoadingWithDependency(state: DepositStateModel): boolean {
    return this.isLoading(state)
257
      || StoreUtil.isLoadingState(state.deposit_person);
258
259
  }

260
261
262
263
264
  @Selector()
  static isReadyToBeDisplayed(state: DepositStateModel): boolean {
    return this.isReadyToBeDisplayedInCreateMode
      && !isNullOrUndefined(state.current)
      && !isNullOrUndefined(state.deposit_person.selected);
Florent Poittevin's avatar
Florent Poittevin committed
265
266
  }

267
268
  @Selector()
  static isReadyToBeDisplayedInCreateMode(state: DepositStateModel): boolean {
269
    return !isNullOrUndefined(state.deposit_organizationalUnit) && !isNullOrUndefined(state.deposit_organizationalUnit.current)
270
      && state.deposit_organizationalUnit.deposit_organizationalUnit_additionalFieldsForm.loaded;
271
272
273
  }

  @Action(DepositAction.LoadResource)
274
  loadResource(ctx: StateContext<DepositStateModel>, action: DepositAction.LoadResource): Observable<boolean> {
Florent Poittevin's avatar
Florent Poittevin committed
275
    ctx.patchState({
276
277
278
      isLoadingCounter: ctx.getState().isLoadingCounter + 1,
    });

279
    return StoreUtil.dispatchParallelActionAndWaitForSubActionsCompletion(ctx, [
280
      {
281
        action: new SharedLanguageAction.GetAll(null, false, false),
282
        subActionCompletions: [
283
284
          this.actions$.pipe(ofSolidifyActionCompleted(SharedLanguageAction.GetAllSuccess)),
          this.actions$.pipe(ofSolidifyActionCompleted(SharedLanguageAction.GetAllFail)),
285
286
        ],
      },
287
    ]).pipe(
288
289
      map(result => {
        if (result.success) {
290
291
292
293
          ctx.dispatch(new DepositAction.LoadResourceSuccess(action));
        } else {
          ctx.dispatch(new DepositAction.LoadResourceFail(action));
        }
294
        return result.success;
295
296
      }),
    );
Florent Poittevin's avatar
Florent Poittevin committed
297
298
  }

299
300
  @Action(DepositAction.SetOrganizationalUnit)
  setOrganizationalUnit(ctx: StateContext<DepositStateModel>, action: DepositAction.SetOrganizationalUnit): void {
301
    const listAuthorizedOrgUnit = MemoizedUtil.listSnapshot(this.store, AppAuthorizedOrganizationalUnitState);
302
303
304
    if (isNullOrUndefined(listAuthorizedOrgUnit) || isEmptyArray(listAuthorizedOrgUnit)) {
      return;
    }
305
306
    const canCreate = this._securityService.canCreateDepositOnOrgUnit(action.organizationalUnitId);
    ctx.dispatch(new DepositAction.CanCreate(canCreate));
307
308
    const orgUnit = listAuthorizedOrgUnit.find(o => o.resId === action.organizationalUnitId);
    ctx.patchState({
309
310
311
312
313
314
315
      counterTabInProgress: undefined,
      counterTabInValidation: undefined,
      counterTabApproved: undefined,
      counterTabCompleted: undefined,
      counterTabRejected: undefined,
      counterTabInError: undefined,
      counterTabAll: undefined,
316
    });
317
    ctx.dispatch(new DepositOrganizationalUnitAction.GetByIdSuccess(action as any, orgUnit));
318
319
  }

320
321
322
323
324
325
326
327
328
329
330
331
332
333
  @OverrideDefaultAction()
  @Action(DepositAction.GetAllSuccess)
  getAllSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.GetAllSuccess): void {
    super.getAllSuccess(ctx, action);
    const searchItem = QueryParametersUtil.getSearchItems(ctx.getState().queryParameters);
    if (isNullOrUndefined(searchItem)) {
      return;
    }
    const statusKey: keyof Deposit = "status";
    const organizationalUnitIdKey: keyof Deposit = "organizationalUnitId";
    const numberInput = MappingObjectUtil.size(searchItem);
    const hasOrgUnitId = MappingObjectUtil.has(searchItem, organizationalUnitIdKey);
    const hasStatus = MappingObjectUtil.has(searchItem, statusKey);
    if ((numberInput === 2 && hasOrgUnitId && hasStatus) || (numberInput === 1 && hasOrgUnitId)) {
Florent POITTEVIN's avatar
Florent POITTEVIN committed
334
      let status: Enums.Deposit.StatusEnum | undefined = undefined;
335
      if (hasStatus) {
Florent POITTEVIN's avatar
Florent POITTEVIN committed
336
        status = MappingObjectUtil.get(searchItem, statusKey) as Enums.Deposit.StatusEnum;
337
338
339
340
341
      }
      ctx.dispatch(new DepositAction.RefreshCounterStatusTabSuccess(null, status, action.list._page.totalItems));
    }
  }

Florent Poittevin's avatar
Florent Poittevin committed
342
343
  @OverrideDefaultAction()
  @Action(DepositAction.Create)
344
  create(ctx: StateContext<DepositStateModel>, action: DepositAction.Create): Observable<Deposit> {
Florent Poittevin's avatar
Florent Poittevin committed
345
346
347
    return super.internalCreate(ctx, action)
      .pipe(
        tap(model => {
348
349
350
351
352
353
354
355
          this.updateSubResource(model, action, ctx)
            .subscribe(success => {
              if (success) {
                ctx.dispatch(new DepositAction.CreateSuccess(action, model));
              } else {
                ctx.dispatch(new DepositAction.CreateFail(action));
              }
            });
Florent Poittevin's avatar
Florent Poittevin committed
356
357
358
359
        }),
      );
  }

360
361
362
363
364
  @OverrideDefaultAction()
  @Action(DepositAction.CreateSuccess)
  createSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.CreateSuccess): void {
    super.createSuccess(ctx, action);
    ctx.dispatch([
365
      new Navigate([RoutesEnum.deposit, action.model.organizationalUnitId, DepositRoutesEnum.detail, action.model.resId, DepositRoutesEnum.data]),
366
367
368
369
      new DepositAction.SetOrganizationalUnit(action.model.organizationalUnitId),
    ]);
  }

Florent Poittevin's avatar
Florent Poittevin committed
370
371
  @OverrideDefaultAction()
  @Action(DepositAction.Update)
372
  update(ctx: StateContext<DepositStateModel>, action: DepositAction.Update): Observable<Deposit> {
373
    action.modelFormControlEvent.model.status = Enums.Deposit.StatusEnum.IN_PROGRESS;
Florent Poittevin's avatar
Florent Poittevin committed
374
375
376
    return super.internalUpdate(ctx, action)
      .pipe(
        tap(model => {
377
378
379
380
381
382
383
384
          this.updateSubResource(model, action, ctx)
            .subscribe(success => {
              if (success) {
                ctx.dispatch(new DepositAction.UpdateSuccess(action, model));
              } else {
                ctx.dispatch(new DepositAction.UpdateFail(action));
              }
            });
Florent Poittevin's avatar
Florent Poittevin committed
385
386
387
388
        }),
      );
  }

389
390
391
392
393
394
  @OverrideDefaultAction()
  @Action(DepositAction.UpdateSuccess)
  updateSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.UpdateSuccess): void {
    super.updateSuccess(ctx, action);

    const depositId = action.model.resId;
395
396
    ctx.dispatch([
      new DepositPersonAction.GetAll(depositId),
397
      new Navigate([RoutesEnum.deposit, action.model.organizationalUnitId, DepositRoutesEnum.detail, action.model.resId, DepositRoutesEnum.metadata]),
398
399
      new DepositAction.SetOrganizationalUnit(action.model.organizationalUnitId),
    ]);
400
401
  }

402
  private updateSubResource(model: Deposit, action: DepositAction.Create | DepositAction.Update, ctx: StateContext<DepositStateModel>): Observable<boolean> {
403
    const depositId = model.resId;
404
405
    const selectedPerson = ctx.getState().deposit_person.selected;
    const oldAuthors: string[] = isNullOrUndefined(selectedPerson) ? [] : selectedPerson.map(p => p.resId);
406
    const newAuthors: string[] = action.modelFormControlEvent.model[LocalModelAttributeEnum.authors];
407
408
409
410
411
    // Need to delete before submit new authors to update order
    return StoreUtil.dispatchSequentialActionAndWaitForSubActionsCompletion(ctx, [
      {
        action: new DepositPersonAction.DeleteList(depositId, oldAuthors),
        subActionCompletions: [
412
413
          this.actions$.pipe(ofSolidifyActionCompleted(DepositPersonAction.DeleteListSuccess)),
          this.actions$.pipe(ofSolidifyActionCompleted(DepositPersonAction.DeleteListFail)),
414
415
416
417
418
        ],
      },
      {
        action: new DepositPersonAction.Create(depositId, newAuthors),
        subActionCompletions: [
419
420
          this.actions$.pipe(ofSolidifyActionCompleted(DepositPersonAction.CreateSuccess)),
          this.actions$.pipe(ofSolidifyActionCompleted(DepositPersonAction.CreateFail)),
421
422
        ],
      },
423
424
425
    ]).pipe(
      map(result => result.success),
    );
426
427
  }

428
  @Action(DepositAction.Submit)
429
  submit(ctx: StateContext<DepositStateModel>, action: DepositAction.Submit): Observable<Result> {
430
    ctx.dispatch(new Navigate([RoutesEnum.deposit, ctx.getState().deposit_organizationalUnit.current.resId, DepositRoutesEnum.detail, action.deposit.resId]));
431

432
433
434
435
436
437
438
439
440
    let submissionWithApproval = false;
    let submissionPolicy = action.deposit.submissionPolicy;
    if (isNullOrUndefined(submissionPolicy)) {
      submissionPolicy = action.deposit.organizationalUnit.defaultSubmissionPolicy;
    }
    if (!isNullOrUndefined(submissionPolicy)) {
      submissionWithApproval = submissionPolicy.submissionApproval;
    }

441
    let suffix = ApiActionNameEnum.APPROVE;
442
    if (isTrue(submissionWithApproval)) {
443
      suffix = ApiActionNameEnum.SUBMIT_FOR_APPROVAL;
444
    }
445
    return this.apiService.post<any, Result>(this._urlResource + urlSeparator + action.deposit.resId + urlSeparator + suffix)
446
      .pipe(
447
        tap(result => {
448
          if (result?.status === ResultActionStatusEnum.EXECUTED) {
449
450
451
452
453
            ctx.dispatch(new DepositAction.SubmitSuccess(result));
          } else {
            ctx.dispatch(new DepositAction.SubmitFail());
          }
        }),
454
        catchError((error: SolidifyHttpErrorResponseModel) => {
455
          ctx.dispatch(new DepositAction.SubmitFail(ErrorHelper.extractValidationErrors(error)));
456
          throw new SolidifyStateError(this, error);
457
458
        }),
      );
Florent Poittevin's avatar
Florent Poittevin committed
459
460
461
462
  }

  @Action(DepositAction.SubmitSuccess)
  submitSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.SubmitSuccess): void {
463
    this.redirectToDepositDetail(ctx, action.result.resId);
464
    this.notificationService.showSuccess(MARK_AS_TRANSLATABLE("deposit.notification.submit.success"));
Florent Poittevin's avatar
Florent Poittevin committed
465
466
467
468
  }

  @Action(DepositAction.SubmitFail)
  submitFail(ctx: StateContext<DepositStateModel>, action: DepositAction.SubmitFail): void {
469
470
471
472
473
474
475
476
477
478
479
480
481
    let errorsMessage = StringUtil.stringEmpty;
    if (isNotNullNorUndefined(action.listValidationError)) {
      const errorMessageSeparator = ". ";
      action.listValidationError.forEach(validationError => {
        if (isNonEmptyString(errorsMessage)) {
          errorsMessage = errorMessageSeparator;
        }
        if (isNotNullNorUndefined(validationError.errorMessages)) {
          errorsMessage = errorsMessage + validationError.errorMessages.join(errorMessageSeparator);
        }
      });
    }
    this.notificationService.showError(MARK_AS_TRANSLATABLE("deposit.notification.submit.fail"), {errors: errorsMessage});
482
483
  }

484
  @Action(DepositAction.Approve)
485
  approve(ctx: StateContext<DepositStateModel>, action: DepositAction.Approve): Observable<Result> {
486
    return this.apiService.post<any, Result>(this._urlResource + urlSeparator + action.deposit.resId + urlSeparator + ApiActionNameEnum.APPROVE)
487
      .pipe(
488
        tap(result => {
489
          if (result?.status === ResultActionStatusEnum.EXECUTED) {
490
491
492
493
494
            ctx.dispatch(new DepositAction.ApproveSuccess(result));
          } else {
            ctx.dispatch(new DepositAction.ApproveFail());
          }
        }),
495
        catchError((error: SolidifyHttpErrorResponseModel) => {
496
          ctx.dispatch(new DepositAction.ApproveFail());
497
          throw new SolidifyStateError(this, error);
498
499
        }),
      );
500
501
502
503
  }

  @Action(DepositAction.ApproveSuccess)
  approveSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.ApproveSuccess): void {
504
    this.redirectToDepositDetail(ctx, action.result.resId);
505
    this.notificationService.showSuccess(MARK_AS_TRANSLATABLE("deposit.notification.approve.success"));
506
507
508
509
  }

  @Action(DepositAction.ApproveFail)
  approveFail(ctx: StateContext<DepositStateModel>, action: DepositAction.ApproveFail): void {
510
    this.notificationService.showError(MARK_AS_TRANSLATABLE("deposit.notification.approve.fail"));
511
512
513
  }

  @Action(DepositAction.Reject)
514
  reject(ctx: StateContext<DepositStateModel>, action: DepositAction.Reject): Observable<Result> {
515
    return this.apiService.post<any, Result>(`${this._urlResource + urlSeparator + action.deposit.resId + urlSeparator + ApiActionNameEnum.REJECT}?${this._DEPOSIT_REJECT_REASON}=${action.message}`)
516
      .pipe(
517
        tap(result => {
518
          if (result?.status === ResultActionStatusEnum.EXECUTED) {
519
520
521
522
523
            ctx.dispatch(new DepositAction.RejectSuccess(result));
          } else {
            ctx.dispatch(new DepositAction.RejectFail());
          }
        }),
524
        catchError((error: SolidifyHttpErrorResponseModel) => {
525
526
527
528
          ctx.dispatch(new DepositAction.RejectFail());
          throw new SolidifyStateError(this, error);
        }),
      );
529
530
531
532
  }

  @Action(DepositAction.RejectSuccess)
  rejectSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.RejectSuccess): void {
533
    this.redirectToDepositDetail(ctx, action.result.resId);
534
    this.notificationService.showSuccess(MARK_AS_TRANSLATABLE("deposit.notification.reject.success"));
535
536
537
538
  }

  @Action(DepositAction.RejectFail)
  rejectFail(ctx: StateContext<DepositStateModel>, action: DepositAction.RejectFail): void {
539
    this.notificationService.showError(MARK_AS_TRANSLATABLE("deposit.notification.reject.fail"));
540
541
542
  }

  @Action(DepositAction.BackToEdit)
543
  backToEdit(ctx: StateContext<DepositStateModel>, action: DepositAction.BackToEdit): Observable<Result> {
544
    return this.apiService.post<Result>(this._urlResource + urlSeparator + action.deposit.resId + urlSeparator + ApiActionNameEnum.ENABLE_REVISION)
545
      .pipe(
546
        tap(result => ctx.dispatch(new DepositAction.BackToEditSuccess(result))),
547
        catchError((error: SolidifyHttpErrorResponseModel) => {
548
          ctx.dispatch(new DepositAction.BackToEditFail());
549
          throw new SolidifyStateError(this, error);
550
551
        }),
      );
552
553
554
555
  }

  @Action(DepositAction.BackToEditSuccess)
  backToEditSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.BackToEditSuccess): void {
556
    this.redirectToDepositDetail(ctx, action.result.resId);
557
    this.notificationService.showSuccess(MARK_AS_TRANSLATABLE("deposit.notification.backToEdit.success"));
558
559
560
561
  }

  @Action(DepositAction.BackToEditFail)
  backToEditFail(ctx: StateContext<DepositStateModel>, action: DepositAction.BackToEditFail): void {
562
    this.notificationService.showError(MARK_AS_TRANSLATABLE("deposit.notification.backToEdit.fail"));
563
564
  }

Alicia.DeDiosFuente's avatar
Alicia.DeDiosFuente committed
565
  @Action(DepositAction.ReserveDOI)
566
  reserveDOI(ctx: StateContext<DepositStateModel>, action: DepositAction.ReserveDOI): Observable<Deposit> {
567
568
569
    ctx.patchState({
      isLoadingCounter: ctx.getState().isLoadingCounter + 1,
    });
570
    return this.apiService.post(this._urlResource + urlSeparator + action.deposit.resId + urlSeparator + ApiActionNameEnum.RESERVE_DOI)
Alicia.DeDiosFuente's avatar
Alicia.DeDiosFuente committed
571
572
      .pipe(
        tap(deposit => ctx.dispatch(new DepositAction.ReserveDOISuccess(deposit))),
573
        catchError((error: SolidifyHttpErrorResponseModel) => {
Alicia.DeDiosFuente's avatar
Alicia.DeDiosFuente committed
574
575
576
577
578
579
580
581
          ctx.dispatch(new DepositAction.ReserveDOIFail());
          throw new SolidifyStateError(this, error);
        }),
      );
  }

  @Action(DepositAction.ReserveDOISuccess)
  reserveDOISuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.ReserveDOISuccess): void {
582
    // We want to explicitly set to undefined before set the new value
583
584
585
586
587
588
589
    ctx.patchState({
      current: undefined,
    });
    ctx.patchState({
      current: action.deposit,
      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
    });
590
    this.notificationService.showSuccess(MARK_AS_TRANSLATABLE("deposit.notification.reserveDOI.success"));
Alicia.DeDiosFuente's avatar
Alicia.DeDiosFuente committed
591
592
593
594
  }

  @Action(DepositAction.ReserveDOIFail)
  reserveDOIFail(ctx: StateContext<DepositStateModel>, action: DepositAction.ReserveDOIFail): void {
595
596
597
    ctx.patchState({
      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
    });
598
    this.notificationService.showError(MARK_AS_TRANSLATABLE("deposit.notification.reserveDOI.fail"));
Alicia.DeDiosFuente's avatar
Alicia.DeDiosFuente committed
599
  }
600
601

  @Action(DepositAction.RefreshAllCounterStatusTab)
602
  refreshAllCounterStatusTab(ctx: StateContext<DepositStateModel>, action: DepositAction.RefreshAllCounterStatusTab): Observable<boolean> {
603
604
605
606
607
    const listActionsWrappper = [];
    action.list.forEach(status => {
      listActionsWrappper.push({
        action: new DepositAction.RefreshCounterStatusTab(status),
        subActionCompletions: [
608
609
          this.actions$.pipe(ofSolidifyActionCompleted(DepositAction.RefreshCounterStatusTabSuccess)),
          this.actions$.pipe(ofSolidifyActionCompleted(DepositAction.RefreshCounterStatusTabFail)),
610
611
612
613
        ],
      });
    });

614
615
    return StoreUtil.dispatchParallelActionAndWaitForSubActionsCompletion(ctx, listActionsWrappper)
      .pipe(
616
617
        map(result => {
          if (result.success) {
618
619
620
621
            ctx.dispatch(new DepositAction.RefreshAllCounterStatusTabSuccess(action));
          } else {
            ctx.dispatch(new DepositAction.RefreshAllCounterStatusTabFail(action));
          }
622
          return result.success;
623
624
        }),
      );
625
626
627
628
  }

  @Action(DepositAction.RefreshCounterStatusTab)
  refreshCounterStatusTab(ctx: StateContext<DepositStateModel>, action: DepositAction.RefreshCounterStatusTab): Observable<number> {
629
    const queryParameters = new QueryParameters(environment.minimalPageSizeToRetrievePaginationInfo);
630
    const searchItems = QueryParametersUtil.getSearchItems(queryParameters);
631
    if (!isNullOrUndefined(ctx.getState().deposit_organizationalUnit) && !isNullOrUndefined(ctx.getState().deposit_organizationalUnit.current)) {
632
      MappingObjectUtil.set(searchItems, "organizationalUnitId", ctx.getState().deposit_organizationalUnit.current.resId);
633
634
635
636
637
638
639
640
641
642
643
    }
    if (!isUndefined(action.status)) {
      MappingObjectUtil.set(searchItems, "status", action.status);
    }
    return this.apiService.get<Deposit>(this._urlResource, queryParameters)
      .pipe(
        map((collection: CollectionTyped<Deposit>) => {
          const totalItem = collection._page.totalItems;
          ctx.dispatch(new DepositAction.RefreshCounterStatusTabSuccess(action, action.status, totalItem));
          return totalItem;
        }),
644
        catchError((error: SolidifyHttpErrorResponseModel) => {
645
646
647
648
649
650
651
652
653
654
          ctx.dispatch(new DepositAction.RefreshCounterStatusTabFail(action));
          throw new SolidifyStateError(this, error);
        }),
      );
  }

  @Action(DepositAction.RefreshCounterStatusTabSuccess)
  refreshCounterStatusTabSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.RefreshCounterStatusTabSuccess): void {
    let key: keyof DepositStateModel | undefined = undefined;
    switch (action.status) {
655
      case Enums.Deposit.StatusEnum.IN_PROGRESS:
656
657
        key = "counterTabInProgress";
        break;
658
      case Enums.Deposit.StatusEnum.IN_VALIDATION:
659
660
        key = "counterTabInValidation";
        break;
Florent POITTEVIN's avatar
Florent POITTEVIN committed
661
      case Enums.Deposit.StatusEnum.APPROVED:
662
663
        key = "counterTabApproved";
        break;
Florent POITTEVIN's avatar
Florent POITTEVIN committed
664
      case Enums.Deposit.StatusEnum.COMPLETED:
665
666
        key = "counterTabCompleted";
        break;
Florent POITTEVIN's avatar
Florent POITTEVIN committed
667
      case Enums.Deposit.StatusEnum.REJECTED:
668
669
        key = "counterTabRejected";
        break;
670
      case Enums.Deposit.StatusEnum.IN_ERROR:
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
        key = "counterTabInError";
        break;
      case undefined:
        key = "counterTabAll";
        break;
    }

    ctx.patchState({
      [key]: action.counter,
    });
  }

  @Action(DepositAction.RefreshCounterStatusTabFail)
  refreshCounterStatusTabFail(ctx: StateContext<DepositStateModel>, action: DepositAction.RefreshCounterStatusTabFail): void {
  }
686
687
688
689

  @OverrideDefaultAction()
  @Action(DepositAction.Clean)
  clean(ctx: StateContext<DepositStateModel>, action: DepositAction.Clean): void {
690
    const canCreate = ctx.getState().canCreate;
691
    const depositOrganizationalUnitStateModel = ctx.getState().deposit_organizationalUnit;
692
693
    const ignoredFileList = ctx.getState().ignoredFileList;
    const excludedFileList = ctx.getState().excludedFileList;
694
    const isLoadingCounter = ctx.getState().isLoadingCounter;
695
696
    super.clean(ctx, action);
    ctx.patchState({
697
      canCreate,
698
      deposit_organizationalUnit: depositOrganizationalUnitStateModel,
699
700
      ignoredFileList,
      excludedFileList,
701
      isLoadingCounter,
702
703
    });
  }
704
705

  private redirectToDepositDetail(ctx: StateContext<DepositStateModel>, depositId: string): void {
706
    ctx.dispatch(new Navigate([RoutesEnum.deposit, ctx.getState().deposit_organizationalUnit.current.resId, DepositRoutesEnum.detail, depositId]));
707
    ctx.dispatch(new DepositAction.GetById(depositId));
708
  }
709
710
711
712
713
714
715

  @Action(DepositAction.ChangeEditProperty)
  changeEditProperty(ctx: StateContext<DepositStateModel>, action: DepositAction.ChangeEditProperty): void {
    ctx.patchState({
      canEdit: action.isEditable,
    });
  }
716
717
718
719
720
721

  @Action(DepositAction.GetExcludedListFiles)
  getExcludedListFiles(ctx: StateContext<DepositStateModel>, action: DepositAction.GetExcludedListFiles): Observable<FileListModel> {
    ctx.patchState({
      isLoadingCounter: ctx.getState().isLoadingCounter + 1,
    });
722
    return this.apiService.getByIdInPath<FileListModel>(this._urlResource + urlSeparator + ApiActionNameEnum.LIST_EXCLUDE_FILES)
723
724
      .pipe(
        tap(fileList => ctx.dispatch(new DepositAction.GetExcludedListFilesSuccess(action, fileList))),
725
        catchError((error: SolidifyHttpErrorResponseModel) => {
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
          ctx.dispatch(new DepositAction.GetExcludedListFilesFail(action));
          throw new SolidifyStateError(this, error);
        }),
      );
  }

  @Action(DepositAction.GetExcludedListFilesSuccess)
  getExcludedListFilesSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.GetExcludedListFilesSuccess): void {
    ctx.patchState({
      excludedFileList: action.fileList,
      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
    });
  }

  @Action(DepositAction.GetExcludedListFilesFail)
  getExcludedListFilesFail(ctx: StateContext<DepositStateModel>, action: DepositAction.GetExcludedListFilesFail): void {
    ctx.patchState({
      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
    });
  }

  @Action(DepositAction.GetIgnoredListFiles)
  getIgnoredListFiles(ctx: StateContext<DepositStateModel>, action: DepositAction.GetIgnoredListFiles): Observable<FileListModel> {
    ctx.patchState({
      isLoadingCounter: ctx.getState().isLoadingCounter + 1,
    });
752
    return this.apiService.getByIdInPath<FileListModel>(this._urlResource + urlSeparator + ApiActionNameEnum.LIST_IGNORE_FILES)
753
754
      .pipe(
        tap(fileList => ctx.dispatch(new DepositAction.GetIgnoredListFilesSuccess(action, fileList))),
755
        catchError((error: SolidifyHttpErrorResponseModel) => {
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
          ctx.dispatch(new DepositAction.GetIgnoredListFilesFail(action));
          throw new SolidifyStateError(this, error);
        }),
      );
  }

  @Action(DepositAction.GetIgnoredListFilesSuccess)
  getIgnoredListFilesSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.GetIgnoredListFilesSuccess): void {
    ctx.patchState({
      ignoredFileList: action.fileList,
      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
    });
  }

  @Action(DepositAction.GetIgnoredListFilesFail)
  getIgnoredListFilesFail(ctx: StateContext<DepositStateModel>, action: DepositAction.GetIgnoredListFilesFail): void {
    ctx.patchState({
      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
    });
  }
776
777
778
779
780
781
782

  @Action(DepositAction.CanCreate)
  canCreate(ctx: StateContext<DepositStateModel>, action: DepositAction.CanCreate): void {
    ctx.patchState({
      canCreate: action.canCreate,
    });
  }
Homada.Boumedane's avatar
Homada.Boumedane committed
783
784

  @Action(DepositAction.Download)
785
  download(ctx: StateContext<DepositStateModel>, action: DepositAction.Download): void {
786
    let fileName = this._DEPOSIT_FILE_DOWNLOAD_PREFIX + action.id;
787
    let url = `${this._urlResource}/${action.id}/${ApiActionNameEnum.DL}`;
788
789
790
    if (isNotNullNorUndefined(action.fullFolderName)) {
      url = url + `?${this._FOLDER_QUERY_PARAM}=${action.fullFolderName}`;
      fileName = fileName + action.fullFolderName;
791
    }
792
    this.downloadService.download(url, fileName + this._ZIP_EXTENSION);
793
  }
794
795
796

  @Action(DepositAction.ComputeModeTab)
  computeModeTab(ctx: StateContext<DepositStateModel>, action: DepositAction.ComputeModeTab): void {
797
    let mode: ModeDepositTabEnum;
798

799
800
801
    const url = this.store.selectSnapshot((s: LocalStateModel) => s.router.state.url);
    const currentDisplay = url.substring(url.lastIndexOf(urlSeparator) + 1, url.length);
    const baseUrl = url.substring(0, url.lastIndexOf(urlSeparator));
802
    const numberCollection = ctx.getState()?.deposit_collection?.numberCollections;
803
    const numberFiles = DataFileUploadHelper.numberFiles(this.store);
804
805

    let redirectionRouteSuffix = undefined;
806
807
    if (numberCollection > 0) {
      mode = ModeDepositTabEnum.COLLECTION;
808
809
810
      if (currentDisplay === DepositRoutesEnum.data || currentDisplay === DepositRoutesEnum.files) {
        redirectionRouteSuffix = DepositRoutesEnum.collections;
      }
811
812
    } else if (numberFiles > 0) {
      mode = ModeDepositTabEnum.FILE;
813
814
815
816
817
818
819
820
      if (currentDisplay === DepositRoutesEnum.data || currentDisplay === DepositRoutesEnum.collections) {
        redirectionRouteSuffix = DepositRoutesEnum.files;
      }
    } else {
      mode = ModeDepositTabEnum.FILE_OR_COLLECTION;
      if (currentDisplay === DepositRoutesEnum.files || currentDisplay === DepositRoutesEnum.collections) {
        redirectionRouteSuffix = DepositRoutesEnum.data;
      }
821
822
823
824
    }
    ctx.patchState({
      depositModeTabEnum: mode,
    });
825
826
827
    if (isNotNullNorUndefined(redirectionRouteSuffix)) {
      ctx.dispatch(new Navigate([baseUrl, redirectionRouteSuffix]));
    }
828
  }
829

830
831
832
833
834
835
836
  @Action(DepositAction.SetActiveListTabStatus)
  setActiveListTabStatus(ctx: StateContext<DepositStateModel>, action: DepositAction.SetActiveListTabStatus): void {
    ctx.patchState({
      activeListTabStatus: action.activeListTabStatus,
    });
  }

837
838
839
840
  @OverrideDefaultAction()
  @Action(DepositAction.UploadPhoto)
  uploadPhoto(ctx: StateContext<DepositStateModel>, action: DepositAction.UploadPhoto): Observable<any> {
    if (isNotNullNorUndefined(ctx.getState().dataFileLogo)) {
841
      ctx.dispatch(new DepositAction.DeletePhoto(action.resId, action.file));
842
      return this.actions$.pipe(
843
        ofSolidifyActionCompleted(DepositAction.DeletePhotoSuccess),
844
845
846
847
848
849
850
851
852
853
854
855
856
        switchMap(result => {
          if (isTrue(result.result.successful)) {
            return this._uploadPhotoReady(ctx, action);
          }
          return of(false);
        }));
    } else {
      return this._uploadPhotoReady(ctx, action);
    }
  }

  private _uploadPhotoReady(ctx: StateContext<DepositStateModel>, action: DepositAction.UploadPhoto): Observable<any> {
    const parentId = ctx.getState().current?.resId;
857
    const actionUploadDataFile = new DepositUploadAction.UploadFile(parentId, {
858
859
860
861
862
      dataCategory: Enums.DataFile.DataCategoryEnum.Internal,
      dataType: Enums.DataFile.DataTypeEnum.DatasetThumbnail,
      metadataType: undefined,
      subDirectory: undefined,
      file: action.file,
863
    } as DlcmFileUploadWrapper);
864

865
    this.subscribe(StoreUtil.waitForSubActionCompletion(this.actions$,
866
      actionUploadDataFile,
867
      DepositUploadAction.UploadFileSuccess,
868
      resultAction => {
869
        const actionUploadDatafileSuccess = resultAction.solidifyFile;
Florent POITTEVIN's avatar