File attributes primary & type

This commit is contained in:
Adina Țeudan 2021-04-26 16:34:58 +03:00
parent 313285d978
commit 7440dd597d
14 changed files with 121 additions and 90 deletions

View File

@ -20,12 +20,25 @@
/> />
</div> </div>
<div class="red-input-group"> <div class="red-input-group w-300 required">
<mat-slide-toggle formControlName="readonly" color="primary">{{ 'add-edit-file-attribute.form.read-only' | translate }}</mat-slide-toggle> <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>
<div class="red-input-group"> <div class="options-wrapper">
<mat-slide-toggle formControlName="visible" color="primary">{{ 'add-edit-file-attribute.form.visible' | translate }}</mat-slide-toggle> <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> </div>
<div class="dialog-actions"> <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 fileAttributeForm: FormGroup;
public fileAttribute: FileAttributeConfig; public fileAttribute: FileAttributeConfig;
public ruleSetId: string; public ruleSetId: string;
public readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
constructor( constructor(
private readonly _appStateService: AppStateService, private readonly _appStateService: AppStateService,
@ -26,8 +27,9 @@ export class AddEditFileAttributeDialogComponent {
this.fileAttributeForm = this._formBuilder.group({ this.fileAttributeForm = this._formBuilder.group({
label: [this.fileAttribute?.label, Validators.required], label: [this.fileAttribute?.label, Validators.required],
csvColumnHeader: [this.fileAttribute?.csvColumnHeader, Validators.required], csvColumnHeader: [this.fileAttribute?.csvColumnHeader, Validators.required],
type: [this.fileAttribute?.type || FileAttributeConfig.TypeEnum.TEXT, Validators.required],
readonly: [this.fileAttribute ? !this.fileAttribute.editable : false], 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" icon="red:read-only"
> >
</redaction-circle-button> </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 <redaction-circle-button
(action)="deactivateSelection()" (action)="deactivateSelection()"
tooltip="file-attributes-csv-import.table-header.actions.remove-selected" tooltip="file-attributes-csv-import.table-header.actions.remove-selected"
@ -50,22 +44,9 @@
></button> ></button>
</mat-menu> </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"> <mat-menu #typeMenu="matMenu" class="no-padding-bottom">
<button *ngFor="let type of ['Text', 'Number', 'Date']" mat-menu-item (click)="setAttributeForSelection('type', type)"> <button *ngFor="let type of typeOptions" mat-menu-item (click)="setAttributeForSelection('type', type)">
{{ 'file-attributes-csv-import.types.' + type | translate }} {{ 'file-attribute-types.' + type | translate }}
</button> </button>
</mat-menu> </mat-menu>
</ng-container> </ng-container>
@ -85,9 +66,10 @@
></redaction-table-col-name> ></redaction-table-col-name>
<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" class="flex-center"
leftIcon="red:visibility"
></redaction-table-col-name> ></redaction-table-col-name>
<div></div> <div></div>
@ -146,8 +128,8 @@
<div class="red-input-group"> <div class="red-input-group">
<mat-form-field class="no-label"> <mat-form-field class="no-label">
<mat-select [(ngModel)]="field.type"> <mat-select [(ngModel)]="field.type">
<mat-option *ngFor="let type of ['Text', 'Number', 'Date']" [value]="type"> <mat-option *ngFor="let type of typeOptions" [value]="type">
{{ 'file-attributes-csv-import.types.' + type | translate }} {{ 'file-attribute-types.' + type | translate }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
@ -156,11 +138,13 @@
<div class="center"> <div class="center">
<mat-slide-toggle [(ngModel)]="field.readonly" color="primary"></mat-slide-toggle> <mat-slide-toggle [(ngModel)]="field.readonly" color="primary"></mat-slide-toggle>
</div> </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="actions-container">
<div class="action-buttons"> <div class="action-buttons">
<redaction-circle-button <redaction-circle-button
(action)="toggleFieldActive.emit(field)" (action)="field.primaryAttribute = false; toggleFieldActive.emit(field)"
[removeTooltip]="true" [removeTooltip]="true"
tooltip="file-attributes-csv-import.action.remove" tooltip="file-attributes-csv-import.action.remove"
type="dark-bg" type="dark-bg"

View File

@ -36,7 +36,7 @@ cdk-virtual-scroll-viewport {
height: calc(100% - 80px); height: calc(100% - 80px);
::ng-deep.cdk-virtual-scroll-content-wrapper { ::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 { .table-item {
> div { > div {
@ -78,7 +78,7 @@ cdk-virtual-scroll-viewport {
&.has-scrollbar:hover { &.has-scrollbar:hover {
::ng-deep.cdk-virtual-scroll-content-wrapper { ::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 { Component, EventEmitter, Injector, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { BaseListingComponent } from '../../../../shared/base/base-listing.component'; import { BaseListingComponent } from '../../../../shared/base/base-listing.component';
import { Field } from '../file-attributes-csv-import-dialog.component'; import { Field } from '../file-attributes-csv-import-dialog.component';
import { FileAttributeConfig } from '@redaction/red-ui-http';
@Component({ @Component({
selector: 'redaction-active-fields-listing', selector: 'redaction-active-fields-listing',
@ -13,6 +14,8 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
@Output() public setHoveredColumn = new EventEmitter<string>(); @Output() public setHoveredColumn = new EventEmitter<string>();
@Output() public toggleFieldActive = new EventEmitter<Field>(); @Output() public toggleFieldActive = new EventEmitter<Field>();
public readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
protected readonly _selectionKey = 'csvColumn'; protected readonly _selectionKey = 'csvColumn';
constructor(protected readonly _injector: Injector) { constructor(protected readonly _injector: Injector) {
@ -27,6 +30,7 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
} }
public deactivateSelection() { public deactivateSelection() {
this.allEntities.filter((field) => this.isEntitySelected(field)).forEach((field) => (field.primaryAttribute = false));
this.allEntities = [...this.allEntities.filter((field) => !this.isEntitySelected(field))]; this.allEntities = [...this.allEntities.filter((field) => !this.isEntitySelected(field))];
this.allEntitiesChange.emit(this.allEntities); this.allEntitiesChange.emit(this.allEntities);
this.selectedEntitiesIds = []; this.selectedEntitiesIds = [];
@ -37,4 +41,15 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
this.allEntities.find((f) => f.csvColumn === csvColumn)[attribute] = value; 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 { AppStateService } from '../../../../state/app-state.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import * as Papa from 'papaparse'; 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 { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators'; 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 { NotificationService, NotificationType } from '../../../../services/notification.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
enum FieldType {
Text = 'Text',
Number = 'Number',
Date = 'Date'
}
export interface Field { export interface Field {
id?: string; id?: string;
csvColumn: string; csvColumn: string;
name: string; name: string;
type: FieldType; type: FileAttributeConfig.TypeEnum;
readonly: boolean; readonly: boolean;
display: boolean; primaryAttribute: boolean;
editingName?: boolean; editingName?: boolean;
temporaryName?: string; temporaryName?: string;
} }
@ -107,9 +101,9 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
entity.id = existing.id; entity.id = existing.id;
entity.name = existing.label; entity.name = existing.label;
entity.temporaryName = existing.label; entity.temporaryName = existing.label;
// TODO: entity.type entity.type = existing.type;
entity.display = existing.visible;
entity.readonly = !existing.editable; entity.readonly = !existing.editable;
entity.primaryAttribute = existing.primaryAttribute;
this.toggleFieldActive(entity); this.toggleFieldActive(entity);
} }
} }
@ -174,9 +168,9 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
csvColumn, csvColumn,
name: csvColumn, name: csvColumn,
temporaryName: csvColumn, temporaryName: csvColumn,
type: isNumber ? FieldType.Number : FieldType.Text, type: isNumber ? FileAttributeConfig.TypeEnum.NUMBER : FileAttributeConfig.TypeEnum.TEXT,
readonly: false, readonly: false,
display: true primaryAttribute: false
}; };
} }
@ -189,29 +183,33 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
} }
public async save() { 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 { try {
await this._fileAttributesControllerService await this._fileAttributesControllerService.setFileAttributesConfig(fileAttributes, this.ruleSetId).toPromise();
.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();
this._notificationService.showToastNotification( this._notificationService.showToastNotification(
this._translateService.instant('file-attributes-csv-import.save.success', { count: this.activeFields.length }), this._translateService.instant('file-attributes-csv-import.save.success', { count: this.activeFields.length }),
null, null,

View File

@ -117,9 +117,7 @@
<span>{{ attribute.label }}</span> <span>{{ attribute.label }}</span>
</div> </div>
<div class="small-label"> <div class="small-label" [translate]="'file-attribute-types.' + attribute.type"></div>
Free text
</div>
<div class="center read-only"> <div class="center read-only">
<mat-icon <mat-icon
@ -133,7 +131,7 @@
{{ attribute.csvColumnHeader }} {{ attribute.csvColumnHeader }}
</div> </div>
<div class="center"> <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>
<div class="actions-container"> <div class="actions-container">
<div class="action-buttons"> <div class="action-buttons">

View File

@ -68,3 +68,7 @@ redaction-table-col-name::ng-deep {
display: none; display: none;
visibility: hidden; 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[]; public fileAttributesConfig: FileAttributeConfig[];
constructor(private readonly _appStateService: AppStateService, private readonly _dialogService: ProjectsDialogService) { 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 {} ngOnInit(): void {}

View File

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

View File

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

View File

@ -634,10 +634,16 @@
"column-header": "CSV Column Header", "column-header": "CSV Column Header",
"column-header-placeholder": "Enter CSV Column Header", "column-header-placeholder": "Enter CSV Column Header",
"read-only": "Make Read-Only", "read-only": "Make Read-Only",
"visible": "Visible in Document Info" "type": "Type",
"primary": "Set as Primary"
}, },
"save": "Save Attribute" "save": "Save Attribute"
}, },
"file-attribute-types": {
"TEXT": "Free Text",
"NUMBER": "Number",
"DATE": "Date"
},
"add-edit-dictionary": { "add-edit-dictionary": {
"title": { "title": {
"edit": "Edit {{name}} Dictionary", "edit": "Edit {{name}} Dictionary",
@ -1199,9 +1205,6 @@
"read-only": "Make Read-only", "read-only": "Make Read-only",
"enable-read-only": "Enable Read-only for all attributes", "enable-read-only": "Enable Read-only for all attributes",
"disable-read-only": "Disable 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" "type": "Type"
} }
}, },
@ -1210,17 +1213,13 @@
"name": "Name", "name": "Name",
"type": "Type", "type": "Type",
"read-only": "Read-Only", "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": { "quick-activation": {
"all": "All", "all": "All",
"none": "None" "none": "None"
}, },
"types": {
"Text": "Free Text",
"Number": "Number",
"Date": "Date"
},
"action": { "action": {
"edit-name": "Edit Name", "edit-name": "Edit Name",
"save-name": "Save", "save-name": "Save",

View File

@ -15,5 +15,15 @@ export interface FileAttributeConfig {
editable?: boolean; editable?: boolean;
id?: string; id?: string;
label?: 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
};
} }