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:
commit
0af709f2d7
@ -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">
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
.options-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 24px;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 32px;
|
||||
}
|
||||
}
|
||||
@ -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]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 {}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user