Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,10 @@ export class ScreensService {

/**
* @param requestBody
* @returns any default response
* @returns ScreenInput default response
* @throws ApiError
*/
public saveInput(requestBody?: ScreenInput): Observable<any> {
public saveInput(requestBody?: ScreenInput): Observable<ScreenInput> {
return this.httpRequest.request({
method: 'POST',
url: '/screens/input',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<step-modal-window title="Warning" [hideButtonsSection]="true">
<div step-modal-window-body>
<form (submit)="onSubmit()">
<step-alert [type]="AlertType.DANGER">{{ dialogData.message }}</step-alert>
<step-alert [type]="alertType">{{ dialogData.message }}</step-alert>

<div class="dialog-actions">
<button type="button" mat-stroked-button [matDialogClose]="undefined">Cancel</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AlertType } from '../../types/alert-type.enum';

export interface ConfirmationDialogData {
message: string;
alertType?: AlertType;
}

export type ConfirmationDialogResult = boolean | undefined;
Expand All @@ -17,7 +18,7 @@ export class ConfirmationDialogComponent {
private _dialogRef = inject<MatDialogRef<ConfirmationDialogComponent, ConfirmationDialogResult>>(MatDialogRef);

readonly dialogData = inject<ConfirmationDialogData>(MAT_DIALOG_DATA);
readonly AlertType = AlertType;
readonly alertType = this.dialogData.alertType ?? AlertType.DANGER;

@HostListener('keydown.enter')
onSubmit(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
MessagesListDialogData,
MessagesListDialogResult,
} from '../components/messages-list-dialog/messages-list-dialog.component';
import { AlertType } from '../types/alert-type.enum';

@Injectable({
providedIn: 'root',
Expand All @@ -45,14 +46,15 @@ export class DialogsService {
return dialogRef.afterClosed().pipe(filter((result) => result !== undefined)) as Observable<string>;
}

showWarning(message: string): Observable<boolean> {
showWarning(message: string, alertType?: AlertType): Observable<boolean> {
const dialogRef = this._matDialog.open<
ConfirmationDialogComponent,
ConfirmationDialogData,
ConfirmationDialogResult
>(ConfirmationDialogComponent, {
data: {
message,
alertType,
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { Provider } from '@angular/core';
import { TableColumnsDefaultVisibilityService } from './table-columns-default-visibility.service';

export abstract class TableColumnsConfig {
readonly entityTableRemoteId!: string;
readonly entityScreenId?: string;
readonly entityScreenSubPath?: string;
readonly entityScreenDefaultVisibleFields?: string[];
readonly customColumnOptions?: string | string[];
readonly allowDefaultVisibilityConfiguration?: boolean;
}

export const tableColumnsConfigProvider = (config: TableColumnsConfig | null): Provider => ({
provide: TableColumnsConfig,
useValue: config,
});
export const tableColumnsConfigProvider = (config: TableColumnsConfig | null): Provider => {
TableColumnsDefaultVisibilityService.configureLinkage(config);

return {
provide: TableColumnsConfig,
useValue: config,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { inject, Injectable } from '@angular/core';
import { map, Observable } from 'rxjs';
import { ConfirmVisibilityStrategy } from '../types/confirm-visibility-strategy';
import { ScreenInput } from '../../../client/generated';
import { AlertType, DialogsService } from '../../basics/step-basics.module';

@Injectable({
providedIn: 'root',
})
export class TableColumnsDefaultVisibilityConfirmService implements ConfirmVisibilityStrategy {
private _dialogs = inject(DialogsService);

confirmVisibility(screenInput: ScreenInput): Observable<{ isVisible?: boolean; scope?: string[] }> {
return this._dialogs
.showWarning(`Show the column "${screenInput.input?.label}" in tables?`, AlertType.DEFAULT)
.pipe(map((isVisible) => ({ isVisible })));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { inject, Injectable } from '@angular/core';
import { TableColumnsDefaultVisibilityConfirmService } from './table-columns-default-visibility-confirm.service';
import { ScreenInput, TableApiWrapperService } from '../../../client/step-client-module';
import { ConfirmVisibilityStrategy } from '../types/confirm-visibility-strategy';
import { forkJoin, map, Observable, of, switchMap } from 'rxjs';
import { TableColumnsConfig } from './table-columns-config.provider';
import { TableColumnsSettingsFactoryService } from './table-columns-settings-factory.service';

@Injectable({
providedIn: 'root',
})
export class TableColumnsDefaultVisibilityService {
private static screenTablesLinkageInfo = new Map<string, Set<TableColumnsConfig>>();

static configureLinkage(config?: TableColumnsConfig | null): void {
if (!config?.entityScreenId || !config.allowDefaultVisibilityConfiguration) {
return;
}

if (!this.screenTablesLinkageInfo.has(config.entityScreenId)) {
this.screenTablesLinkageInfo.set(config.entityScreenId, new Set([config]));
} else {
this.screenTablesLinkageInfo.get(config.entityScreenId)!.add(config);
}
}

private _tableApi = inject(TableApiWrapperService);
private _defaultVisibilityConfirmStrategy = inject(TableColumnsDefaultVisibilityConfirmService);
private _columnsSettingsFactory = inject(TableColumnsSettingsFactoryService);
private confirmStrategy: ConfirmVisibilityStrategy = this._defaultVisibilityConfirmStrategy;

useStrategy(strategy: ConfirmVisibilityStrategy): void {
this.confirmStrategy = strategy;
}

setupDefaultVisibilityForScreenInputColumn(screenInput: ScreenInput): Observable<unknown> {
if (!screenInput.screenId) {
return of(undefined);
}
const linkedConfigs = TableColumnsDefaultVisibilityService.screenTablesLinkageInfo.get(screenInput.screenId);
if (!linkedConfigs?.size) {
return of(undefined);
}

return this.confirmStrategy.confirmVisibility(screenInput).pipe(
switchMap((confirmation) => {
if (!confirmation?.isVisible) {
return of(undefined);
}

const visibilityUpdateOperations = Array.from(linkedConfigs).map((config) =>
this.setDefaultVisibility(config, screenInput, confirmation.scope),
);

return forkJoin(visibilityUpdateOperations);
}),
);
}

private setDefaultVisibility(
config: TableColumnsConfig,
screenInput: ScreenInput,
scope: string[] = [],
): Observable<unknown> {
return this._tableApi.getTableSettings(config.entityTableRemoteId).pipe(
map((settings) => {
settings = settings ?? {};
settings.columnSettingList = settings.columnSettingList ?? [];
const newColumnSettings = this._columnsSettingsFactory.createScreenInputColumnSettings(
screenInput,
0, // For columns at zero position, natural table's order will applied
config,
true,
);
settings.columnSettingList.push(newColumnSettings);
return settings;
}),
switchMap((tableSettings) => {
return this._tableApi.saveTableSettings(config.entityTableRemoteId, { tableSettings, scope });
}),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Injectable } from '@angular/core';
import { MatColumnDef } from '@angular/material/table';
import { ColumnInfo } from '../types/column-info';
import { ColumnSettings, ScreenInput, ScreenInputColumnSettings } from '../../../client/step-client-module';
import { CustomCellApplySubPathPipe } from '../pipe/custom-cell-apply-sub-path.pipe';
import { TableColumnsConfig } from './table-columns-config.provider';

@Injectable({
providedIn: 'root',
})
export class TableColumnsSettingsFactoryService {
createStandardColumnSettings(
matColDef: MatColumnDef,
position: number,
infoDictionary?: Record<string, ColumnInfo>,
): ColumnSettings {
const info = infoDictionary?.[matColDef.name];
const columnId = info?.columnId ?? matColDef.name;
const visible = info ? !info.isHiddenByDefault : true;
return {
columnId,
position,
visible,
type: 'step.plugins.table.settings.ColumnSettings',
};
}

createScreenInputColumnSettings(
screenInput: ScreenInput,
position: number,
config: TableColumnsConfig | undefined | null,
visible: boolean,
): ScreenInputColumnSettings {
return {
columnId: CustomCellApplySubPathPipe.transform(screenInput.input!.id!, config?.entityScreenSubPath),
position,
visible,
type: 'step.plugins.table.settings.ScreenInputColumnSettings',
screenInput,
};
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { computed, inject, Injectable, signal } from '@angular/core';
import { ColumnSettings, ScreenInput, ScreenInputColumnSettings, TableSettings } from '../../../client/generated';
import { ColumnSettings, TableSettings } from '../../../client/step-client-module';
import { MatColumnDef } from '@angular/material/table';
import { TableColumnsConfig } from './table-columns-config.provider';
import { TableApiWrapperService } from '../../../client/table/step-table-client.module';
import { TableColumnsDefinitionService } from './table-columns-definition.service';
import { map, Observable, of } from 'rxjs';
import { ColumnInfo } from '../types/column-info';
import { CustomCellApplySubPathPipe } from '../pipe/custom-cell-apply-sub-path.pipe';
import { TableColumnsSettingsFactoryService } from './table-columns-settings-factory.service';

enum VisibilityState {
HIDDEN,
Expand All @@ -28,6 +28,7 @@ export class TableColumnsService {
private _tableApi = inject(TableApiWrapperService);
private _columnsConfig = inject(TableColumnsConfig, { optional: true });
private _columnsDefinition = inject(TableColumnsDefinitionService);
private _columnsSettingsFactory = inject(TableColumnsSettingsFactoryService);

private defaultVisibleScreenColumns?: Set<string> = !!this._columnsConfig?.entityScreenDefaultVisibleFields
? new Set(this._columnsConfig?.entityScreenDefaultVisibleFields)
Expand Down Expand Up @@ -147,17 +148,24 @@ export class TableColumnsService {

let columnSettingList = mainColumnsDefinitions
.reduce((res, col) => [...res, ...col.columnDefinitions], [] as MatColumnDef[])
.map((col, i) => this.createStandardColumnSettings(col, i, infoDictionary));
.map((col, i) => this._columnsSettingsFactory.createStandardColumnSettings(col, i, infoDictionary));

const remoteColumns = remoteColumnsDefinitions.map((screenInput, i) =>
this.createScreenInputColumnSettings(screenInput, columnSettingList.length + i),
this._columnsSettingsFactory.createScreenInputColumnSettings(
screenInput,
columnSettingList.length + i,
this._columnsConfig,
!!this.defaultVisibleScreenColumns?.has(screenInput.input!.id!),
),
);

columnSettingList = [...columnSettingList, ...remoteColumns];

const actionsColumns = actionColumnsDefinitions
.reduce((res, col) => [...res, ...col.columnDefinitions], [] as MatColumnDef[])
.map((col, i) => this.createStandardColumnSettings(col, columnSettingList.length + i, infoDictionary));
.map((col, i) =>
this._columnsSettingsFactory.createStandardColumnSettings(col, columnSettingList.length + i, infoDictionary),
);

columnSettingList = [...columnSettingList, ...actionsColumns];

Expand All @@ -167,32 +175,6 @@ export class TableColumnsService {
return { tableSettings, actionColumnsIds };
}

private createStandardColumnSettings(
matColDef: MatColumnDef,
position: number,
infoDictionary?: Record<string, ColumnInfo>,
): ColumnSettings {
const info = infoDictionary?.[matColDef.name];
const columnId = info?.columnId ?? matColDef.name;
const visible = info ? !info.isHiddenByDefault : true;
return {
columnId,
position,
visible,
type: 'step.plugins.table.settings.ColumnSettings',
};
}

private createScreenInputColumnSettings(screenInput: ScreenInput, position: number): ScreenInputColumnSettings {
return {
columnId: CustomCellApplySubPathPipe.transform(screenInput.input!.id!, this.entityScreenSubPath),
position,
visible: !!this.defaultVisibleScreenColumns?.has(screenInput.input!.id!),
type: 'step.plugins.table.settings.ScreenInputColumnSettings',
screenInput,
};
}

private prepareColumns(columns: ColumnSettings[] = [], visibility: VisibilityState): string[] {
const cols = columns.filter((col) => (visibility === VisibilityState.VISIBLE ? col.visible : !col.visible));
cols.sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
Expand Down Expand Up @@ -222,15 +204,24 @@ export class TableColumnsService {
return defaultTableSettings;
}

const defaultColumnKeys = new Set((defaultTableSettings.columnSettingList ?? []).map((col) => col.columnId));
const defaultColumnKeysAndPositions = new Map(
(defaultTableSettings.columnSettingList ?? []).map((col) => [col.columnId, col.position]),
);
const actionColumnKeys = new Set(actionColumnIds);

// Remove columns from settings, that was deleted
// Also remove action columns from remote settings, because remote settings position might be wrong
// in case if new custom columns have been added
remoteTableSettings.columnSettingList = (remoteTableSettings.columnSettingList ?? []).filter(
(col) => defaultColumnKeys.has(col.columnId) && !actionColumnKeys.has(col.columnId!),
);
remoteTableSettings.columnSettingList = (remoteTableSettings.columnSettingList ?? [])
.filter((col) => defaultColumnKeysAndPositions.has(col.columnId) && !actionColumnKeys.has(col.columnId!))
.map((col) => {
// Special case to properly initialize newly added default column position
// If it not set, try to get it from default columns.
if (!col.position) {
col.position = defaultColumnKeysAndPositions.get(col.columnId!);
}
return col;
});

// Add new columns
const removeColumnsKey = new Set(remoteTableSettings.columnSettingList.map((col) => col.columnId));
Expand Down
2 changes: 2 additions & 0 deletions projects/step-core/src/lib/modules/table/table.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,6 @@ export * from './shared/search-column-accessor';
export * from './services/table-highlight-item-container.service';
export * from './services/table-columns-config.provider';
export * from './services/table-columns.service';
export * from './services/table-columns-default-visibility.service';
export * from './types/confirm-visibility-strategy';
export * from './services/items-per-page.service';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ScreenInput } from '../../../client/generated';
import { Observable } from 'rxjs';

export interface ConfirmVisibilityStrategy {
confirmVisibility(screenInput: ScreenInput): Observable<{ isVisible?: boolean; scope?: string[] }>;
}
1 change: 0 additions & 1 deletion projects/step-core/styles/_core-mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,3 @@
}
}
}

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<step-modal-window [title]="modalTitle">
<step-modal-window [title]="modalTitle" [showSpinner]="inProgress">
<div step-modal-window-body>
@if (screenInput.input; as model) {
<step-form-field alignLabelAddon="near">
Expand Down
Loading