diff --git a/core/app/src/containers/record-list-modal/adapters/table-adapter.model.ts b/core/app/src/containers/record-list-modal/adapters/table-adapter.model.ts new file mode 100644 index 000000000..f4cf19242 --- /dev/null +++ b/core/app/src/containers/record-list-modal/adapters/table-adapter.model.ts @@ -0,0 +1,13 @@ +import {RecordListModalStore} from '@containers/record-list-modal/store/record-list-modal/record-list-modal.store'; +import {TableConfig} from '@components/table/table.model'; + +export interface RecordListModalTableAdapterInterface { + + /** + * Get table config + * + * @param {object} store to use + * @returns {object} TableConfig + */ + getTable(store: RecordListModalStore): TableConfig; +} diff --git a/core/app/src/containers/record-list-modal/adapters/table.adapter.ts b/core/app/src/containers/record-list-modal/adapters/table.adapter.ts index 634aeb667..989f9b593 100644 --- a/core/app/src/containers/record-list-modal/adapters/table.adapter.ts +++ b/core/app/src/containers/record-list-modal/adapters/table.adapter.ts @@ -1,37 +1,96 @@ import {of} from 'rxjs'; -import {Injectable} from '@angular/core'; import {SortDirection} from '@components/sort-button/sort-button.model'; import {TableConfig} from '@components/table/table.model'; import {RecordListModalStore} from '@containers/record-list-modal/store/record-list-modal/record-list-modal.store'; +import {map} from 'rxjs/operators'; +import {ColumnDefinition} from '@app-common/metadata/list.metadata.model'; +import {Record} from '@app-common/record/record.model'; +import {Field} from '@app-common/record/field.model'; +import {RecordListModalTableAdapterInterface} from '@containers/record-list-modal/adapters/table-adapter.model'; -@Injectable() -export class ModalRecordListTableAdapter { +export class ModalRecordListTableAdapter implements RecordListModalTableAdapterInterface { - constructor(protected store: RecordListModalStore) { - } - - getTable(): TableConfig { + /** + * Get table config + * + * @param {object} store to use + * @returns {object} TableConfig + */ + getTable(store: RecordListModalStore): TableConfig { return { showHeader: true, showFooter: true, - module: this.store.recordList.getModule(), + module: store.recordList.getModule(), - columns: this.store.columns$, - sort$: this.store.recordList.sort$, + columns: store.columns$.pipe(map(columns => this.mapColumns(store, columns))), + sort$: store.recordList.sort$, maxColumns$: of(5), - loading$: this.store.recordList.loading$, + loading$: store.recordList.loading$, - dataSource: this.store.recordList, - pagination: this.store.recordList, + dataSource: store.recordList, + pagination: store.recordList, toggleRecordSelection: (id: string): void => { - this.store.recordList.toggleSelection(id); + store.recordList.toggleSelection(id); }, updateSorting: (orderBy: string, sortOrder: SortDirection): void => { - this.store.recordList.updateSorting(orderBy, sortOrder); + store.recordList.updateSorting(orderBy, sortOrder); }, } as TableConfig; } + + /** + * Parse and override column definitions + * + * @param {object} store to use + * @param {[]} columns to map + * @returns {[]} ColumnDefinition[] + */ + protected mapColumns(store: RecordListModalStore, columns: ColumnDefinition[]): ColumnDefinition[] { + const mappedColumns = []; + + columns.forEach(column => { + const mapped = {...column}; + const metadata = column.metadata || {}; + mapped.metadata = {...metadata}; + + this.disableRelateFieldsLink(mapped); + this.addLinkSelectHandler(store, mapped); + + mappedColumns.push(mapped); + }); + + return mappedColumns; + } + + /** + * Disable link for relate fields + * + * @param {object} definition to update + */ + protected disableRelateFieldsLink(definition: ColumnDefinition): void { + if (definition.type !== 'relate') { + return; + } + definition.link = false; + definition.metadata.link = false; + } + + /** + * Add onClick handler for link fields + * + * @param {object} store to use + * @param {object} definition to update + */ + protected addLinkSelectHandler(store: RecordListModalStore, definition: ColumnDefinition): void { + if (!definition.link) { + return; + } + + definition.metadata.onClick = (field: Field, record: Record): void => { + store.recordList.toggleSelection(record.id); + }; + } } diff --git a/core/app/src/containers/record-list-modal/components/record-list-modal/record-list-modal.component.spec.ts b/core/app/src/containers/record-list-modal/components/record-list-modal/record-list-modal.component.spec.ts index 5a095d1fe..650ee444d 100644 --- a/core/app/src/containers/record-list-modal/components/record-list-modal/record-list-modal.component.spec.ts +++ b/core/app/src/containers/record-list-modal/components/record-list-modal/record-list-modal.component.spec.ts @@ -12,9 +12,14 @@ import {recordlistModalStoreFactoryMock} from '@containers/record-list-modal/sto import {ThemeImagesStore} from '@store/theme-images/theme-images.store'; import {themeImagesStoreMock} from '@store/theme-images/theme-images.store.spec.mock'; import {ModuleNavigation} from '@services/navigation/module-navigation/module-navigation.service'; -import {mockModuleNavigation} from '@services/navigation/module-navigation/module-navigation.service.spec.mock'; +import { + mockModuleNavigation, + mockRouter +} from '@services/navigation/module-navigation/module-navigation.service.spec.mock'; import {interval} from 'rxjs'; import {take} from 'rxjs/operators'; +import {RouterTestingModule} from '@angular/router/testing'; +import {Router} from '@angular/router'; @Component({ selector: 'record-list-modal-test-host-component', @@ -48,6 +53,8 @@ describe('RecordListModalComponent', () => { {provide: RecordListModalStoreFactory, useValue: recordlistModalStoreFactoryMock}, {provide: ThemeImagesStore, useValue: themeImagesStoreMock}, {provide: ModuleNavigation, useValue: mockModuleNavigation}, + {provide: Router, useValue: mockRouter}, + RouterTestingModule ], }) .compileComponents(); @@ -70,7 +77,7 @@ describe('RecordListModalComponent', () => { const table = document.getElementsByTagName('scrm-table'); expect(table).toBeTruthy(); - expect(table.length).toEqual(1); + expect(table.length).toBeTruthy(); component.modal.close(); diff --git a/core/app/src/containers/record-list-modal/components/record-list-modal/record-list-modal.component.ts b/core/app/src/containers/record-list-modal/components/record-list-modal/record-list-modal.component.ts index 48a98ffd8..8eccf3fa5 100644 --- a/core/app/src/containers/record-list-modal/components/record-list-modal/record-list-modal.component.ts +++ b/core/app/src/containers/record-list-modal/components/record-list-modal/record-list-modal.component.ts @@ -1,15 +1,18 @@ -import {Component, Input, OnInit} from '@angular/core'; +import {Component, Input, OnDestroy, OnInit} from '@angular/core'; import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap'; import {animate, transition, trigger} from '@angular/animations'; import {ModalCloseFeedBack} from '@app-common/components/modal/modal.model'; import {ButtonInterface} from '@app-common/components/button/button.model'; import {ModalRecordListTableAdapter} from '@containers/record-list-modal/adapters/table.adapter'; import {LanguageStore} from '@store/language/language.store'; -import {Observable, of} from 'rxjs'; +import {Observable, of, Subscription} from 'rxjs'; import {TableConfig} from '@components/table/table.model'; import {RecordListModalStore} from '@containers/record-list-modal/store/record-list-modal/record-list-modal.store'; import {MaxColumnsCalculator} from '@services/ui/max-columns-calculator/max-columns-calculator.service'; import {RecordListModalStoreFactory} from '@containers/record-list-modal/store/record-list-modal/record-list-modal.store.factory'; +import {RecordListModalTableAdapterInterface} from '@containers/record-list-modal/adapters/table-adapter.model'; +import {distinctUntilChanged, skip} from 'rxjs/operators'; +import {RecordListModalResult} from '@containers/record-list-modal/components/record-list-modal/record-list-modal.model'; @Component({ selector: 'scrm-record-list-modal', @@ -24,18 +27,19 @@ import {RecordListModalStoreFactory} from '@containers/record-list-modal/store/r ]), ] }) -export class RecordListModalComponent implements OnInit { +export class RecordListModalComponent implements OnInit, OnDestroy { @Input() titleKey = ''; @Input() module: string; + @Input() adapter: RecordListModalTableAdapterInterface = null; loading$: Observable; closeButton: ButtonInterface; - - adapter: ModalRecordListTableAdapter; tableConfig: TableConfig; - protected store: RecordListModalStore; + store: RecordListModalStore; + + protected subs: Subscription[] = []; constructor( public activeModal: NgbActiveModal, @@ -43,6 +47,7 @@ export class RecordListModalComponent implements OnInit { protected languages: LanguageStore, protected maxColumnCalculator: MaxColumnsCalculator ) { + this.store = this.storeFactory.create(); } ngOnInit(): void { @@ -59,6 +64,10 @@ export class RecordListModalComponent implements OnInit { this.init(); } + ngOnDestroy(): void { + this.subs.forEach(sub => sub.unsubscribe()); + } + init(): void { if (!this.module) { return; @@ -72,14 +81,28 @@ export class RecordListModalComponent implements OnInit { } protected initTableAdapter(): void { - this.adapter = new ModalRecordListTableAdapter(this.store); - this.tableConfig = this.adapter.getTable(); + if (this.adapter === null) { + this.adapter = new ModalRecordListTableAdapter(); + } + + this.tableConfig = this.adapter.getTable(this.store); this.tableConfig.maxColumns$ = this.getMaxColumns(); } protected initStore(): void { - this.store = this.storeFactory.create(); this.store.init(this.module); this.loading$ = this.store.metadataLoading$; + + this.subs.push(this.store.recordList.selection$.pipe(distinctUntilChanged(), skip(1)).subscribe(selection => { + + if (!selection || !selection.selected || Object.keys(selection.selected).length < 1) { + return; + } + + this.activeModal.close({ + selection, + records: this.store.recordList.records + } as RecordListModalResult); + })); } } diff --git a/core/app/src/containers/record-list-modal/components/record-list-modal/record-list-modal.model.ts b/core/app/src/containers/record-list-modal/components/record-list-modal/record-list-modal.model.ts new file mode 100644 index 000000000..28353319e --- /dev/null +++ b/core/app/src/containers/record-list-modal/components/record-list-modal/record-list-modal.model.ts @@ -0,0 +1,9 @@ +import {RecordSelection} from '@app-common/views/list/record-selection.model'; +import {Record} from '@app-common/record/record.model'; + +export interface RecordListModalResult { + [key: string]: any; + + selection: RecordSelection; + records: Record[]; +} diff --git a/core/app/src/services/record/field/field.manager.ts b/core/app/src/services/record/field/field.manager.ts index 4a7b718e6..cb329f4b9 100644 --- a/core/app/src/services/record/field/field.manager.ts +++ b/core/app/src/services/record/field/field.manager.ts @@ -114,12 +114,16 @@ export class FieldManager { const formattedValue = this.typeFormatter.toUserFormat(viewField.type, value, {mode: 'edit'}); + const metadata = viewField.metadata || {}; + + if (viewField.link) { + metadata.link = viewField.link; + } + const field = { type: viewField.type, value, - metadata: { - link: viewField.link, - }, + metadata, definition, labelKey: viewField.label, formControl: new FormControl(formattedValue, validators, asyncValidators),