Handle invalid project templates
This commit is contained in:
parent
0757a468bd
commit
ec07903562
@ -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]
|
||||
|
||||
@ -43,30 +43,38 @@
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="valid-from">
|
||||
<mat-checkbox [checked]="hasValidFrom" (change)="hasValidFrom = !hasValidFrom" class="filter-menu-checkbox" color="primary">
|
||||
{{ 'project-listing.add-edit-dialog.form.due-date' | translate }}
|
||||
</mat-checkbox>
|
||||
<div class="validity">
|
||||
<div>
|
||||
<mat-checkbox [checked]="hasValidFrom" (change)="hasValidFrom = !hasValidFrom" class="filter-menu-checkbox" color="primary">
|
||||
{{ 'add-edit-project-template.form.valid-from' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<ng-container *ngIf="hasValidFrom">
|
||||
<div class="red-input-group datepicker-wrapper ml-16 mr-16">
|
||||
<input placeholder="dd/mm/yy" [matDatepicker]="fromPicker" formControlName="validFrom" />
|
||||
<mat-datepicker-toggle matSuffix [for]="fromPicker">
|
||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #fromPicker></mat-datepicker>
|
||||
<mat-checkbox [checked]="hasValidTo" (change)="hasValidTo = !hasValidTo" class="filter-menu-checkbox" color="primary">
|
||||
{{ 'add-edit-project-template.form.valid-to' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="red-input-group datepicker-wrapper">
|
||||
<ng-container *ngIf="hasValidFrom">
|
||||
<input placeholder="dd/mm/yy" [matDatepicker]="fromPicker" formControlName="validFrom" />
|
||||
<mat-datepicker-toggle matSuffix [for]="fromPicker">
|
||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #fromPicker></mat-datepicker>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
to
|
||||
|
||||
<div class="red-input-group datepicker-wrapper ml-16">
|
||||
<input placeholder="dd/mm/yy" [matDatepicker]="toPicker" formControlName="validTo" />
|
||||
<mat-datepicker-toggle matSuffix [for]="toPicker">
|
||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #toPicker></mat-datepicker>
|
||||
<div class="red-input-group datepicker-wrapper">
|
||||
<ng-container *ngIf="hasValidTo">
|
||||
<input placeholder="dd/mm/yy" [matDatepicker]="toPicker" formControlName="validTo" />
|
||||
<mat-datepicker-toggle matSuffix [for]="toPicker">
|
||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #toPicker></mat-datepicker>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -97,7 +97,7 @@
|
||||
<redaction-empty-state *ngIf="!logs?.totalHits" icon="red:document" screen="audit-screen"></redaction-empty-state>
|
||||
|
||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||
<div class="table-item pointer" *cdkVirtualFor="let log of logs?.data">
|
||||
<div class="table-item" *cdkVirtualFor="let log of logs?.data">
|
||||
<div>
|
||||
{{ log.message }}
|
||||
</div>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -55,10 +55,6 @@
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.mt-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.mt-24 {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<div class="red-input-group required w-400">
|
||||
<mat-form-field floatLabel="always">
|
||||
<mat-label>{{ 'project-listing.add-edit-dialog.form.template' | translate }}</mat-label>
|
||||
<mat-select formControlName="ruleSet" style="width: 100%;" (valueChange)="ruleSetChanged($event)">
|
||||
<mat-select formControlName="ruleSetId" style="width: 100%;" (valueChange)="ruleSetChanged($event)">
|
||||
<mat-option
|
||||
*ngFor="let ruleSet of ruleSets"
|
||||
[value]="ruleSet.ruleSetId"
|
||||
|
||||
@ -16,6 +16,7 @@ export class AddEditProjectDialogComponent {
|
||||
public hasDueDate: boolean;
|
||||
public downloadTypesEnum = ['ORIGINAL', 'PREVIEW', 'REDACTED'];
|
||||
public reportTypesEnum = Object.values(RuleSetModel.ReportTypesEnum);
|
||||
public ruleSets: RuleSetModel[];
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
@ -23,9 +24,10 @@ export class AddEditProjectDialogComponent {
|
||||
public dialogRef: MatDialogRef<AddEditProjectDialogComponent>,
|
||||
@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;
|
||||
}
|
||||
|
||||
@ -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 {}
|
||||
|
||||
19
apps/red-ui/src/app/utils/date-inputs-utils.ts
Normal file
19
apps/red-ui/src/app/utils/date-inputs-utils.ts
Normal file
@ -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;
|
||||
}
|
||||
@ -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"
|
||||
},
|
||||
|
||||
@ -228,6 +228,10 @@ section.settings {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.mt-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.mt-20 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user