From ec079035624652218a783ef4010783ed489bfc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Thu, 29 Apr 2021 01:00:26 +0300 Subject: [PATCH] Handle invalid project templates --- apps/red-ui/src/app/app.module.ts | 14 ----- .../add-edit-rule-set-dialog.component.html | 48 ++++++++------- .../add-edit-rule-set-dialog.component.scss | 30 ++++++---- .../add-edit-rule-set-dialog.component.ts | 59 ++++++++++++++++--- .../screens/audit/audit-screen.component.html | 2 +- .../screens/audit/audit-screen.component.ts | 16 ++--- .../project-details.component.scss | 4 -- .../add-edit-project-dialog.component.html | 2 +- .../add-edit-project-dialog.component.ts | 19 +++++- .../src/app/modules/shared/shared.module.ts | 18 +++++- .../red-ui/src/app/utils/date-inputs-utils.ts | 19 ++++++ apps/red-ui/src/assets/i18n/en.json | 1 + .../src/assets/styles/red-page-layout.scss | 4 ++ 13 files changed, 160 insertions(+), 76 deletions(-) create mode 100644 apps/red-ui/src/app/utils/date-inputs-utils.ts diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index daf5d79f8..34210459f 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -11,14 +11,12 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { languageInitializer } from './i18n/language.initializer'; import { LanguageService } from './i18n/language.service'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { ToastrModule } from 'ngx-toastr'; import { ServiceWorkerModule } from '@angular/service-worker'; import { environment } from '../environments/environment'; import { AuthModule } from './modules/auth/auth.module'; import { LogoComponent } from './components/logo/logo.component'; import { AuthErrorComponent } from './components/auth-error/auth-error.component'; -import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core'; import { ToastComponent } from './components/toast/toast.component'; import { HttpCacheInterceptor } from '@redaction/red-cache'; import { NotificationsComponent } from './components/notifications/notifications.component'; @@ -97,18 +95,6 @@ const components = [AppComponent, LogoComponent, AuthErrorComponent, ToastCompon multi: true, useFactory: languageInitializer, deps: [LanguageService] - }, - { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }, - { - provide: MAT_DATE_FORMATS, - useValue: { - display: { - dateInput: 'DD/MM/YY', - monthYearLabel: 'YYYY', - dateA11yLabel: 'LL', - monthYearA11yLabel: 'YYYY' - } - } } ], bootstrap: [AppComponent] diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.html b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.html index c9b57b083..91395a925 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.html +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.html @@ -43,30 +43,38 @@ > -
- - {{ 'project-listing.add-edit-dialog.form.due-date' | translate }} - +
+
+ + {{ 'add-edit-project-template.form.valid-from' | translate }} + - -
- - - - - + + {{ 'add-edit-project-template.form.valid-to' | translate }} + +
+ +
+
+ + + + + + +
- to - -
- - - - - +
+ + + + + + +
- +
diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.scss b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.scss index f8dcfe010..3ec9509e9 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.scss +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.scss @@ -1,19 +1,23 @@ -.valid-from { - margin-top: 16px; - min-height: 34px; +.validity { + width: 230px; display: flex; - flex-direction: row; - align-items: center; - mat-checkbox { - width: fit-content; - } + > div { + display: flex; + flex-direction: column; + margin-top: 16px; - .mr-16 { - margin-right: 16px; - } + mat-checkbox { + margin-right: 16px; + height: 100%; + align-items: center; + display: flex; + min-height: 42px; + } - .ml-16 { - margin-left: 16px; + .red-input-group { + min-height: 42px; + justify-content: center; + } } } diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.ts index 6304692e9..f84e82893 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.ts @@ -3,7 +3,9 @@ import { AppStateService } from '../../../../state/app-state.service'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import * as moment from 'moment'; +import { Moment } from 'moment'; import { RuleSetControllerService, RuleSetModel } from '@redaction/red-ui-http'; +import { applyIntervalConstraints } from '../../../../utils/date-inputs-utils'; @Component({ selector: 'redaction-add-edit-rule-set-dialog', @@ -13,9 +15,13 @@ import { RuleSetControllerService, RuleSetModel } from '@redaction/red-ui-http'; export class AddEditRuleSetDialogComponent { public ruleSetForm: FormGroup; public hasValidFrom: boolean; + public hasValidTo: boolean; public downloadTypesEnum = ['ORIGINAL', 'PREVIEW', 'REDACTED']; public reportTypesEnum = Object.values(RuleSetModel.ReportTypesEnum); + private _previousValidFrom: Moment; + private _previousValidTo: Moment; + constructor( private readonly _appStateService: AppStateService, private readonly _formBuilder: FormBuilder, @@ -26,8 +32,8 @@ export class AddEditRuleSetDialogComponent { this.ruleSetForm = this._formBuilder.group({ name: [this.ruleSet?.name, Validators.required], description: [this.ruleSet?.description], - validFrom: [this.ruleSet?.validFrom], - validTo: [this.ruleSet?.validTo], + validFrom: [this.ruleSet?.validFrom ? moment(this.ruleSet?.validFrom) : null, this._requiredIfValidator(() => this.hasValidFrom)], + validTo: [this.ruleSet?.validTo ? moment(this.ruleSet?.validTo) : null, this._requiredIfValidator(() => this.hasValidTo)], downloadFileTypes: [this.ruleSet ? this.ruleSet.downloadFileTypes : ['PREVIEW', 'REDACTED']], reportTypes: [ this.ruleSet @@ -36,18 +42,55 @@ export class AddEditRuleSetDialogComponent { Validators.required ] }); - this.hasValidFrom = !!this.ruleSet?.validFrom && !!this.ruleSet?.validTo; + this.hasValidFrom = !!this.ruleSet?.validFrom; + this.hasValidTo = !!this.ruleSet?.validTo; + + this._previousValidFrom = this.ruleSetForm.get('validFrom').value; + this._previousValidTo = this.ruleSetForm.get('validTo').value; + + this.ruleSetForm.valueChanges.subscribe((value) => { + this._applyValidityIntervalConstraints(value); + }); + } + + private _applyValidityIntervalConstraints(value): boolean { + if (applyIntervalConstraints(value, this._previousValidFrom, this._previousValidTo, this.ruleSetForm, 'validFrom', 'validTo')) { + return true; + } + + this._previousValidFrom = this.ruleSetForm.get('validFrom').value; + this._previousValidTo = this.ruleSetForm.get('validTo').value; + return false; + } + + private _requiredIfValidator(predicate) { + return (formControl) => { + if (!formControl.parent) { + return null; + } + if (predicate()) { + return Validators.required(formControl); + } + return null; + }; } public get changed(): boolean { if (!this.ruleSet) return true; for (const key of Object.keys(this.ruleSetForm.getRawValue())) { - if (key === 'validFrom' || key === 'validTo') { - if (this.hasValidFrom !== (!!this.ruleSet.validFrom && !!this.ruleSet.validTo)) { + if (key === 'validFrom') { + if (this.hasValidFrom !== !!this.ruleSet.validFrom) { return true; } - if (this.hasValidFrom && !moment(this.ruleSet[key]).isSame(moment(this.ruleSetForm.get(key).value))) { + if (this.hasValidFrom && !moment(this.ruleSet.validFrom).isSame(moment(this.ruleSetForm.get('validFrom').value))) { + return true; + } + } else if (key === 'validTo') { + if (this.hasValidTo !== !!this.ruleSet.validTo) { + return true; + } + if (this.hasValidTo && !moment(this.ruleSet.validTo).isSame(moment(this.ruleSetForm.get('validTo').value))) { return true; } } else if (this.ruleSet[key] !== this.ruleSetForm.get(key).value) { @@ -65,7 +108,9 @@ export class AddEditRuleSetDialogComponent { async saveRuleSet() { const ruleSet = { ruleSetId: this.ruleSet?.ruleSetId, - ...this.ruleSetForm.getRawValue() + ...this.ruleSetForm.getRawValue(), + validFrom: this.hasValidFrom ? this.ruleSetForm.get('validFrom').value : null, + validTo: this.hasValidTo ? this.ruleSetForm.get('validTo').value : null }; await this._ruleSetController.createOrUpdateRuleSet(ruleSet).toPromise(); await this._appStateService.loadAllRuleSets(); diff --git a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html index 62a0e63a7..05d133535 100644 --- a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html @@ -97,7 +97,7 @@ -
+
{{ log.message }}
diff --git a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts index e096504b9..651877e0f 100644 --- a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts @@ -4,6 +4,7 @@ import { FormBuilder, FormGroup } from '@angular/forms'; import { AuditControllerService, AuditResponse, AuditSearchRequest } from '@redaction/red-ui-http'; import { TranslateService } from '@ngx-translate/core'; import { Moment } from 'moment'; +import { applyIntervalConstraints } from '../../../../utils/date-inputs-utils'; const PAGE_SIZE = 50; @@ -49,19 +50,10 @@ export class AuditScreenComponent { } private _updateDateFilters(value): boolean { - if (!!value.to && !!value.from) { - if (this._previousFrom !== value.from) { - if (value.to.isBefore(value.from)) { - this.filterForm.patchValue({ to: value.from }); - return true; - } - } else if (this._previousTo !== value.to) { - if (value.to.isBefore(value.from)) { - this.filterForm.patchValue({ from: value.to }); - return true; - } - } + if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.filterForm, 'from', 'to')) { + return true; } + this._previousFrom = this.filterForm.get('from').value; this._previousTo = this.filterForm.get('to').value; return false; diff --git a/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.scss b/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.scss index 81ca6ddcf..3aa1137c2 100644 --- a/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.scss +++ b/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.scss @@ -55,10 +55,6 @@ margin-top: 12px; } -.mt-16 { - margin-top: 16px; -} - .mt-24 { margin-top: 24px; } diff --git a/apps/red-ui/src/app/modules/projects/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.html b/apps/red-ui/src/app/modules/projects/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.html index 33fcb6313..36cfc1f3b 100644 --- a/apps/red-ui/src/app/modules/projects/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.html +++ b/apps/red-ui/src/app/modules/projects/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.html @@ -19,7 +19,7 @@
{{ 'project-listing.add-edit-dialog.form.template' | translate }} - + , @Inject(MAT_DIALOG_DATA) public project: ProjectWrapper ) { + this._filterInvalidRuleSets(); this.projectForm = this._formBuilder.group({ projectName: [this.project?.projectName, Validators.required], - ruleSet: [{ value: this.project?.ruleSetId, disabled: this.project?.hasFiles }, Validators.required], + ruleSetId: [{ value: this.project?.ruleSetId, disabled: this.project?.hasFiles }, Validators.required], downloadFileTypes: [this.project?.project?.downloadFileTypes], reportTypes: [this.project?.project?.reportTypes, Validators.required], description: [this.project?.description], @@ -34,8 +36,15 @@ export class AddEditProjectDialogComponent { this.hasDueDate = !!this.project?.dueDate; } - public get ruleSets(): RuleSetModel[] { - return this._appStateService.ruleSets; + private _filterInvalidRuleSets() { + this.ruleSets = this._appStateService.ruleSets.filter((r) => { + if (this.project?.ruleSetId === r.ruleSetId) { + return true; + } + const notYetValid = !!r.validFrom && moment(r.validFrom).isAfter(moment()); + const notValidAnymore = !!r.validTo && moment(r.validTo).add(1, 'd').isBefore(moment()); + return !(notYetValid || notValidAnymore); + }); } public humanize(value: string): string { @@ -55,6 +64,10 @@ export class AddEditProjectDialogComponent { if (this.hasDueDate && !moment(this.project.dueDate).isSame(moment(this.projectForm.get(key).value))) { return true; } + } else if (key === 'downloadFileTypes' || key === 'reportTypes') { + if (this.project.project[key] !== this.projectForm.get(key).value) { + return true; + } } else if (this.project[key] !== this.projectForm.get(key).value) { return true; } diff --git a/apps/red-ui/src/app/modules/shared/shared.module.ts b/apps/red-ui/src/app/modules/shared/shared.module.ts index e10cbf5bc..581115458 100644 --- a/apps/red-ui/src/app/modules/shared/shared.module.ts +++ b/apps/red-ui/src/app/modules/shared/shared.module.ts @@ -29,6 +29,8 @@ import { EmptyStateComponent } from './components/empty-state/empty-state.compon import { BaseListingComponent } from './base/base-listing.component'; import { SortByPipe } from './components/sort-pipe/sort-by.pipe'; import { RoundCheckboxComponent } from './components/checkbox/round-checkbox.component'; +import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; const buttons = [ChevronButtonComponent, CircleButtonComponent, FileDownloadBtnComponent, IconButtonComponent, UserButtonComponent]; @@ -60,6 +62,20 @@ const modules = [MatConfigModule, TranslateModule, ScrollingModule, IconsModule, @NgModule({ declarations: [...components, ...utils], imports: [CommonModule, ...modules], - exports: [...modules, ...components, ...utils, RoundCheckboxComponent] + exports: [...modules, ...components, ...utils, RoundCheckboxComponent], + providers: [ + { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }, + { + provide: MAT_DATE_FORMATS, + useValue: { + display: { + dateInput: 'DD/MM/YY', + monthYearLabel: 'YYYY', + dateA11yLabel: 'LL', + monthYearA11yLabel: 'YYYY' + } + } + } + ] }) export class SharedModule {} diff --git a/apps/red-ui/src/app/utils/date-inputs-utils.ts b/apps/red-ui/src/app/utils/date-inputs-utils.ts new file mode 100644 index 000000000..5b5e30e3a --- /dev/null +++ b/apps/red-ui/src/app/utils/date-inputs-utils.ts @@ -0,0 +1,19 @@ +import { FormGroup } from '@angular/forms'; +import { Moment } from 'moment'; + +export function applyIntervalConstraints(value, previousFrom: Moment, previousTo: Moment, form: FormGroup, fromKey: string, toKey: string): boolean { + if (!!value[fromKey] && !!value[toKey]) { + if (previousFrom !== value[fromKey]) { + if (value[toKey].isBefore(value[fromKey])) { + form.patchValue({ [toKey]: value[fromKey] }); + return true; + } + } else if (previousTo !== value[toKey]) { + if (value[toKey].isBefore(value[fromKey])) { + form.patchValue({ [fromKey]: value[toKey] }); + return true; + } + } + } + return false; +} diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 7d5246ba8..05a4c2500 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -690,6 +690,7 @@ "description": "Description", "description-placeholder": "Enter Description", "valid-from": "Valid from", + "valid-to": "Valid to", "download-file-types": { "label": "Download file types" }, diff --git a/apps/red-ui/src/assets/styles/red-page-layout.scss b/apps/red-ui/src/assets/styles/red-page-layout.scss index ce6b6c562..255d7fbe4 100644 --- a/apps/red-ui/src/assets/styles/red-page-layout.scss +++ b/apps/red-ui/src/assets/styles/red-page-layout.scss @@ -228,6 +228,10 @@ section.settings { margin-top: 8px; } +.mt-16 { + margin-top: 16px; +} + .mt-20 { margin-top: 20px; }