Update list view api data retrieval

- Update ListViewHandler to use new method to retrieve data
- Update View definitions api to send fieldDefinitions as sub-object
- Update table and list view components to use new data format
- Move common front end interfaces to a common module
- UnEscape and sanitize html characters in varchar field
-- Add htmlSanitize pipe
- Update unit tests
This commit is contained in:
Clemente Raposo 2020-09-10 17:51:33 +01:00 committed by Dillon-Brown
parent b1c2f9efb1
commit 994b2c69bf
49 changed files with 307 additions and 158 deletions

View file

@ -11,6 +11,7 @@ module.exports = {
"map": [
["@app", "./core/app/src/app"],
["@base", "./core/app/src"],
["@app-common", "./core/app/src/app-common"],
["@views", "./core/app/views"],
["@fields", "./core/app/fields"],
["@services", "./core/app/src/services"],

View file

@ -1,6 +1,6 @@
import {Input} from '@angular/core';
import {Field} from '../field.model';
import {FieldComponentInterface} from './field.interface';
import {Field} from '@app-common/record/field.model';
export class BaseFieldComponent implements FieldComponentInterface {
@Input() field: Field;

View file

@ -1,4 +1,4 @@
import {Field} from '../field.model';
import {Field} from '@app-common/record/field.model';
export interface FieldComponentInterface {
field?: Field;

View file

@ -6,7 +6,7 @@ import {UserPreferenceStore} from '@store/user-preference/user-preference.store'
import {SystemConfigStore} from '@store/system-config/system-config.store';
import {distinctUntilChanged} from 'rxjs/operators';
import {FormatCurrencyPipe} from '@base/pipes/format-currency/format-currency.pipe';
import {Field} from '@fields/field.model';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'currency-detail-field-test-host-component',

View file

@ -6,7 +6,7 @@ import {BehaviorSubject, of} from 'rxjs';
import {CommonModule} from '@angular/common';
import {UserPreferenceStore} from '@store/user-preference/user-preference.store';
import {SystemConfigStore} from '@store/system-config/system-config.store';
import {Field} from '@fields/field.model';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'date-detail-field-test-host-component',

View file

@ -6,7 +6,7 @@ import {distinctUntilChanged} from 'rxjs/operators';
import {BehaviorSubject, of} from 'rxjs';
import {UserPreferenceStore} from '@store/user-preference/user-preference.store';
import {SystemConfigStore} from '@store/system-config/system-config.store';
import {Field} from '@fields/field.model';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'datetime-detail-field-test-host-component',

View file

@ -1,7 +1,7 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {EmailDetailFieldsComponent} from './email.component';
import {Field} from '@fields/field.model';
import {Component} from '@angular/core';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'email-detail-field-test-host-component',

View file

@ -1,7 +1,7 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {EmailListFieldsComponent} from './email.component';
import {Field} from '@fields/field.model';
import {Component} from '@angular/core';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'email-list-field-test-host-component',

View file

@ -1,9 +1,9 @@
import {Component, Input} from '@angular/core';
import {Field} from './field.model';
import {viewFieldsMap} from './field.manifest';
import {Record} from '@store/list-view/list-view.store';
import {ModuleNavigation} from '@services/navigation/module-navigation/module-navigation.service';
import {Record} from '@app-common/record/record.model';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'scrm-field',

View file

@ -1,19 +0,0 @@
import {SearchCriteriaFieldFilter} from '@store/list-view/list-view.store';
export interface FieldMetadata {
format?: boolean;
target?: string;
link?: boolean;
rows?: number;
cols?: number;
}
export interface Field {
type: string;
value?: string;
name?: string;
label?: string;
labelKey?: string;
metadata?: FieldMetadata;
criteria?: SearchCriteriaFieldFilter;
}

View file

@ -6,7 +6,7 @@ import {UserPreferenceStore} from '@store/user-preference/user-preference.store'
import {SystemConfigStore} from '@store/system-config/system-config.store';
import {FormatNumberPipe} from '@base/pipes/format-number/format-number.pipe';
import {distinctUntilChanged} from 'rxjs/operators';
import {Field} from '@fields/field.model';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'float-detail-field-test-host-component',

View file

@ -5,7 +5,7 @@ import {UserPreferenceStore} from '@store/user-preference/user-preference.store'
import {BehaviorSubject, of} from 'rxjs';
import {SystemConfigStore} from '@store/system-config/system-config.store';
import {FormatNumberPipe} from '@base/pipes/format-number/format-number.pipe';
import {Field} from '@fields/field.model';
import {Field} from '@app-common/record/field.model';
@Component({

View file

@ -1,7 +1,7 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {Component} from '@angular/core';
import {PhoneDetailFieldComponent} from './phone.component';
import {Field} from '@fields/field.model';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'phone-detail-field-test-host-component',

View file

@ -1,7 +1,7 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {Component} from '@angular/core';
import {TextDetailFieldComponent} from './text.component';
import {Field} from '@fields/field.model';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'text-detail-field-test-host-component',

View file

@ -1,7 +1,7 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {Component} from '@angular/core';
import {UrlDetailFieldComponent} from './url.component';
import {Field, FieldMetadata} from '@fields/field.model';
import {Field, FieldMetadata} from '@app-common/record/field.model';
@Component({
selector: 'url-detail-field-test-host-component',

View file

@ -1 +1 @@
{{field.value}}
<span [innerHTML]="field.value | htmlSanitize"></span>

View file

@ -1,7 +1,8 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {Component} from '@angular/core';
import {VarcharDetailFieldComponent} from './varchar.component';
import {Field} from '@fields/field.model';
import {Field} from '@app-common/record/field.model';
import {HtmlSanitizeModule} from '@base/pipes/html-sanitize/html-sanitize.module';
@Component({
selector: 'varchar-detail-field-test-host-component',
@ -24,7 +25,9 @@ describe('VarcharDetailFieldComponent', () => {
VarcharDetailFieldTestHostComponent,
VarcharDetailFieldComponent,
],
imports: [],
imports: [
HtmlSanitizeModule
],
providers: [],
}).compileComponents();

View file

@ -3,13 +3,15 @@ import {CommonModule} from '@angular/common';
import {AppManagerModule} from '@base/app-manager/app-manager.module';
import {VarcharDetailFieldComponent} from './varchar.component';
import {HtmlSanitizeModule} from '@base/pipes/html-sanitize/html-sanitize.module';
@NgModule({
declarations: [VarcharDetailFieldComponent],
exports: [VarcharDetailFieldComponent],
imports: [
CommonModule,
AppManagerModule.forChild(VarcharDetailFieldComponent)
AppManagerModule.forChild(VarcharDetailFieldComponent),
HtmlSanitizeModule
]
})
export class VarcharDetailFieldModule {

View file

@ -1,8 +1,8 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {Component} from '@angular/core';
import {VarcharEditFieldComponent} from './varchar.component';
import {Field} from '@fields/field.model';
import {FormsModule} from '@angular/forms';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'varchar-edit-field-test-host-component',

View file

@ -1,8 +1,8 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {Component} from '@angular/core';
import {VarcharFilterFieldComponent} from './filter.component';
import {Field} from '@fields/field.model';
import {FormsModule} from '@angular/forms';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'varchar-filter-field-test-host-component',

View file

@ -0,0 +1,12 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class AppCommonModule {
}

View file

@ -0,0 +1,9 @@
import {FieldDefinition} from '@app-common/record/field.model';
export interface ViewFieldDefinition {
name?: string;
label?: string;
link?: boolean;
type?: string;
fieldDefinition?: FieldDefinition;
}

View file

@ -0,0 +1,68 @@
import {Record} from './record.model';
import {ViewFieldDefinition} from '@app-common/metadata/metadata.model';
import {SearchCriteriaFieldFilter} from '@app-common/views/list/search-criteria.model';
export interface FieldDefinition {
name?: string;
type?: string; // label key to use
vname?: string; // original label
reportable?: boolean;
required?: boolean;
// eslint-disable-next-line camelcase
duplicate_merge?: string;
source?: string;
id?: string;
// eslint-disable-next-line camelcase
id_name?: string;
link?: string;
module?: string;
rname?: string;
table?: string;
}
export interface FieldMetadata {
format?: boolean;
target?: string;
link?: boolean;
rows?: number;
cols?: number;
}
export interface Field {
type: string;
value?: string;
name?: string;
label?: string;
labelKey?: string;
metadata?: FieldMetadata;
definition?: FieldDefinition;
criteria?: SearchCriteriaFieldFilter;
}
export class FieldManager {
public static buildField(record: Record, viewField: ViewFieldDefinition): Field {
const definition = (viewField && viewField.fieldDefinition) || {} as FieldDefinition;
const type = (viewField && viewField.type) || '';
const source = (definition && definition.source) || '';
const rname = (definition && definition.rname) || '';
let value;
if (type === 'relate' && source === 'non-db' && rname !== '') {
value = record.attributes[viewField.name][rname];
} else {
value = record.attributes[viewField.name];
}
return {
type: viewField.type,
value,
metadata: {
link: viewField.link,
},
definition,
labelKey: viewField.label
} as Field;
}
}

View file

@ -0,0 +1,10 @@
export interface FieldMap {
[key: string]: any;
}
export interface Record {
type: string;
module: string;
attributes: FieldMap;
id?: string;
}

View file

@ -0,0 +1,17 @@
export interface SearchCriteriaFieldFilter {
field?: string;
operator: string;
values?: string[];
start?: string;
end?: string;
}
export interface SearchCriteriaFilter {
[key: string]: SearchCriteriaFieldFilter;
}
export interface SearchCriteria {
name?: string;
type?: string;
filters: SearchCriteriaFilter;
}

View file

@ -1,11 +1,12 @@
import {Injectable} from '@angular/core';
import {Observable} from 'rxjs';
import {ListViewStore, Record} from '@store/list-view/list-view.store';
import {ListViewStore} from '@store/list-view/list-view.store';
import {map} from 'rxjs/operators';
import {LineChartDataSource, LineChartResult} from '@components/chart/charts/line-chart/line-chart.model';
import {SystemConfigStore} from '@store/system-config/system-config.store';
import {DateTime} from 'luxon';
import {LanguageStore} from '@store/language/language.store';
import {Record} from '@app-common/record/record.model';
@Injectable()
export class AccountTypesPerMonthLineChart implements LineChartDataSource {

View file

@ -1,12 +1,13 @@
import {Injectable} from '@angular/core';
import {Observable} from 'rxjs';
import {Record, ListViewStore} from '@store/list-view/list-view.store';
import {ListViewStore} from '@store/list-view/list-view.store';
import {map} from 'rxjs/operators';
import {
PieGridChartChartDataSource,
PieGridChartResult
} from '@components/chart/charts/pie-grid-chart/pie-grid-chart.model';
import {LanguageStore} from '@store/language/language.store';
import {Record} from '@app-common/record/record.model';
@Injectable()
export class LeadsByStatus implements PieGridChartChartDataSource {

View file

@ -4,11 +4,12 @@ import {
VerticalBarChartResult
} from '@components/chart/charts/vertical-bar-chart/vertical-bar-chart.model';
import {Observable} from 'rxjs';
import {Record, ListViewStore} from '@store/list-view/list-view.store';
import {ListViewStore} from '@store/list-view/list-view.store';
import {map} from 'rxjs/operators';
import {LanguageStore} from '@store/language/language.store';
import {UserPreferenceStore} from '@store/user-preference/user-preference.store';
import {SystemConfigStore} from '@store/system-config/system-config.store';
import {Record} from '@app-common/record/record.model';
@Injectable()
export class PipelineBySalesStage implements VerticalBarChartDataSource {

View file

@ -7,10 +7,10 @@ import {DropdownButtonModule} from '@components/dropdown-button/dropdown-button.
import {BrowserDynamicTestingModule} from '@angular/platform-browser-dynamic/testing';
import {LayoutModule} from '@angular/cdk/layout';
import {FieldModule} from '@fields/field.module';
import {Field} from '@fields/field.model';
import {By} from '@angular/platform-browser';
import {CommonModule} from '@angular/common';
import {RouterTestingModule} from '@angular/router/testing';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'field-grid-host-component',

View file

@ -1,8 +1,8 @@
import {Component, Input, OnChanges, OnDestroy, OnInit} from '@angular/core';
import {Field} from '@fields/field.model';
import {FieldGridColumn, FieldGridRow} from '@components/field-grid/field-grid.model';
import {BreakpointObserver, Breakpoints, BreakpointState} from '@angular/cdk/layout';
import {Subscription} from 'rxjs';
import {Field} from '@app-common/record/field.model';
@Component({
selector: 'scrm-field-grid',

View file

@ -1,5 +1,5 @@
import {Field} from '@fields/field.model';
import {ButtonInterface} from '@components/button/button.model';
import {Field} from '@app-common/record/field.model';
export interface FieldGridColumn {
field?: Field;

View file

@ -1,7 +1,7 @@
import {Component, Input, OnInit} from '@angular/core';
import {LanguageStore} from '@store/language/language.store';
import {LineAction, ListViewMeta} from '@store/metadata/metadata.store.service';
import {Record} from '@store/list-view/list-view.store';
import {Record} from '@app-common/record/record.model';
@Component({
selector: 'scrm-line-action-menu',

View file

@ -1,13 +1,14 @@
import {Component, OnInit} from '@angular/core';
import {BulkActionsMap, Filter, SearchMetaField} from '@store/metadata/metadata.store.service';
import {Filter, SearchMetaField} from '@store/metadata/metadata.store.service';
import {LanguageStore, LanguageStringMap, LanguageStrings} from '@store/language/language.store';
import {Field} from '@fields/field.model';
import {ListViewStore, SearchCriteria, SearchCriteriaFieldFilter} from '@store/list-view/list-view.store';
import {ListViewStore} from '@store/list-view/list-view.store';
import {DropdownButtonInterface} from '@components/dropdown-button/dropdown-button.model';
import {ButtonInterface} from '@components/button/button.model';
import {deepClone} from '@base/utils/object-utils';
import {combineLatest, Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {Field} from '@app-common/record/field.model';
import {SearchCriteria, SearchCriteriaFieldFilter} from '@app-common/views/list/search-criteria.model';
export interface FilterDataSource {
getFilter(): Observable<Filter>;

View file

@ -1,5 +1,5 @@
import {Component, OnInit} from '@angular/core';
import {ListViewStore, SearchCriteriaFilter} from '@store/list-view/list-view.store';
import {ListViewStore} from '@store/list-view/list-view.store';
import {ButtonInterface} from '@components/button/button.model';
import {DropdownButtonInterface} from '@components/dropdown-button/dropdown-button.model';
import {ButtonGroupInterface} from '@components/button-group/button-group.model';
@ -9,6 +9,7 @@ import {BehaviorSubject, combineLatest} from 'rxjs';
import {map} from 'rxjs/operators';
import {ScreenSize, ScreenSizeObserverService} from '@services/ui/screen-size-observer/screen-size-observer.service';
import {SystemConfigStore} from '@store/system-config/system-config.store';
import {SearchCriteriaFilter} from '@app-common/views/list/search-criteria.model';
@Component({
selector: 'scrm-settings-menu',

View file

@ -15,7 +15,7 @@
</td>
</ng-container>
<ng-container *ngFor="let column of vm.listMetadata.fields" [cdkColumnDef]="column.fieldName">
<ng-container *ngFor="let column of vm.listMetadata.fields" [cdkColumnDef]="column.name">
<th cdk-header-cell
*cdkHeaderCellDef
scope="col"

View file

@ -1,14 +1,15 @@
import {Component, Input} from '@angular/core';
import {combineLatest, Observable} from 'rxjs';
import {LanguageStore, LanguageStrings} from '@store/language/language.store';
import {ListField, ListViewMeta, MetadataStore} from '@store/metadata/metadata.store.service';
import {ColumnDefinition, ListViewMeta, MetadataStore} from '@store/metadata/metadata.store.service';
import {map} from 'rxjs/operators';
import {ListViewStore, Record, RecordSelection, SortingSelection} from '@store/list-view/list-view.store';
import {ListViewStore, RecordSelection, SortingSelection} from '@store/list-view/list-view.store';
import {SelectionStatus} from '@components/bulk-action-menu/bulk-action-menu.component';
import {SortDirection, SortDirectionDataSource} from '@components/sort-button/sort-button.model';
import {ScreenSize, ScreenSizeObserverService} from '@services/ui/screen-size-observer/screen-size-observer.service';
import {SystemConfigStore} from '@store/system-config/system-config.store';
import {Field} from '@fields/field.model';
import {Record} from '@app-common/record/record.model';
import {Field, FieldManager} from '@app-common/record/field.model';
@Component({
selector: 'scrm-table-body',
@ -91,15 +92,15 @@ export class TableBodyComponent {
let hasLinkField = false;
const returnArray = [];
while (i < this.maxColumns && i < listMetadata.fields.length) {
returnArray.push(listMetadata.fields[i].fieldName);
returnArray.push(listMetadata.fields[i].name);
hasLinkField = hasLinkField || listMetadata.fields[i].link;
i++;
}
if (!hasLinkField && (this.maxColumns < listMetadata.fields.length)) {
for (i = this.maxColumns; i < listMetadata.fields.length; i++) {
if (listMetadata.fields[i].link) {
returnArray.splice(-1,1);
returnArray.push(listMetadata.fields[i].fieldName);
returnArray.splice(-1, 1);
returnArray.push(listMetadata.fields[i].name);
break;
}
}
@ -131,13 +132,13 @@ export class TableBodyComponent {
return this.language.getFieldLabel(label, module, languages);
}
getFieldSort(field: ListField): SortDirectionDataSource {
getFieldSort(field: ColumnDefinition): SortDirectionDataSource {
return {
getSortDirection: (): Observable<SortDirection> => this.sort$.pipe(
map((sort: SortingSelection) => {
let direction = SortDirection.NONE;
if (sort.orderBy === field.fieldName) {
if (sort.orderBy === field.name) {
direction = sort.sortOrder;
}
@ -145,20 +146,14 @@ export class TableBodyComponent {
})
),
changeSortDirection: (direction: SortDirection): void => {
this.changeSort(field.fieldName, direction);
this.changeSort(field.name, direction);
}
} as SortDirectionDataSource;
}
getField(column: ListField, record: Record): Field {
return {
type: column.type,
value: record.attributes[column.fieldName],
metadata: {
link: column.link,
},
labelKey: column.label
} as Field;
getField(column: ColumnDefinition, record: Record): Field {
return FieldManager.buildField(record, column);
}
protected changeSort(orderBy: string, sortOrder: SortDirection): void {

View file

@ -0,0 +1,18 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {HtmlSanitizePipe} from '@base/pipes/html-sanitize/html-sanitize.pipe';
@NgModule({
declarations: [
HtmlSanitizePipe
],
exports: [
HtmlSanitizePipe
],
imports: [
CommonModule
]
})
export class HtmlSanitizeModule {
}

View file

@ -0,0 +1,37 @@
import {HtmlSanitizePipe} from './html-sanitize.pipe';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {Component} from '@angular/core';
import {BrowserDynamicTestingModule} from '@angular/platform-browser-dynamic/testing';
@Component({
selector: 'html-sanitize-test-host-component',
template: '<span [innerHTML]="value | htmlSanitize"></span>'
})
class HtmlSanitizePipeTestHostComponent {
value = 'V8 Api test Account &amp;&ccedil;!&quot;#$%&amp;/`&agrave;';
}
describe('HtmlSanitizePipe', () => {
let testHostComponent: HtmlSanitizePipeTestHostComponent;
let testHostFixture: ComponentFixture<HtmlSanitizePipeTestHostComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
HtmlSanitizePipeTestHostComponent,
HtmlSanitizePipe,
],
imports: [BrowserDynamicTestingModule],
providers: [],
}).compileComponents();
testHostFixture = TestBed.createComponent(HtmlSanitizePipeTestHostComponent);
testHostComponent = testHostFixture.componentInstance;
testHostFixture.detectChanges();
}));
it('create an instance', () => {
expect(testHostComponent).toBeTruthy();
expect(testHostFixture.nativeElement.textContent).toContain('V8 Api test Account &ç!"#$%&/`à');
});
});

View file

@ -0,0 +1,15 @@
import {Pipe, PipeTransform, SecurityContext} from '@angular/core';
import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
@Pipe({
name: 'htmlSanitize'
})
export class HtmlSanitizePipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {
}
transform(data): SafeHtml {
return this.sanitizer.sanitize(SecurityContext.HTML, data);
}
}

View file

@ -3,11 +3,12 @@ import {Observable} from 'rxjs';
import {catchError, take, tap} from 'rxjs/operators';
import {Process, ProcessService} from '@services/process/process.service';
import {AppStateStore} from '@store/app-state/app-state.store';
import {SearchCriteria, SortingSelection} from '@store/list-view/list-view.store';
import {SortingSelection} from '@store/list-view/list-view.store';
import {MessageService} from '@services/message/message.service';
import {BulkActionHandler} from '@services/process/processes/bulk-action/bulk-action.model';
import {RedirectBulkAction} from '@services/process/processes/bulk-action/actions/redirect/redirect.bulk-action';
import {ExportBulkAction} from '@services/process/processes/bulk-action/actions/export/export.bulk-action';
import {SearchCriteria} from '@app-common/views/list/search-criteria.model';
export interface BulkActionProcessInput {
action: string;

View file

@ -1,9 +1,10 @@
import {ListViewStore, SearchCriteria} from '@store/list-view/list-view.store';
import {ListViewStore} from '@store/list-view/list-view.store';
import {listviewMockData, listviewStoreMock} from '@store/list-view/list-view.store.spec.mock';
import {SelectionStatus} from '@components/bulk-action-menu/bulk-action-menu.component';
import {take} from 'rxjs/operators';
import {PageSelection} from '@components/pagination/pagination.model';
import {localStorageServiceMock} from '@services/local-storage/local-storage.service.spec.mock';
import {SearchCriteria} from '@app-common/views/list/search-criteria.model';
describe('Listview Store', () => {
const service: ListViewStore = listviewStoreMock;

View file

@ -26,35 +26,8 @@ import {SortDirection} from '@components/sort-button/sort-button.model';
import {BulkActionProcess, BulkActionProcessInput} from '@services/process/processes/bulk-action/bulk-action';
import {MessageService} from '@services/message/message.service';
import {Process} from '@services/process/process.service';
export interface FieldMap {
[key: string]: any;
}
export interface Record {
type: string;
module: string;
attributes: FieldMap;
id?: string;
}
export interface SearchCriteriaFieldFilter {
field?: string;
operator: string;
values?: string[];
start?: string;
end?: string;
}
export interface SearchCriteriaFilter {
[key: string]: SearchCriteriaFieldFilter;
}
export interface SearchCriteria {
name?: string;
type?: string;
filters: SearchCriteriaFilter;
}
import {Record} from '@app-common/record/record.model';
import {SearchCriteria} from '@app-common/views/list/search-criteria.model';
const initialSearchCriteria = {
filters: {}
@ -536,7 +509,7 @@ export class ListViewStore extends ViewStore implements StateStore,
const displayedFields = [];
this.metadata.listView.fields.forEach(value => {
displayedFields.push(value.fieldName);
displayedFields.push(value.name);
});
const data = {
@ -761,7 +734,7 @@ export class ListViewStore extends ViewStore implements StateStore,
.pipe(map(({data}) => {
const recordsList: ListData = {
records: [],
pagination: {} as Pagination
pagination: {...pagination} as Pagination
};
if (!data || !data.getListView) {

View file

@ -6,6 +6,7 @@ import {deepClone} from '@base/utils/object-utils';
import {StateStore} from '@base/store/state';
import {AppStateStore} from '@store/app-state/app-state.store';
import {MenuItemLink} from '@components/navbar/navbar.abstract';
import {ViewFieldDefinition} from '@app-common/metadata/metadata.model';
export interface ChartType {
key: string;
@ -44,7 +45,7 @@ export interface LineAction {
}
export interface ListViewMeta {
fields: ListField[];
fields: ColumnDefinition[];
bulkActions: BulkActionsMap;
lineActions: LineAction[];
chartTypes: ChartTypesMap;
@ -57,16 +58,12 @@ export interface Filter {
contents: { [key: string]: any };
}
export interface ListField {
fieldName: string;
export interface ColumnDefinition extends ViewFieldDefinition {
width: string;
label: string;
link: boolean;
default: boolean;
module: string;
id: string;
sortable: boolean;
type: string;
}
export interface SearchMetaField {
@ -117,7 +114,7 @@ export class MetadataStore implements StateStore {
/**
* Public long-lived observable streams
*/
fields$: Observable<ListField[]>;
fields$: Observable<ColumnDefinition[]>;
listMetadata$: Observable<ListViewMeta>;
searchMetadata$: Observable<SearchMeta>;
metadata$: Observable<Metadata>;
@ -291,7 +288,7 @@ export class MetadataStore implements StateStore {
};
if (data.viewDefinition.listView.columns) {
data.viewDefinition.listView.columns.forEach((field: ListField) => {
data.viewDefinition.listView.columns.forEach((field: ColumnDefinition) => {
listViewMeta.fields.push(
field
);

View file

@ -184,7 +184,7 @@ export const metadataMockData = {
},
columns: [
{
fieldName: 'name',
name: 'name',
width: '20%',
label: 'LBL_LIST_ACCOUNT_NAME',
link: true,
@ -194,7 +194,7 @@ export const metadataMockData = {
sortable: false
},
{
fieldName: 'billing_address_city',
name: 'billing_address_city',
width: '10%',
label: 'LBL_LIST_CITY',
link: false,
@ -204,7 +204,7 @@ export const metadataMockData = {
sortable: false
},
{
fieldName: 'billing_address_country',
name: 'billing_address_country',
width: '10%',
label: 'LBL_BILLING_ADDRESS_COUNTRY',
link: false,
@ -214,7 +214,7 @@ export const metadataMockData = {
sortable: false
},
{
fieldName: 'phone_office',
name: 'phone_office',
width: '10%',
label: 'LBL_LIST_PHONE',
link: false,
@ -224,7 +224,7 @@ export const metadataMockData = {
sortable: false
},
{
fieldName: 'assigned_user_name',
name: 'assigned_user_name',
width: '10%',
label: 'LBL_LIST_ASSIGNED_USER',
link: false,
@ -234,7 +234,7 @@ export const metadataMockData = {
sortable: false
},
{
fieldName: 'email1',
name: 'email1',
width: '15%',
label: 'LBL_EMAIL_ADDRESS',
link: true,
@ -245,7 +245,7 @@ export const metadataMockData = {
customCode: '{$EMAIL1_LINK}'
},
{
fieldName: 'date_entered',
name: 'date_entered',
width: '5%',
label: 'LBL_DATE_ENTERED',
link: false,

View file

@ -12,8 +12,8 @@ import {ModuleNavigation} from '@services/navigation/module-navigation/module-na
import {LocalStorageService} from '@services/local-storage/local-storage.service';
import {MessageService} from '@services/message/message.service';
import {distinctUntilChanged, map, shareReplay, tap, catchError} from 'rxjs/operators';
import {Record} from '@store/list-view/list-view.store';
import {RecordViewGQL} from '@store/record-view/api.record.get';
import {Record} from '@app-common/record/record.model';
export interface RecordViewModel {
data: RecordViewData;

View file

@ -15,13 +15,30 @@
"../../node_modules/@types"
],
"paths": {
"@app/*": ["./src/app/*"],
"@base/*": ["./src/*"],
"@views/*": ["./views/*"],
"@fields/*": ["./fields/*"],
"@services/*": ["./src/services/*"],
"@components/*": ["./src/components/*"],
"@store/*": ["./src/store/*"]
"@app/*": [
"./src/app/*"
],
"@base/*": [
"./src/*"
],
"@app-common/*": [
"./src/app-common/*"
],
"@views/*": [
"./views/*"
],
"@fields/*": [
"./fields/*"
],
"@services/*": [
"./src/services/*"
],
"@components/*": [
"./src/components/*"
],
"@store/*": [
"./src/store/*"
]
},
"lib": [
"es2018",

View file

@ -247,7 +247,7 @@ class ListViewHandler extends LegacyHandler implements ListViewProviderInterface
require_once 'include/portability/ListView/ListViewDataPort.php';
$listViewData = new ListViewDataPort();
return $listViewData->getListViewData($bean, $where, $offset, $limit, $filter_fields, $params);
return $listViewData->get($bean, $where, $offset, $limit, $filter_fields, $params);
}
/**

View file

@ -25,13 +25,11 @@ class ViewDefinitionsHandler extends LegacyHandler implements ViewDefinitionsPro
{
public const HANDLER_KEY = 'view-definitions';
/**
* @var array
*/
protected static $listViewColumnInterface = [
'fieldName' => '',
'name' => '',
'width' => '',
'label' => '',
'link' => false,
@ -72,21 +70,19 @@ class ViewDefinitionsHandler extends LegacyHandler implements ViewDefinitionsPro
*/
private $filterDefinitionProvider;
/**
* @var array
*/
private $defaultFields = [
'type' => 'fieldType',
'type' => 'type',
'label' => 'vname',
];
private $vardefsToRename = [
'type' => 'fieldType',
];
/**
* @var ModuleNameMapperInterface
*/
protected $moduleNameMapper;
/**
* ViewDefinitionsHandler constructor.
* @param string $projectDir
@ -272,11 +268,11 @@ class ViewDefinitionsHandler extends LegacyHandler implements ViewDefinitionsPro
protected function buildListViewColumn($column, $key, ?array $vardefs): array
{
$column = array_merge(self::$listViewColumnInterface, $column);
$column['fieldName'] = strtolower($key);
$column['name'] = strtolower($key);
$column = $this->addFieldDefinition($vardefs, strtolower($key), $column);
if ($column['fieldName'] === 'email1') {
if ($column['name'] === 'email1') {
$column['type'] = 'email';
$column['link'] = false;
}
@ -360,17 +356,7 @@ class ViewDefinitionsHandler extends LegacyHandler implements ViewDefinitionsPro
return $field;
}
foreach( $vardefs[$key] as $attributeName => $attributeValue){
$attribute = $attributeName;
if (!empty($this->vardefsToRename[$attributeName])){
$attribute = $this->vardefsToRename[$attributeName];
}
if (!isset($field[$attribute]) || empty($field[$attribute])) {
$field[$attribute] = $attributeValue;
}
}
$field['fieldDefinition'] = $vardefs[$key];
$field = $this->applyDefaults($field);
@ -390,10 +376,6 @@ class ViewDefinitionsHandler extends LegacyHandler implements ViewDefinitionsPro
foreach ($definition['layout'][$type] as $key => $field) {
$name = $field['name'] ?? '';
if (empty($name)) {
$name = $field['fieldName'] ?? '';
}
if ($this->useRangeSearch($module, $searchDefs, $name)) {
$definition['layout'][$type][$key]['enable_range_search'] = true;
}
@ -478,7 +460,6 @@ class ViewDefinitionsHandler extends LegacyHandler implements ViewDefinitionsPro
if (is_string($field)) {
$baseField = [
'name' => $field,
'fieldName' => $field
];
}
@ -487,14 +468,14 @@ class ViewDefinitionsHandler extends LegacyHandler implements ViewDefinitionsPro
/**
* Apply defaults
* @param $field
* @return mixed
* @param array $field
* @return array
*/
protected function applyDefaults($field)
protected function applyDefaults(array $field): array
{
foreach ($this->defaultFields as $attribute => $default) {
if (empty($field[$attribute])) {
$defaultValue = $field[$default] ?? '';
$defaultValue = $field['fieldDefinition'][$default] ?? '';
$field[$attribute] = $defaultValue;
}
}

View file

@ -219,12 +219,18 @@ final class ViewDefinitionsHandlerTest extends Unit
static::assertIsArray($firstColumn);
static::assertNotEmpty($firstColumn);
static::assertArrayHasKey('fieldName', $firstColumn);
static::assertArrayHasKey('name', $firstColumn);
static::assertArrayHasKey('label', $firstColumn);
static::assertArrayHasKey('link', $firstColumn);
static::assertIsBool($firstColumn['link']);
static::assertArrayHasKey('sortable', $firstColumn);
static::assertIsBool($firstColumn['sortable']);
static::assertArrayHasKey('fieldDefinition', $firstColumn);
static::assertIsArray($firstColumn['fieldDefinition']);
static::assertArrayHasKey('name', $firstColumn['fieldDefinition']);
static::assertArrayHasKey('type', $firstColumn['fieldDefinition']);
static::assertArrayHasKey('vname', $firstColumn['fieldDefinition']);
$actions = $listViewDefs->getListView()['bulkActions'];
static::assertIsArray($actions);