|
|
|
@ -15,20 +15,22 @@ |
|
|
|
///
|
|
|
|
|
|
|
|
import { |
|
|
|
AfterViewInit, |
|
|
|
ChangeDetectorRef, |
|
|
|
Component, |
|
|
|
DestroyRef, |
|
|
|
forwardRef, |
|
|
|
Input, |
|
|
|
OnChanges, |
|
|
|
Renderer2, |
|
|
|
SimpleChanges, |
|
|
|
ViewChild, |
|
|
|
ViewContainerRef, |
|
|
|
} from '@angular/core'; |
|
|
|
import { |
|
|
|
AbstractControl, |
|
|
|
ControlValueAccessor, |
|
|
|
FormBuilder, |
|
|
|
FormGroup, |
|
|
|
NG_VALIDATORS, |
|
|
|
NG_VALUE_ACCESSOR, |
|
|
|
ValidationErrors, |
|
|
|
@ -48,8 +50,11 @@ import { TbPopoverService } from '@shared/components/popover.service'; |
|
|
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
|
|
|
import { EntityId } from '@shared/models/id/entity-id'; |
|
|
|
import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; |
|
|
|
import { isDefined, isDefinedAndNotNull } from '@core/utils'; |
|
|
|
import { getEntityDetailsPageURL, isDefined, isDefinedAndNotNull } from '@core/utils'; |
|
|
|
import { TbPopoverComponent } from '@shared/components/popover.component'; |
|
|
|
import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract'; |
|
|
|
import { EntityService } from '@core/http/entity.service'; |
|
|
|
import { MatSort } from '@angular/material/sort'; |
|
|
|
|
|
|
|
@Component({ |
|
|
|
selector: 'tb-calculated-field-arguments-table', |
|
|
|
@ -68,19 +73,23 @@ import { TbPopoverComponent } from '@shared/components/popover.component'; |
|
|
|
} |
|
|
|
], |
|
|
|
}) |
|
|
|
export class CalculatedFieldArgumentsTableComponent implements ControlValueAccessor, Validator, OnChanges { |
|
|
|
export class CalculatedFieldArgumentsTableComponent implements ControlValueAccessor, Validator, OnChanges, AfterViewInit { |
|
|
|
|
|
|
|
@Input() entityId: EntityId; |
|
|
|
@Input() tenantId: string; |
|
|
|
@Input() entityName: string; |
|
|
|
@Input() calculatedFieldType: CalculatedFieldType; |
|
|
|
|
|
|
|
@ViewChild(MatSort, { static: true }) sort: MatSort; |
|
|
|
|
|
|
|
errorText = ''; |
|
|
|
argumentsFormArray = this.fb.array<AbstractControl>([]); |
|
|
|
entityNameMap = new Map<string, string>(); |
|
|
|
sortOrder = { direction: 'asc', property: '' }; |
|
|
|
dataSource = new CalculatedFieldArgumentDatasource(); |
|
|
|
|
|
|
|
readonly entityTypeTranslations = entityTypeTranslations; |
|
|
|
readonly ArgumentTypeTranslations = ArgumentTypeTranslations; |
|
|
|
readonly EntityType = EntityType; |
|
|
|
readonly ArgumentEntityType = ArgumentEntityType; |
|
|
|
readonly ArgumentType = ArgumentType; |
|
|
|
readonly CalculatedFieldType = CalculatedFieldType; |
|
|
|
@ -93,10 +102,14 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces |
|
|
|
private popoverService: TbPopoverService, |
|
|
|
private viewContainerRef: ViewContainerRef, |
|
|
|
private cd: ChangeDetectorRef, |
|
|
|
private renderer: Renderer2 |
|
|
|
private renderer: Renderer2, |
|
|
|
private entityService: EntityService, |
|
|
|
private destroyRef: DestroyRef, |
|
|
|
) { |
|
|
|
this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(() => { |
|
|
|
this.propagateChange(this.getArgumentsObject()); |
|
|
|
this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => { |
|
|
|
this.updateEntityNameMap(value); |
|
|
|
this.updateDataSource(value); |
|
|
|
this.propagateChange(this.getArgumentsObject(value)); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
@ -107,6 +120,14 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ngAfterViewInit(): void { |
|
|
|
this.sort.sortChange.asObservable().pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { |
|
|
|
this.sortOrder.property = this.sort.active; |
|
|
|
this.sortOrder.direction = this.sort.direction; |
|
|
|
this.updateDataSource(this.argumentsFormArray.value); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
registerOnChange(fn: (argumentsObj: Record<string, CalculatedFieldArgument>) => void): void { |
|
|
|
this.propagateChange = fn; |
|
|
|
} |
|
|
|
@ -118,12 +139,13 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces |
|
|
|
return this.errorText ? { argumentsFormArray: false } : null; |
|
|
|
} |
|
|
|
|
|
|
|
onDelete(index: number): void { |
|
|
|
onDelete($event: Event, index: number): void { |
|
|
|
$event.stopPropagation(); |
|
|
|
this.argumentsFormArray.removeAt(index); |
|
|
|
this.argumentsFormArray.markAsDirty(); |
|
|
|
} |
|
|
|
|
|
|
|
manageArgument($event: Event, matButton: MatButton, index?: number): void { |
|
|
|
manageArgument($event: Event, matButton: MatButton, argument = {} as CalculatedFieldArgumentValue, index?: number): void { |
|
|
|
$event?.stopPropagation(); |
|
|
|
if (this.popoverComponent && !this.popoverComponent.tbHidden) { |
|
|
|
this.popoverComponent.hide(); |
|
|
|
@ -132,16 +154,15 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces |
|
|
|
if (this.popoverService.hasPopover(trigger)) { |
|
|
|
this.popoverService.hidePopover(trigger); |
|
|
|
} else { |
|
|
|
const argumentObj = this.argumentsFormArray.at(index)?.getRawValue() ?? {}; |
|
|
|
const ctx = { |
|
|
|
index, |
|
|
|
argument: argumentObj, |
|
|
|
argument, |
|
|
|
entityId: this.entityId, |
|
|
|
calculatedFieldType: this.calculatedFieldType, |
|
|
|
buttonTitle: this.argumentsFormArray.at(index)?.value ? 'action.apply' : 'action.add', |
|
|
|
tenantId: this.tenantId, |
|
|
|
entityName: this.entityName, |
|
|
|
usedArgumentNames: this.argumentsFormArray.getRawValue().map(({ argumentName }) => argumentName).filter(name => name !== argumentObj.argumentName), |
|
|
|
usedArgumentNames: this.argumentsFormArray.value.map(({ argumentName }) => argumentName).filter(name => name !== argument.argumentName), |
|
|
|
}; |
|
|
|
this.popoverComponent = this.popoverService.displayPopover(trigger, this.renderer, |
|
|
|
this.viewContainerRef, CalculatedFieldArgumentPanelComponent, isDefined(index) ? 'left' : 'right', false, null, |
|
|
|
@ -150,7 +171,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces |
|
|
|
{}, {}, true); |
|
|
|
this.popoverComponent.tbComponentRef.instance.argumentsDataApplied.subscribe(({ value, index }) => { |
|
|
|
this.popoverComponent.hide(); |
|
|
|
const formGroup = this.getArgumentFormGroup(value); |
|
|
|
const formGroup = this.fb.group(value); |
|
|
|
if (isDefinedAndNotNull(index)) { |
|
|
|
this.argumentsFormArray.setControl(index, formGroup); |
|
|
|
} else { |
|
|
|
@ -162,9 +183,14 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private updateDataSource(value: CalculatedFieldArgumentValue[]): void { |
|
|
|
const sortedValue = this.sortData(value); |
|
|
|
this.dataSource.loadData(sortedValue); |
|
|
|
} |
|
|
|
|
|
|
|
private updateErrorText(): void { |
|
|
|
if (this.calculatedFieldType === CalculatedFieldType.SIMPLE |
|
|
|
&& this.argumentsFormArray.controls.some(control => control.get('refEntityKey').get('type').value === ArgumentType.Rolling)) { |
|
|
|
&& this.argumentsFormArray.controls.some(control => control.value.refEntityKey.type === ArgumentType.Rolling)) { |
|
|
|
this.errorText = 'calculated-fields.hint.arguments-simple-with-rolling'; |
|
|
|
} else if (!this.argumentsFormArray.controls.length) { |
|
|
|
this.errorText = 'calculated-fields.hint.arguments-empty'; |
|
|
|
@ -173,9 +199,9 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private getArgumentsObject(): Record<string, CalculatedFieldArgument> { |
|
|
|
return this.argumentsFormArray.getRawValue().reduce((acc, rawValue) => { |
|
|
|
const { argumentName, ...argument } = rawValue as CalculatedFieldArgumentValue; |
|
|
|
private getArgumentsObject(value: CalculatedFieldArgumentValue[]): Record<string, CalculatedFieldArgument> { |
|
|
|
return value.reduce((acc, argumentValue) => { |
|
|
|
const { argumentName, ...argument } = argumentValue as CalculatedFieldArgumentValue; |
|
|
|
acc[argumentName] = argument; |
|
|
|
return acc; |
|
|
|
}, {} as Record<string, CalculatedFieldArgument>); |
|
|
|
@ -186,31 +212,62 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces |
|
|
|
this.populateArgumentsFormArray(argumentsObj) |
|
|
|
} |
|
|
|
|
|
|
|
getEntityDetailsPageURL(id: string, type: EntityType): string { |
|
|
|
return getEntityDetailsPageURL(id, type); |
|
|
|
} |
|
|
|
|
|
|
|
private populateArgumentsFormArray(argumentsObj: Record<string, CalculatedFieldArgument>): void { |
|
|
|
Object.keys(argumentsObj).forEach(key => { |
|
|
|
const value: CalculatedFieldArgumentValue = { |
|
|
|
...argumentsObj[key], |
|
|
|
argumentName: key |
|
|
|
}; |
|
|
|
this.argumentsFormArray.push(this.getArgumentFormGroup(value), {emitEvent: false}); |
|
|
|
this.argumentsFormArray.push(this.fb.group(value), { emitEvent: false }); |
|
|
|
}); |
|
|
|
this.argumentsFormArray.updateValueAndValidity(); |
|
|
|
} |
|
|
|
|
|
|
|
private updateEntityNameMap(value: CalculatedFieldArgumentValue[]): void { |
|
|
|
value.forEach(({ refEntityId = {}}) => { |
|
|
|
if (refEntityId.id && !this.entityNameMap.has(refEntityId.id)) { |
|
|
|
const { id, entityType } = refEntityId as EntityId; |
|
|
|
this.entityService.getEntity(entityType as EntityType, id, { ignoreLoading: true, ignoreErrors: true }) |
|
|
|
.pipe(takeUntilDestroyed(this.destroyRef)) |
|
|
|
.subscribe(entity => this.entityNameMap.set(id, entity.name)); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private getSortValue(argument: CalculatedFieldArgumentValue, column: string): string { |
|
|
|
switch (column) { |
|
|
|
case 'entityType': |
|
|
|
if (argument.refEntityId?.entityType === ArgumentEntityType.Tenant) { |
|
|
|
return 'calculated-fields.argument-current-tenant'; |
|
|
|
} else if (argument.refEntityId?.id) { |
|
|
|
return entityTypeTranslations.get((argument.refEntityId)?.entityType as unknown as EntityType).type; |
|
|
|
} else { |
|
|
|
return 'calculated-fields.argument-current'; |
|
|
|
} |
|
|
|
case 'type': |
|
|
|
return ArgumentTypeTranslations.get(argument.refEntityKey.type); |
|
|
|
case 'key': |
|
|
|
return argument.refEntityKey.key; |
|
|
|
default: |
|
|
|
return argument.argumentName; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private sortData(data: CalculatedFieldArgumentValue[]): CalculatedFieldArgumentValue[] { |
|
|
|
return data.sort((a, b) => { |
|
|
|
const valA = this.getSortValue(a, this.sortOrder.property) ?? ''; |
|
|
|
const valB = this.getSortValue(b, this.sortOrder.property) ?? ''; |
|
|
|
return (this.sortOrder.direction === 'asc' ? 1 : -1) * valA.localeCompare(valB); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private getArgumentFormGroup(value: CalculatedFieldArgumentValue): FormGroup { |
|
|
|
return this.fb.group({ |
|
|
|
...value, |
|
|
|
argumentName: [{ value: value.argumentName, disabled: true }], |
|
|
|
...(value.refEntityId ? { |
|
|
|
refEntityId: this.fb.group({ |
|
|
|
entityType: [{ value: value.refEntityId.entityType, disabled: true }], |
|
|
|
id: [{ value: value.refEntityId.id , disabled: true }], |
|
|
|
}), |
|
|
|
} : {}), |
|
|
|
refEntityKey: this.fb.group({ |
|
|
|
...value.refEntityKey, |
|
|
|
type: [{ value: value.refEntityKey.type, disabled: true }], |
|
|
|
key: [{ value: value.refEntityKey.key, disabled: true }], |
|
|
|
}), |
|
|
|
}) |
|
|
|
class CalculatedFieldArgumentDatasource extends TbTableDatasource<CalculatedFieldArgumentValue> { |
|
|
|
constructor() { |
|
|
|
super(); |
|
|
|
} |
|
|
|
} |
|
|
|
|