Pull request #169: File attributes primary & type

Merge in RED/ui from RED-1353 to master

* commit '7440dd597d131731e3b297da594b1ce3cb5bbd87':
  File attributes primary & type
This commit is contained in:
Adina Teudan 2021-04-26 15:41:08 +02:00
commit 0af709f2d7
14 changed files with 121 additions and 90 deletions

View File

@ -20,12 +20,25 @@
/>
</div>
<div class="red-input-group">
<mat-slide-toggle formControlName="readonly" color="primary">{{ 'add-edit-file-attribute.form.read-only' | translate }}</mat-slide-toggle>
<div class="red-input-group w-300 required">
<label translate="add-edit-file-attribute.form.type"></label>
<mat-select formControlName="type">
<mat-option *ngFor="let type of typeOptions" [value]="type">
{{ 'file-attribute-types.' + type | translate }}
</mat-option>
</mat-select>
</div>
<div class="red-input-group">
<mat-slide-toggle formControlName="visible" color="primary">{{ 'add-edit-file-attribute.form.visible' | translate }}</mat-slide-toggle>
<div class="options-wrapper">
<div class="red-input-group">
<mat-slide-toggle formControlName="readonly" color="primary">{{ 'add-edit-file-attribute.form.read-only' | translate }}</mat-slide-toggle>
</div>
<div class="red-input-group mt-0">
<mat-checkbox name="primaryAttribute" formControlName="primaryAttribute" color="primary">
{{ 'add-edit-file-attribute.form.primary' | translate }}
</mat-checkbox>
</div>
</div>
</div>
<div class="dialog-actions">

View File

@ -0,0 +1,9 @@
.options-wrapper {
display: flex;
align-items: center;
margin-top: 24px;
> *:not(:last-child) {
margin-right: 32px;
}
}

View File

