From dfb3787d42555add7ad8ea37992606ce89ee7296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Tue, 20 Apr 2021 00:58:41 +0300 Subject: [PATCH] Refactor file attributes CSV import --- .../src/app/modules/admin/admin.module.ts | 8 +- .../active-fields-listing.component.html | 175 ++++++++++++++++ .../active-fields-listing.component.scss | 84 ++++++++ .../active-fields-listing.component.ts | 40 ++++ ...ttributes-csv-import-dialog.component.html | 198 +----------------- ...ttributes-csv-import-dialog.component.scss | 84 +------- ...-attributes-csv-import-dialog.component.ts | 83 ++------ .../dictionary-listing-screen.component.ts | 10 +- .../project-overview-screen.component.html | 2 - .../shared/base/base-listing.component.ts | 4 + 10 files changed, 337 insertions(+), 351 deletions(-) create mode 100644 apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html create mode 100644 apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss create mode 100644 apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts diff --git a/apps/red-ui/src/app/modules/admin/admin.module.ts b/apps/red-ui/src/app/modules/admin/admin.module.ts index 2c12acb71..3f31c7f61 100644 --- a/apps/red-ui/src/app/modules/admin/admin.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin.module.ts @@ -32,6 +32,7 @@ import { AddEditUserDialogComponent } from './dialogs/add-edit-user-dialog/add-e import { UsersStatsComponent } from './components/users-stats/users-stats.component'; import { ConfirmDeleteUsersDialogComponent } from './dialogs/confirm-delete-users-dialog/confirm-delete-users-dialog.component'; import { FileAttributesCsvImportDialogComponent } from './dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component'; +import { ActiveFieldsListingComponent } from './dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component'; const dialogs = [ AddEditRuleSetDialogComponent, @@ -41,7 +42,8 @@ const dialogs = [ EditColorDialogComponent, SmtpAuthDialogComponent, AddEditUserDialogComponent, - ConfirmDeleteUsersDialogComponent + ConfirmDeleteUsersDialogComponent, + FileAttributesCsvImportDialogComponent ]; const screens = [ @@ -66,12 +68,14 @@ const components = [ ComboChartComponent, ComboSeriesVerticalComponent, UsersStatsComponent, + ActiveFieldsListingComponent, + ...dialogs, ...screens ]; @NgModule({ - declarations: [...components, FileAttributesCsvImportDialogComponent], + declarations: [...components], providers: [AdminDialogService], imports: [CommonModule, SharedModule, AdminRoutingModule, AceEditorModule, NgxChartsModule, ColorPickerModule] }) diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html new file mode 100644 index 000000000..a33c01a53 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html @@ -0,0 +1,175 @@ +
+
+
+ + +
+ + {{ 'file-attributes-csv-import.table-header.title' | translate: { length: allEntities.length } }} + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + + + + +
+
+
+ + + + + +
+
+
+ +
+
+
+ {{ field.name }} +
+
+
+ +
+
+ + + + + + + + +
+
+
+ + + + {{ 'file-attributes-csv-import.types.' + type | translate }} + + + +
+
+
+ +
+
+
+
+ + +
+
+
+
+
diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss new file mode 100644 index 000000000..42b2d5706 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss @@ -0,0 +1,84 @@ +@import '../../../../../../assets/styles/red-variables'; + +redaction-table-col-name::ng-deep { + > div { + padding: 0 13px 0 10px !important; + + &.name { + padding-left: 22px !important; + } + } +} + +.header-item { + padding: 0 24px 0 10px; + box-shadow: none; + border-top: 1px solid $separator; + + .all-caps-label { + margin-right: 10px; + } + + redaction-circle-button { + margin-right: 2px; + } + + .separator { + margin-left: 14px; + background-color: $separator; + width: 1px; + height: 30px; + margin-right: 16px; + } +} + +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; + + .table-item { + > div { + height: 50px; + + &:not(.scrollbar-placeholder) { + padding-left: 10px; + + &.center { + align-items: center; + } + } + + &.name { + flex-direction: row; + align-items: center; + justify-content: flex-start; + + &:not(.editing) { + padding-left: 22px; + } + + .edit-name-button { + display: none; + } + + redaction-circle-button:first-of-type { + margin-left: 7px; + margin-right: 2px; + } + } + } + + &:hover .name .edit-name-button { + display: block; + } + } + } + + &.has-scrollbar:hover { + ::ng-deep.cdk-virtual-scroll-content-wrapper { + grid-template-columns: 30px minmax(0, 25vw) 150px auto auto auto; + } + } +} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts new file mode 100644 index 000000000..84c01afd9 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts @@ -0,0 +1,40 @@ +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'; + +@Component({ + selector: 'redaction-active-fields-listing', + templateUrl: './active-fields-listing.component.html', + styleUrls: ['./active-fields-listing.component.scss'] +}) +export class ActiveFieldsListingComponent extends BaseListingComponent implements OnChanges { + @Input() public allEntities: Field[]; + @Output() public allEntitiesChange = new EventEmitter(); + @Output() public setHoveredColumn = new EventEmitter(); + @Output() public toggleFieldActive = new EventEmitter(); + + protected readonly _selectionKey = 'csvColumn'; + + constructor(protected readonly _injector: Injector) { + super(_injector); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.allEntities) { + this.displayedEntities = this.allEntities; + this._updateSelection(); + } + } + + public deactivateSelection() { + this.allEntities = [...this.allEntities.filter((field) => !this.isEntitySelected(field))]; + this.allEntitiesChange.emit(this.allEntities); + this.selectedEntitiesIds = []; + } + + public setAttributeForSelection(attribute: string, value: any) { + for (const csvColumn of this.selectedEntitiesIds) { + this.allEntities.find((f) => f.csvColumn === csvColumn)[attribute] = value; + } + } +} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html index 443b5c7c3..2131fdde6 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html @@ -90,7 +90,7 @@
-
-
-
- - -
- - {{ 'file-attributes-csv-import.table-header.title' | translate: { length: activeFields.length } }} - - - - - - - - - - -
- - - - - - - - - - - - - - - - -
-
- -
-
- - - - - - - - - -
-
-
- - - - - -
-
-
- -
-
-
- {{ field.name }} -
-
-
- -
-
- - - - - - - - -
-
-
- - - - {{ 'file-attributes-csv-import.types.' + type | translate }} - - - -
-
-
- -
-
-
-
- - -
-
-
-
-
+
diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.scss b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.scss index fdf45c402..88d6fc0d2 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.scss +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.scss @@ -114,6 +114,7 @@ > .left { width: 375px; + min-width: 375px; background: $grey-2; .csv-header-pill-content { @@ -198,88 +199,5 @@ > .content-container { width: 100%; - - redaction-table-col-name::ng-deep { - > div { - padding: 0 13px 0 10px !important; - - &.name { - padding-left: 22px !important; - } - } - } - - .header-item { - padding: 0 24px 0 10px; - box-shadow: none; - border-top: 1px solid $separator; - - .all-caps-label { - margin-right: 10px; - } - - redaction-circle-button { - margin-right: 2px; - } - - .separator { - margin-left: 14px; - background-color: $separator; - width: 1px; - height: 30px; - margin-right: 16px; - } - } - - 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; - - .table-item { - > div { - height: 50px; - - &:not(.scrollbar-placeholder) { - padding-left: 10px; - - &.center { - align-items: center; - } - } - - &.name { - flex-direction: row; - align-items: center; - justify-content: flex-start; - - &:not(.editing) { - padding-left: 22px; - } - - .edit-name-button { - display: none; - } - - redaction-circle-button:first-of-type { - margin-left: 7px; - margin-right: 2px; - } - } - } - - &:hover .name .edit-name-button { - display: block; - } - } - } - - &.has-scrollbar:hover { - ::ng-deep.cdk-virtual-scroll-content-wrapper { - grid-template-columns: 30px minmax(0, 25vw) 150px auto auto auto; - } - } - } } } diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts index 45a7afd1c..64fa985b3 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts @@ -1,13 +1,13 @@ -import { Component, Inject, ViewChild } from '@angular/core'; -import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { Component, Inject, Injector, ViewChild } from '@angular/core'; +import { AbstractControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; import { AppStateService } from '../../../../state/app-state.service'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import * as Papa from 'papaparse'; import { FileAttributesControllerService } from '@redaction/red-ui-http'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; -import { debounce } from '../../../../utils/debounce'; import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; +import { BaseListingComponent } from '../../../shared/base/base-listing.component'; enum FieldType { Text = 'Text', @@ -15,7 +15,7 @@ enum FieldType { Date = 'Date' } -interface Field { +export interface Field { csvColumn: string; name: string; type: FieldType; @@ -30,17 +30,16 @@ interface Field { templateUrl: './file-attributes-csv-import-dialog.component.html', styleUrls: ['./file-attributes-csv-import-dialog.component.scss'] }) -export class FileAttributesCsvImportDialogComponent { +export class FileAttributesCsvImportDialogComponent extends BaseListingComponent { + protected readonly _searchKey = 'csvColumn'; + public csvFile: File; public ruleSetId: string; public parseResult: { data: any[]; errors: any[]; meta: any; fields: Field[] }; public hoveredColumn: string; public activeFields: Field[] = []; - public selectedFields: string[] = []; public baseConfigForm: FormGroup; public isSearchOpen = false; - public searchForm: FormGroup; - public filteredFields: Field[]; public previewExpanded = true; public filteredKeyOptions: Observable; public keepPreview = false; @@ -50,18 +49,15 @@ export class FileAttributesCsvImportDialogComponent { constructor( private readonly _appStateService: AppStateService, - private readonly _formBuilder: FormBuilder, private readonly _fileAttributesControllerService: FileAttributesControllerService, public dialogRef: MatDialogRef, + protected readonly _injector: Injector, @Inject(MAT_DIALOG_DATA) public data: { csv: File; ruleSetId: string } ) { + super(_injector); this.csvFile = data.csv; this.ruleSetId = data.ruleSetId; - this.searchForm = this._formBuilder.group({ - query: [''] - }); - this.baseConfigForm = this._formBuilder.group({ filenameMappingColumnHeaderName: ['', [Validators.required, this._autocompleteStringValidator()]], delimiter: [undefined, Validators.required], @@ -69,13 +65,6 @@ export class FileAttributesCsvImportDialogComponent { }); this._readFile(); - - this.searchForm.valueChanges.subscribe((value) => this._executeSearch(value)); - } - - @debounce(200) - private _executeSearch(value: { query: string }) { - this.filteredFields = this.parseResult.fields.filter((f) => f.csvColumn.toLowerCase().includes(value.query.toLowerCase())); } private _autocompleteStringValidator(): ValidatorFn { @@ -98,12 +87,14 @@ export class FileAttributesCsvImportDialogComponent { if (!this.baseConfigForm.get('delimiter').value) { this.baseConfigForm.patchValue({ delimiter: this.parseResult.meta.delimiter }); } - this.parseResult.fields = this.parseResult.meta.fields.map((field) => this._buildAttribute(field)); - this.filteredFields = [...this.parseResult.fields]; + this.allEntities = this.parseResult.meta.fields.map((field) => this._buildAttribute(field)); + this.displayedEntities = [...this.allEntities]; this.filteredKeyOptions = this.baseConfigForm.get('filenameMappingColumnHeaderName').valueChanges.pipe( startWith(''), - map((value: string) => this.parseResult.meta.fields.filter((field) => field.toLowerCase().indexOf(value.toLowerCase()) !== -1)) + map((value: string) => + this.allEntities.filter((field) => field.csvColumn.toLowerCase().indexOf(value.toLowerCase()) !== -1).map((field) => field.csvColumn) + ) ); }); reader.readAsText(this.csvFile, this.baseConfigForm.get('encoding').value); @@ -137,9 +128,6 @@ export class FileAttributesCsvImportDialogComponent { } else { this.activeFields.splice(this.activeFields.indexOf(field), 1); this.activeFields = [...this.activeFields]; - if (this.isFieldSelected(field.csvColumn)) { - this.toggleFieldSelected(field.csvColumn); - } } } @@ -157,52 +145,11 @@ export class FileAttributesCsvImportDialogComponent { } public activateAll() { - this.activeFields = [...this.parseResult.fields]; + this.activeFields = [...this.allEntities]; } public deactivateAll() { this.activeFields = []; - this.selectedFields = []; - } - - public toggleFieldSelected(field: string) { - const idx = this.selectedFields.indexOf(field); - if (idx === -1) { - this.selectedFields.push(field); - } else { - this.selectedFields.splice(idx, 1); - } - } - - public toggleSelectAll() { - if (this.areSomeFieldsSelected) { - this.selectedFields = []; - } else { - this.selectedFields = this.activeFields.map((field) => field.csvColumn); - } - } - - public get areAllFieldsSelected() { - return this.activeFields.length !== 0 && this.selectedFields.length === this.activeFields.length; - } - - public get areSomeFieldsSelected() { - return this.selectedFields.length > 0; - } - - public isFieldSelected(field: string) { - return this.selectedFields.indexOf(field) !== -1; - } - - public deactivateSelection() { - this.activeFields = [...this.activeFields.filter((field) => !this.isFieldSelected(field.csvColumn))]; - this.selectedFields = []; - } - - public setAttributeForSelection(attribute: string, value: any) { - for (const csvColumn of this.selectedFields) { - this.activeFields.find((f) => f.csvColumn === csvColumn)[attribute] = value; - } } public async save() { diff --git a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts index 71bdcc55b..e9b951c22 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts @@ -2,7 +2,7 @@ import { Component, Injector, OnInit } from '@angular/core'; import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http'; import { AppStateService } from '../../../../state/app-state.service'; -import { tap } from 'rxjs/operators'; +import { defaultIfEmpty, tap } from 'rxjs/operators'; import { forkJoin } from 'rxjs'; import { PermissionsService } from '../../../../services/permissions.service'; import { ActivatedRoute } from '@angular/router'; @@ -51,9 +51,11 @@ export class DictionaryListingScreenComponent extends BaseListingComponent { - this._calculateData(); - }); + forkJoin(dataObs) + .pipe(defaultIfEmpty(null)) + .subscribe(() => { + this._calculateData(); + }); } private _calculateData() { diff --git a/apps/red-ui/src/app/modules/projects/screens/project-overview-screen/project-overview-screen.component.html b/apps/red-ui/src/app/modules/projects/screens/project-overview-screen/project-overview-screen.component.html index 01b8ba998..f8c08808d 100644 --- a/apps/red-ui/src/app/modules/projects/screens/project-overview-screen/project-overview-screen.component.html +++ b/apps/red-ui/src/app/modules/projects/screens/project-overview-screen/project-overview-screen.component.html @@ -108,8 +108,6 @@ [selectedFileIds]="selectedEntitiesIds" (reload)="bulkActionPerformed()" > - - {{ selectedEntitiesIds.length }} selected
diff --git a/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts b/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts index e42e89b05..e2d99e543 100644 --- a/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts +++ b/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts @@ -88,6 +88,10 @@ export class BaseListingComponent { this.displayedEntities = (this.filters.length ? this.filteredEntities : this.allEntities).filter((entity) => this._searchField(entity).toLowerCase().includes(this.searchForm.get('query').value.toLowerCase()) ); + this._updateSelection(); + } + + protected _updateSelection() { if (this._selectionKey) { this.selectedEntitiesIds = this.displayedEntities.map((entity) => entity[this.selectionKey]).filter((id) => this.selectedEntitiesIds.includes(id)); }