@ -13,6 +13,7 @@ export class AddEditFileAttributeDialogComponent {
public fileAttributeForm: FormGroup;
public fileAttribute: FileAttributeConfig;
public ruleSetId: string;
public readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
constructor(
private readonly _appStateService: AppStateService,
@ -26,8 +27,9 @@ export class AddEditFileAttributeDialogComponent {
this.fileAttributeForm = this._formBuilder.group({
label: [this.fileAttribute?.label, Validators.required],
csvColumnHeader: [this.fileAttribute?.csvColumnHeader, Validators.required],
type: [this.fileAttribute?.type || FileAttributeConfig.TypeEnum.TEXT, Validators.required],
readonly: [this.fileAttribute ? !this.fileAttribute.editable : false],
visible: [this.fileAttribute?.visible]
primaryAttribute: [this.fileAttribute?.primaryAttribute]
});
}

View File

@ -18,13 +18,7 @@
icon="red:read-only"
>
</redaction-circle-button>
<redaction-circle-button
[matMenuTriggerFor]="displayMenu"
tooltip="file-attributes-csv-import.table-header.actions.display"
type="dark-bg"
icon="red:visibility"
>
</redaction-circle-button>
<redaction-circle-button
(action)="deactivateSelection()"
tooltip="file-attributes-csv-import.table-header.actions.remove-selected"
@ -50,22 +44,9 @@
></button>
</mat-menu>
<mat-menu #displayMenu="matMenu" class="no-padding-bottom">
<button
mat-menu-item
(click)="setAttributeForSelection('display', true)"
translate="file-attributes-csv-import.table-header.actions.enable-display"
></button>
<button
mat-menu-item
(click)="setAttributeForSelection('display', false)"
translate="file-attributes-csv-import.table-header.actions.disable-display"
></button>
</mat-menu>
<mat-menu #typeMenu="matMenu" class="no-padding-bottom">
<button *ngFor="let type of ['Text', 'Number', 'Date']" mat-menu-item (click)="setAttributeForSelection('type', type)">
{{ 'file-attributes-csv-import.types.' + type | translate }}
<button *ngFor="let type of typeOptions" mat-menu-item (click)="setAttributeForSelection('type', type)">
{{ 'file-attribute-types.' + type | translate }}
</button>
</mat-menu>
</ng-container>
@ -85,9 +66,10 @@
></redaction-table-col-name>
<redaction-table-col-name
label="file-attributes-csv-import.table-col-names.display"
label="file-attributes-csv-import.table-col-names.primary"
rightIcon="red:status-info"
rightIconTooltip="file-attributes-csv-import.table-col-names.primary-info-tooltip"
class="flex-center"
leftIcon="red:visibility"
></redaction-table-col-name>
<div></div>
@ -146,8 +128,8 @@
<div class="red-input-group">
<mat-form-field class="no-label">
<mat-select [(ngModel)]="field.type">
<mat-option *ngFor="let type of ['Text', 'Number', 'Date']" [value]="type">
{{ 'file-attributes-csv-import.types.' + type | translate }}
<mat-option *ngFor="let type of typeOptions" [value]="type">
{{ 'file-attribute-types.' + type | translate }}
</mat-option>
</mat-select>
</mat-form-field>
@ -156,11 +138,13 @@
<div class="center">
<mat-slide-toggle [(ngModel)]="field.readonly" color="primary"></mat-slide-toggle>
</div>
<div class="center"><mat-slide-toggle [(ngModel)]="field.display" color="primary"></mat-slide-toggle></div>
<div class="center">
<redaction-round-checkbox (click)="togglePrimary(field)" [active]="field.primaryAttribute"></redaction-round-checkbox>
</div>
<div class="actions-container">
<div class="action-buttons">
<redaction-circle-button
(action)="toggleFieldActive.emit(field)"
(action)="field.primaryAttribute = false; toggleFieldActive.emit(field)"
[removeTooltip]="true"
tooltip="file-attributes-csv-import.action.remove"
type="dark-bg"

View File

@ -36,7 +36,7 @@ cdk-virtual-scroll-viewport {
height: calc(100% - 80px);
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 30px minmax(0, 25vw) 150px auto auto auto 11px;
grid-template-columns: 30px minmax(0, 350px) 150px auto auto auto 11px;
.table-item {
> div {
@ -78,7 +78,7 @@ cdk-virtual-scroll-viewport {
&.has-scrollbar:hover {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 30px minmax(0, 25vw) 150px auto auto auto;
grid-template-columns: 30px minmax(0, 350px) 150px auto auto auto;
}
}
}

View File

@ -1,6 +1,7 @@
import { Component, EventEmitter, Injector, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { BaseListingComponent } from '../../../../shared/base/base-listing.component';
import { Field } from '../file-attributes-csv-import-dialog.component';
import { FileAttributeConfig } from '@redaction/red-ui-http';
@Component({
selector: 'redaction-active-fields-listing',
@ -13,6 +14,8 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
@Output() public setHoveredColumn = new EventEmitter<string>();
@Output() public toggleFieldActive = new EventEmitter<Field>();
public readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
protected readonly _selectionKey = 'csvColumn';
constructor(protected readonly _injector: Injector) {
@ -27,6 +30,7 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
}
public deactivateSelection() {
this.allEntities.filter((field) => this.isEntitySelected(field)).forEach((field) => (field.primaryAttribute = false));
this.allEntities = [...this.allEntities.filter((field) => !this.isEntitySelected(field))];
this.allEntitiesChange.emit(this.allEntities);
this.selectedEntitiesIds = [];
@ -37,4 +41,15 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
this.allEntities.find((f) => f.csvColumn === csvColumn)[attribute] = value;
}
}
public togglePrimary(field: Field) {
if (field.primaryAttribute) {
field.primaryAttribute = false;
} else {
for (const f of this.allEntities) {
f.primaryAttribute = false;
}
field.primaryAttribute = true;
}
}
}

View File

@ -3,7 +3,7 @@ import { AbstractControl, FormGroup, ValidatorFn, Validators } from '@angular/fo
import { AppStateService } from '../../../../state/app-state.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import * as Papa from 'papaparse';
import { FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
@ -11,19 +11,13 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen
import { NotificationService, NotificationType } from '../../../../services/notification.service';
import { TranslateService } from '@ngx-translate/core';
enum FieldType {
Text = 'Text',
Number = 'Number',
Date = 'Date'
}
export interface Field {
id?: string;
csvColumn: string;
name: string;
type: FieldType;
type: FileAttributeConfig.TypeEnum;
readonly: boolean;
display: boolean;
primaryAttribute: boolean;
editingName?: boolean;
temporaryName?: string;
}
@ -107,9 +101,9 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
entity.id = existing.id;
entity.name = existing.label;
entity.temporaryName = existing.label;
// TODO: entity.type
entity.display = existing.visible;
entity.type = existing.type;
entity.readonly = !existing.editable;
entity.primaryAttribute = existing.primaryAttribute;
this.toggleFieldActive(entity);
}
}
@ -174,9 +168,9 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
csvColumn,
name: csvColumn,
temporaryName: csvColumn,
type: isNumber ? FieldType.Number : FieldType.Text,
type: isNumber ? FileAttributeConfig.TypeEnum.NUMBER : FileAttributeConfig.TypeEnum.TEXT,
readonly: false,
display: true
primaryAttribute: false
};
}
@ -189,29 +183,33 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
}
public async save() {
const newPrimary = !!this.activeFields.find((attr) => attr.primaryAttribute);
if (newPrimary) {
this.data.existingConfiguration.fileAttributeConfigs.forEach((attr) => (attr.primaryAttribute = false));
}
const fileAttributes = {
...this.baseConfigForm.getRawValue(),
fileAttributeConfigs: [
...this.data.existingConfiguration.fileAttributeConfigs.filter(
(a) => !this.allEntities.find((entity) => entity.csvColumn === a.csvColumnHeader)
),
...this.activeFields.map((field) => {
return {
id: field.id,
csvColumnHeader: field.csvColumn,
editable: !field.readonly,
label: field.name,
type: field.type,
primaryAttribute: field.primaryAttribute
};
})
]
};
try {
await this._fileAttributesControllerService
.setFileAttributesConfig(
{
...this.baseConfigForm.getRawValue(),
fileAttributeConfigs: [
...this.data.existingConfiguration.fileAttributeConfigs.filter(
(a) => !this.allEntities.find((entity) => entity.csvColumn === a.csvColumnHeader)
),
...this.activeFields.map((field) => {
return {
id: field.id,
csvColumnHeader: field.csvColumn,
editable: !field.readonly,
label: field.name,
visible: field.display
};
})
]
},
this.ruleSetId
)
.toPromise();
await this._fileAttributesControllerService.setFileAttributesConfig(fileAttributes, this.ruleSetId).toPromise();
this._notificationService.showToastNotification(
this._translateService.instant('file-attributes-csv-import.save.success', { count: this.activeFields.length }),
null,

View File

@ -117,9 +117,7 @@
<span>{{ attribute.label }}</span>
</div>
<div class="small-label">
Free text
</div>
<div class="small-label" [translate]="'file-attribute-types.' + attribute.type"></div>
<div class="center read-only">
<mat-icon
@ -133,7 +131,7 @@
{{ attribute.csvColumnHeader }}
</div>
<div class="center">
<redaction-round-checkbox *ngIf="attribute.visible" [size]="18" [active]="true"></redaction-round-checkbox>
<redaction-round-checkbox *ngIf="attribute.primaryAttribute" [size]="18" [active]="true"></redaction-round-checkbox>
</div>
<div class="actions-container">
<div class="action-buttons">

View File

@ -68,3 +68,7 @@ redaction-table-col-name::ng-deep {
display: none;
visibility: hidden;
}
.table-item > div:not(.selection-column) redaction-round-checkbox {
cursor: default;
}

View File

@ -15,7 +15,7 @@ export class DocumentInfoComponent implements OnInit {
public fileAttributesConfig: FileAttributeConfig[];
constructor(private readonly _appStateService: AppStateService, private readonly _dialogService: ProjectsDialogService) {
this.fileAttributesConfig = this._appStateService.fileAttributesConfig.filter((attr) => attr.visible);
this.fileAttributesConfig = this._appStateService.fileAttributesConfig;
}
ngOnInit(): void {}

View File

@ -30,7 +30,7 @@ export class DocumentInfoDialogComponent implements OnInit {
async ngOnInit() {
this.attributes = (await this._fileAttributesService.getFileAttributesConfiguration(this._project.ruleSetId).toPromise()).fileAttributeConfigs.filter(
(attr) => attr.visible && attr.editable
(attr) => attr.editable
);
const formConfig = this.attributes.reduce((acc, attr) => ({ ...acc, [attr.id]: [this.file.fileAttributes.attributeIdToValue[attr.id]] }), {});
this.documentInfoForm = this._formBuilder.group(formConfig);

View File

@ -33,7 +33,7 @@ export class SyncWidthDirective implements AfterViewInit, OnDestroy {
this.el.nativeElement.setAttribute('synced', true);
const { tableRow, length } = this._sampleRow;
const { tableRow, length } = this._sampleRow(tableRows);
const hasExtraColumns = headerItems.length !== length ? 1 : 0;
@ -56,8 +56,7 @@ export class SyncWidthDirective implements AfterViewInit, OnDestroy {
this.matchWidth();
}
private get _sampleRow(): { tableRow: Element; length: number } {
const tableRows = document.getElementsByClassName(this.redactionSyncWidth);
private _sampleRow(tableRows: HTMLCollectionOf<Element>): { tableRow: Element; length: number } {
let length = 0;
let tableRow: Element;

View File

@ -634,10 +634,16 @@
"column-header": "CSV Column Header",
"column-header-placeholder": "Enter CSV Column Header",
"read-only": "Make Read-Only",
"visible": "Visible in Document Info"
"type": "Type",
"primary": "Set as Primary"
},
"save": "Save Attribute"
},
"file-attribute-types": {
"TEXT": "Free Text",
"NUMBER": "Number",
"DATE": "Date"
},
"add-edit-dictionary": {
"title": {
"edit": "Edit {{name}} Dictionary",
@ -1199,9 +1205,6 @@
"read-only": "Make Read-only",
"enable-read-only": "Enable Read-only for all attributes",
"disable-read-only": "Disable Read-only for all attributes",
"display": "Toggle Display",
"enable-display": "Enable Display for all attributes",
"disable-display": "Disable Display for all attributes",
"type": "Type"
}
},
@ -1210,17 +1213,13 @@
"name": "Name",
"type": "Type",
"read-only": "Read-Only",
"display": "Display"
"primary": "primary",
"primary-info-tooltip": "The value of the attribute set as primary shows up under the file name in the documents list."
},
"quick-activation": {
"all": "All",
"none": "None"
},
"types": {
"Text": "Free Text",
"Number": "Number",
"Date": "Date"
},
"action": {
"edit-name": "Edit Name",
"save-name": "Save",

View File

@ -15,5 +15,15 @@ export interface FileAttributeConfig {
editable?: boolean;
id?: string;
label?: string;
visible?: boolean;
primaryAttribute?: boolean;
type?: FileAttributeConfig.TypeEnum;
}
export namespace FileAttributeConfig {
export type TypeEnum = 'TEXT' | 'NUMBER' | 'DATE';
export const TypeEnum = {
TEXT: 'TEXT' as TypeEnum,
NUMBER: 'NUMBER' as TypeEnum,
DATE: 'DATE' as TypeEnum
};
}