From b6d10d99226d4884d1fd4d68e19cbdce983d1d0b Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 22 Jun 2026 14:26:03 +0200 Subject: [PATCH 01/18] [NAE-2454] View Configuration 2.0 - add params to create-case-request-body.ts --- .../src/lib/resources/interface/create-case-request-body.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/projects/netgrif-components-core/src/lib/resources/interface/create-case-request-body.ts b/projects/netgrif-components-core/src/lib/resources/interface/create-case-request-body.ts index 8b1514b640..55626e1e43 100644 --- a/projects/netgrif-components-core/src/lib/resources/interface/create-case-request-body.ts +++ b/projects/netgrif-components-core/src/lib/resources/interface/create-case-request-body.ts @@ -8,4 +8,7 @@ export interface CreateCaseRequestBody { */ netId: string; color?: string; + params?: { + [k: string]: string + }; } From ef891840acbae95b3a0e2de1a395db851cafe0f4 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 22 Jun 2026 14:53:19 +0200 Subject: [PATCH 02/18] [NAE-2454] View Configuration 2.0 - introduce reload menu frontend action --- .../src/lib/navigation/model/reload-menu.ts | 12 ++++++++++++ .../src/lib/navigation/navigation.module.ts | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 projects/netgrif-components/src/lib/navigation/model/reload-menu.ts diff --git a/projects/netgrif-components/src/lib/navigation/model/reload-menu.ts b/projects/netgrif-components/src/lib/navigation/model/reload-menu.ts new file mode 100644 index 0000000000..64701f9307 --- /dev/null +++ b/projects/netgrif-components/src/lib/navigation/model/reload-menu.ts @@ -0,0 +1,12 @@ +import {Injector} from "@angular/core"; +import { + FrontActionDefinition, + FrontAction, DoubleDrawerNavigationService, +} from '@netgrif/components-core'; + +export const reloadMenu: FrontActionDefinition = { + call: (injector: Injector, frontAction: FrontAction) => { + const doubleDrawerNavigationService = injector.get(DoubleDrawerNavigationService); + doubleDrawerNavigationService.loadNavigationItems(doubleDrawerNavigationService.currentNode); + } +} diff --git a/projects/netgrif-components/src/lib/navigation/navigation.module.ts b/projects/netgrif-components/src/lib/navigation/navigation.module.ts index 2f8ff226f3..5d166d41d5 100644 --- a/projects/netgrif-components/src/lib/navigation/navigation.module.ts +++ b/projects/netgrif-components/src/lib/navigation/navigation.module.ts @@ -10,7 +10,7 @@ import { MaterialModule, TranslateLibModule, UtilityModule, - NAE_GROUP_NAVIGATION_COMPONENT_RESOLVER_COMPONENT, + NAE_GROUP_NAVIGATION_COMPONENT_RESOLVER_COMPONENT, FrontActionRegistryService, } from '@netgrif/components-core'; import {NavigationRailComponent} from './navigation-rail/navigation-rail.component'; import {NavigationTreeComponent} from './navigation-tree/navigation-tree.component'; @@ -59,6 +59,7 @@ import {ToolbarComponentModule} from '../toolbar/toolbar.module'; import { DefaultTabbedSingleTaskViewComponent } from './group-navigation-component-resolver/default-components/tabbed/default-tabbed-single-task-view/default-tabbed-single-task-view.component'; import { DefaultSingleTaskViewComponent } from './group-navigation-component-resolver/default-components/simple-views/default-single-task-view/default-single-task-view.component'; import { DefaultTicketViewComponent } from './group-navigation-component-resolver/default-components/tabbed/default-ticket-view/default-ticket-view.component'; +import {reloadMenu} from "./model/reload-menu"; @NgModule({ @@ -135,4 +136,7 @@ import { DefaultTicketViewComponent } from './group-navigation-component-resolve ] }) export class NavigationComponentModule { + constructor(frontActionRegistry: FrontActionRegistryService) { + frontActionRegistry.register('reloadMenu', reloadMenu); + } } From 2ad57eb1f85fad0082f0aa6bd85171260cb12f6d Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 23 Jun 2026 15:45:43 +0200 Subject: [PATCH 03/18] [NAE-2454] View Configuration 2.0 - introduce edit mode in double drawer menu - leave some todos - fix some memory leaks --- .../src/assets/i18n/de.json | 9 ++- .../src/assets/i18n/en.json | 9 ++- .../src/assets/i18n/sk.json | 9 ++- .../abstract-navigation-double-drawer.ts | 58 +++++++++++++++++-- .../navigation-double-drawer.component.html | 35 ++++++++--- .../navigation-double-drawer.component.ts | 8 ++- 6 files changed, 112 insertions(+), 16 deletions(-) diff --git a/projects/netgrif-components-core/src/assets/i18n/de.json b/projects/netgrif-components-core/src/assets/i18n/de.json index df8c571d62..0cf18f74d4 100644 --- a/projects/netgrif-components-core/src/assets/i18n/de.json +++ b/projects/netgrif-components-core/src/assets/i18n/de.json @@ -491,7 +491,14 @@ "filterNotFound": "Diese Ansicht hat keinen Filter", "loadMoreItems": "Mehr", "ascending": "Aufsteigend", - "descending": "Absteigend" + "descending": "Absteigend", + "editModeOff": "Bearbeitungsmodus deaktivieren", + "editModeOn": "Bearbeitungsmodus aktivieren", + "createMenuItemBtn": "Neuen Menüpunkt erstellen", + "editMenuItem": "Bearbeiten", + "snackbar": { + "failedItemCreation": "Fehler beim Erstellen des Artikels" + } }, "impersonation": { "user": { diff --git a/projects/netgrif-components-core/src/assets/i18n/en.json b/projects/netgrif-components-core/src/assets/i18n/en.json index 05e7d0b758..63529c6b22 100644 --- a/projects/netgrif-components-core/src/assets/i18n/en.json +++ b/projects/netgrif-components-core/src/assets/i18n/en.json @@ -492,7 +492,14 @@ "filterNotFound": "This view has no filter", "loadMoreItems": "More", "ascending": "Ascending", - "descending": "Descending" + "descending": "Descending", + "editModeOff": "Disable edit mode", + "editModeOn": "Enable edit mode", + "createMenuItemBtn": "Create new menu item", + "editMenuItem": "Edit", + "snackbar": { + "failedItemCreation": "Failed to create menu item" + } }, "impersonation": { "user": { diff --git a/projects/netgrif-components-core/src/assets/i18n/sk.json b/projects/netgrif-components-core/src/assets/i18n/sk.json index 27b8ecad2d..1ed5266e94 100644 --- a/projects/netgrif-components-core/src/assets/i18n/sk.json +++ b/projects/netgrif-components-core/src/assets/i18n/sk.json @@ -491,7 +491,14 @@ "filterNotFound": "Toto zobrazenie neobsahuje filter", "loadMoreItems": "Viac", "ascending": "Vzostupne", - "descending": "Zostupne" + "descending": "Zostupne", + "editModeOff": "Vypnutie editačného módu", + "editModeOn": "Zapnutie editačného módu", + "createMenuItemBtn": "Vytvoriť novú položku v menu", + "editMenuItem": "Upraviť", + "snackbar": { + "failedItemCreation": "Nastala chyba pri vytváraní položky" + } }, "impersonation": { "user": { diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts index 9d9197b794..5aeb47cc97 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts @@ -4,13 +4,14 @@ import {ActivatedRoute, Router} from '@angular/router'; import {TranslateService} from '@ngx-translate/core'; import {ResizeEvent} from 'angular-resizable-element'; import {Observable, Subscription} from 'rxjs'; -import {filter, take} from 'rxjs/operators'; +import {filter, take, switchMap} from 'rxjs/operators'; import {AccessService} from '../../authorization/permission/access.service'; import {ConfigurationService} from '../../configuration/configuration.service'; import {ImpersonationUserSelectService} from '../../impersonation/services/impersonation-user-select.service'; import {ImpersonationService} from '../../impersonation/services/impersonation.service'; import {LoggerService} from '../../logger/services/logger.service'; import {CaseResourceService} from '../../resources/engine-endpoint/case-resource.service'; +import {Case} from '../../resources/interface/case'; import { DynamicNavigationRouteProviderService, } from '../../routing/dynamic-navigation-route-provider/dynamic-navigation-route-provider.service'; @@ -37,6 +38,9 @@ import {UriNodeResource} from '../model/uri-resource'; import {UriService} from '../service/uri.service'; import {DoubleDrawerNavigationService} from "./service/double-drawer-navigation.service"; import {DoubleDrawerUtils} from "./util/double-drawer-utils"; +import {ProcessService} from "../../process/process.service"; +import {CreateCaseEventOutcome} from "../../event/model/event-outcomes/case-outcomes/create-case-event-outcome"; +import {SnackBarService} from "../../snack-bar/services/snack-bar.service"; @Component({ selector: 'ncc-abstract-navigation-double-drawer', @@ -67,6 +71,8 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, @Output() resized = new EventEmitter(true); // on menu resize @Output() itemLoaded = new EventEmitter(true); // on item loaded + public editModeEnabled: boolean = false; + protected _breakpointSubscription: Subscription; protected _configLeftMenu: ConfigDoubleMenu = { @@ -83,6 +89,9 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, }; protected configUrl: string; + protected itemClickedSub: Subscription; + protected itemLoadedSub: Subscription; + protected rightItemsSub: Subscription; protected constructor(protected _router: Router, protected _activatedRoute: ActivatedRoute, @@ -94,11 +103,13 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, protected _log: LoggerService, protected _config: ConfigurationService, protected _uriService: UriService, + protected _processService: ProcessService, protected _caseResourceService: CaseResourceService, protected _impersonationUserSelect: ImpersonationUserSelectService, protected _impersonation: ImpersonationService, protected _dynamicRoutingService: DynamicNavigationRouteProviderService, protected _redirectService: RedirectService, + protected _snackBarService: SnackBarService, protected _navigationService: DoubleDrawerNavigationService) { let configUrl: string = this._config.getServicesConfiguration()?.doubleDrawer?.url; if (configUrl !== undefined && !configUrl.startsWith('/')) { @@ -106,10 +117,10 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, } this.configUrl = configUrl; - this._navigationService.itemClicked$.subscribe((itemClickEvent: MenuItemClickEvent) => { + this.itemClickedSub = this._navigationService.itemClicked$.subscribe((itemClickEvent: MenuItemClickEvent) => { this.itemClicked.emit(itemClickEvent); }) - this._navigationService.itemLoaded$.subscribe((itemLoadedEvent: MenuItemLoadedEvent) => { + this.itemLoadedSub = this._navigationService.itemLoaded$.subscribe((itemLoadedEvent: MenuItemLoadedEvent) => { this.itemLoaded.emit(itemLoadedEvent); }) } @@ -124,7 +135,7 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, }); if (this.canApplyAutoSelect()) { - this.rightItems$.pipe( + this.rightItemsSub = this.rightItems$.pipe( filter(rightItems => rightItems.length > 0), take(1) ).subscribe(()=> { @@ -146,6 +157,9 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, this.itemClicked.complete(); this.resized.complete(); this.itemLoaded.complete(); + this.itemClickedSub.unsubscribe(); + this.itemLoadedSub.unsubscribe(); + this.rightItemsSub.unsubscribe(); } public get currentNode(): UriNodeResource { @@ -329,6 +343,42 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, return DoubleDrawerUtils.isItemAndNodeEqual(item, node); } + public setMenuEditMode(newVal: boolean): void { + this.editModeEnabled = newVal; + } + + public editMenuItem(menuItemCase: Case) { + // todo 2454 + } + + public createMenuItem() { + this._processService.getNet('menu_item').pipe( + switchMap(net => { + return this._caseResourceService.createCase({ + netId: net.stringId, + title: 'New menu item', + params: { "dest_uri_path": this.currentNode.uriPath } + }); + }), + take(1) + ).subscribe(outcome => { + if (outcome.error) { + this._log.error(`Could not create menu item case`, outcome.error); + this._snackBarService.openErrorSnackBar(this._translateService.instant('dynamicNavigation.snackbar.failedItemCreation')) + return; + } + const _case = (outcome.outcome as CreateCaseEventOutcome).aCase; + // todo 2454 redirect + }, error => { + this._log.error(`Could not create menu item case`, error); + this._snackBarService.openErrorSnackBar(this._translateService.instant('dynamicNavigation.snackbar.failedItemCreation')); + }); + } + + public canEditMenu(): boolean { + return this._userService.hasRoleByIdentifier('admin', 'menu_item') + } + protected resolveLayout(isLargeScreen: boolean): void { this._configLeftMenu = isLargeScreen ? { mode: 'side', diff --git a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.html b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.html index 5795f2d8ed..5bff28e287 100644 --- a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.html +++ b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.html @@ -76,6 +76,16 @@ {{ item.navigation.icon }} {{ item.navigation.title }} +
+ + +
@@ -84,7 +94,7 @@ -
+
+ add + {{ 'dynamicNavigation.createMenuItemBtn' | translate }} + +
diff --git a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts index dda6955265..0317e03c99 100644 --- a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts +++ b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts @@ -7,6 +7,8 @@ import { UriService, LoggerService, UserService, + ProcessService, + SnackBarService, AbstractNavigationDoubleDrawerComponent, DynamicNavigationRouteProviderService, AccessService, @@ -67,15 +69,17 @@ export class NavigationDoubleDrawerComponent extends AbstractNavigationDoubleDra _log: LoggerService, _config: ConfigurationService, _uriService: UriService, + _processService: ProcessService, _caseResourceService: CaseResourceService, _impersonationUserSelect: ImpersonationUserSelectService, _impersonation: ImpersonationService, _dynamicRouteProviderService: DynamicNavigationRouteProviderService, _redirectService: RedirectService, + _snackBarService: SnackBarService, _navigationService: DoubleDrawerNavigationService) { super(_router, _activatedRoute, _breakpoint, _languageService, _translateService, _userService, _accessService, - _log, _config, _uriService, _caseResourceService, _impersonationUserSelect, _impersonation, - _dynamicRouteProviderService, _redirectService, _navigationService) + _log, _config, _uriService, _processService, _caseResourceService, _impersonationUserSelect, _impersonation, + _dynamicRouteProviderService, _redirectService, _snackBarService, _navigationService) } public toggleSection(section: string): void { From 31911955a312e40de285c9711b2c29797800f056 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 29 Jun 2026 16:36:41 +0200 Subject: [PATCH 04/18] [NAE-2454] View Configuration 2.0 - introduce task view dialog - rename existing task dialog to single task dialog - rework task dialog content filtering --- .../abstract-navigation-double-drawer.ts | 31 ++++++- .../content-components/injection-tokens.ts | 1 + .../model/task-view-injection-data.ts | 3 +- .../src/lib/dialog/dialog.module.ts | 12 ++- .../src/lib/dialog/model/dialog-actions.ts | 2 +- .../src/lib/dialog/public-api.ts | 1 + .../single-task-view-dialog.component.html | 16 ++++ .../single-task-view-dialog.component.scss | 78 +++++++++++++++++ .../single-task-view-dialog.component.spec.ts | 66 ++++++++++++++ .../single-task-view-dialog.component.ts | 86 +++++++++++++++++++ .../task-view-dialog.component.html | 19 +--- .../task-view-dialog.component.spec.ts | 2 +- .../task-view-dialog.component.ts | 15 ++-- .../navigation-double-drawer.component.ts | 7 +- 14 files changed, 303 insertions(+), 36 deletions(-) create mode 100644 projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.html create mode 100644 projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.scss create mode 100644 projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.spec.ts create mode 100644 projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.ts diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts index 5aeb47cc97..1798d1f8e7 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts @@ -1,5 +1,5 @@ import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout'; -import {Component, EventEmitter, Input, OnDestroy, OnInit, Output, TemplateRef} from '@angular/core'; +import {Component, EventEmitter, Injector, Input, OnDestroy, OnInit, Output, TemplateRef} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {TranslateService} from '@ngx-translate/core'; import {ResizeEvent} from 'angular-resizable-element'; @@ -41,6 +41,9 @@ import {DoubleDrawerUtils} from "./util/double-drawer-utils"; import {ProcessService} from "../../process/process.service"; import {CreateCaseEventOutcome} from "../../event/model/event-outcomes/case-outcomes/create-case-event-outcome"; import {SnackBarService} from "../../snack-bar/services/snack-bar.service"; +import {MatDialog} from "@angular/material/dialog"; +import {NAE_TASK_VIEW_COMPONENT} from "../../side-menu/content-components/injection-tokens"; +import {TaskViewInjectionData} from "../../side-menu/content-components/task-view/model/task-view-injection-data"; @Component({ selector: 'ncc-abstract-navigation-double-drawer', @@ -110,6 +113,8 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, protected _dynamicRoutingService: DynamicNavigationRouteProviderService, protected _redirectService: RedirectService, protected _snackBarService: SnackBarService, + protected _dialog: MatDialog, + protected _injector: Injector, protected _navigationService: DoubleDrawerNavigationService) { let configUrl: string = this._config.getServicesConfiguration()?.doubleDrawer?.url; if (configUrl !== undefined && !configUrl.startsWith('/')) { @@ -348,7 +353,17 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, } public editMenuItem(menuItemCase: Case) { - // todo 2454 + const taskView = this._injector.get(NAE_TASK_VIEW_COMPONENT); + this._dialog.open(taskView, { + panelClass: "dialog-task-responsive", + data: { + autoCloseOnEvent: false, + searchBody: { + transitionId: ["item_settings", "move_item", "duplicate_item", "children_order"], + case: { id: menuItemCase.stringId } + } + } as TaskViewInjectionData, + }); } public createMenuItem() { @@ -368,7 +383,17 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, return; } const _case = (outcome.outcome as CreateCaseEventOutcome).aCase; - // todo 2454 redirect + const taskView = this._injector.get(NAE_TASK_VIEW_COMPONENT); + this._dialog.open(taskView, { + panelClass: "dialog-task-responsive", + data: { + autoCloseOnEvent: true, + searchBody: { + transitionId: "initialize", + case: { id: _case.stringId } + } + } as TaskViewInjectionData, + }); }, error => { this._log.error(`Could not create menu item case`, error); this._snackBarService.openErrorSnackBar(this._translateService.instant('dynamicNavigation.snackbar.failedItemCreation')); diff --git a/projects/netgrif-components-core/src/lib/side-menu/content-components/injection-tokens.ts b/projects/netgrif-components-core/src/lib/side-menu/content-components/injection-tokens.ts index ea3dc77c1b..0b4d1f68a7 100644 --- a/projects/netgrif-components-core/src/lib/side-menu/content-components/injection-tokens.ts +++ b/projects/netgrif-components-core/src/lib/side-menu/content-components/injection-tokens.ts @@ -10,4 +10,5 @@ export const NAE_SAVE_FILTER_COMPONENT = new InjectionToken>('NaeLoadFilterComponent'); export const NAE_USER_IMPERSONATE_COMPONENT = new InjectionToken>('NaeUserImpersonateComponent'); export const NAE_ADMIN_IMPERSONATE_COMPONENT = new InjectionToken>('NaeAdminImpersonateComponent'); +export const NAE_SINGLE_TASK_VIEW_COMPONENT = new InjectionToken>('NaeSingleTaskViewComponent'); export const NAE_TASK_VIEW_COMPONENT = new InjectionToken>('NaeTaskViewComponent'); diff --git a/projects/netgrif-components-core/src/lib/side-menu/content-components/task-view/model/task-view-injection-data.ts b/projects/netgrif-components-core/src/lib/side-menu/content-components/task-view/model/task-view-injection-data.ts index 71bfbbb8db..186bbb65b9 100644 --- a/projects/netgrif-components-core/src/lib/side-menu/content-components/task-view/model/task-view-injection-data.ts +++ b/projects/netgrif-components-core/src/lib/side-menu/content-components/task-view/model/task-view-injection-data.ts @@ -1,5 +1,6 @@ import {SideMenuInjectionData} from "../../../models/side-menu-injection-data"; +import {TaskSearchRequestBody} from "../../../../filter/models/task-search-request-body"; export interface TaskViewInjectionData extends SideMenuInjectionData { - taskIds: string[]; + searchBody: TaskSearchRequestBody } diff --git a/projects/netgrif-components/src/lib/dialog/dialog.module.ts b/projects/netgrif-components/src/lib/dialog/dialog.module.ts index 51c1e2966e..8bf605eba2 100644 --- a/projects/netgrif-components/src/lib/dialog/dialog.module.ts +++ b/projects/netgrif-components/src/lib/dialog/dialog.module.ts @@ -10,10 +10,8 @@ import { NAE_ADMIN_IMPERSONATE_DIALOG_COMPONENT, NAE_NEW_CASE_DIALOG_COMPONENT, NAE_USER_IMPERSONATE_DIALOG_COMPONENT, - NAE_SAVE_FILTER_DIALOG_COMPONENT, - NAE_LOAD_FILTER_DIALOG_COMPONENT, FrontActionRegistryService, - NAE_TASK_VIEW_COMPONENT, UtilityModule, NAE_MULTI_USER_ASSIGN_DIALOG_COMPONENT, NAE_IMPORT_NET_DIALOG_COMPONENT + NAE_TASK_VIEW_COMPONENT, UtilityModule, NAE_MULTI_USER_ASSIGN_DIALOG_COMPONENT, NAE_IMPORT_NET_DIALOG_COMPONENT, NAE_SINGLE_TASK_VIEW_COMPONENT } from '@netgrif/components-core'; import {A11yModule} from '@angular/cdk/a11y'; import { @@ -41,8 +39,10 @@ import { SideMenuMultiUserAssignComponentModule } from '../side-menu/content-components/multi-user-assign/side-menu-multi-user-assign-component.module'; import { MultiUserAssignDialogComponent } from './multi-user-assign-dialog/multi-user-assign-dialog.component'; +import { SingleTaskViewDialogComponent } from './single-task-view-dialog/single-task-view-dialog.component'; import { TaskViewDialogComponent } from './task-view-dialog/task-view-dialog.component'; import {openTaskDialog} from "./model/dialog-actions"; +import {TranslateModule} from "@ngx-translate/core"; @NgModule({ declarations: [ @@ -53,6 +53,7 @@ import {openTaskDialog} from "./model/dialog-actions"; UserImpersonateDialogComponent, FilterSelectorDialogComponent, MultiUserAssignDialogComponent, + SingleTaskViewDialogComponent, TaskViewDialogComponent ], exports: [ @@ -63,6 +64,7 @@ import {openTaskDialog} from "./model/dialog-actions"; UserImpersonateDialogComponent, FilterSelectorDialogComponent, MultiUserAssignDialogComponent, + SingleTaskViewDialogComponent, TaskViewDialogComponent ], imports: [ @@ -86,7 +88,8 @@ import {openTaskDialog} from "./model/dialog-actions"; HeaderComponentModule, PanelComponentModule, HotkeyModule.forRoot(), - UtilityModule + UtilityModule, + TranslateModule ], providers: [ {provide: NAE_USER_ASSIGN_DIALOG_COMPONENT, useValue: UserAssignDialogComponent}, @@ -95,6 +98,7 @@ import {openTaskDialog} from "./model/dialog-actions"; {provide: NAE_ADMIN_IMPERSONATE_DIALOG_COMPONENT, useValue: AdminImpersonateDialogComponent}, {provide: NAE_USER_IMPERSONATE_DIALOG_COMPONENT, useValue: UserImpersonateDialogComponent}, {provide: NAE_NEW_CASE_DIALOG_COMPONENT, useValue: NewCaseDialogComponent}, + {provide: NAE_SINGLE_TASK_VIEW_COMPONENT, useValue: SingleTaskViewDialogComponent}, {provide: NAE_TASK_VIEW_COMPONENT, useValue: TaskViewDialogComponent} ] }) diff --git a/projects/netgrif-components/src/lib/dialog/model/dialog-actions.ts b/projects/netgrif-components/src/lib/dialog/model/dialog-actions.ts index 82da95d909..3a5b585f54 100644 --- a/projects/netgrif-components/src/lib/dialog/model/dialog-actions.ts +++ b/projects/netgrif-components/src/lib/dialog/model/dialog-actions.ts @@ -14,7 +14,7 @@ export const openTaskDialog: FrontActionDefinition = { const ref = dialog.open(dialogComponent, { panelClass: "dialog-task-responsive", data: { - taskIds: frontAction.args[0] + searchBody: { stringId: frontAction.args[0] } } as TaskViewInjectionData, }); ref.afterClosed().subscribe(event => { diff --git a/projects/netgrif-components/src/lib/dialog/public-api.ts b/projects/netgrif-components/src/lib/dialog/public-api.ts index 21de6bdacc..fe8be08dbc 100644 --- a/projects/netgrif-components/src/lib/dialog/public-api.ts +++ b/projects/netgrif-components/src/lib/dialog/public-api.ts @@ -6,4 +6,5 @@ export * from './multi-user-assign-dialog/multi-user-assign-dialog.component'; export * from './admin-impersonate-dialog/admin-impersonate-dialog.component'; export * from './user-impersonate-dialog/user-impersonate-dialog.component'; export * from './filter-selector-dialog/filter-selector-dialog.component'; +export * from './single-task-view-dialog/single-task-view-dialog.component'; export * from './task-view-dialog/task-view-dialog.component'; diff --git a/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.html b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.html new file mode 100644 index 0000000000..3818ced116 --- /dev/null +++ b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.html @@ -0,0 +1,16 @@ +
+ {{!!taskPanelData?.task?.title ? taskPanelData?.task?.title : taskPanelData?.task?.transitionId}} +
+
+ + + +
diff --git a/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.scss b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.scss new file mode 100644 index 0000000000..b51bae35d1 --- /dev/null +++ b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.scss @@ -0,0 +1,78 @@ +.task-tab-background{ + height: 100%; + width: 100%; + overflow: auto; + background-color: transparent; +} +.search-panel{ + margin-top: 8px; +} +.content-margin{ + margin: 0 8px; +} +.task-panel-padding-mini{ + padding-bottom: 8px; + padding-top: 8px; +} + +.search-width { + width: 100%; +} + +.full-height { + height: 100%; +} +.public-button { + height: 36px; + width: 200px; +} + +.public-button-padding { + padding: 16px; + flex-direction: column; + display: flex; + align-items: center; +} + +.single-page-task { + width: 85% !important; + padding-bottom: 25px; +} + +.logoimg { + height: 120px; + outline: none !important; + cursor: pointer; +} + +.upper-card { + margin-bottom: 10px; +} + +.upper-card-title { + display: flex; + align-items: center; + flex-direction: column; +} + +.upper-divider { + border-top-width: 20px; + border-top-color: #023b7e; + border-radius: 4px; + margin-bottom: 10px; +} + +.footer-custom { + border-radius: 4px; + margin-bottom: 10px; +} +.task-view-container { + height: calc(100% - 64px) !important; + padding-left: 8px !important; + padding-right: 8px !important; + margin: 0; +} +.virtual-scroll-padding { + padding-right: 4px; +} + diff --git a/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.spec.ts b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.spec.ts new file mode 100644 index 0000000000..2406cad636 --- /dev/null +++ b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.spec.ts @@ -0,0 +1,66 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { SingleTaskViewDialogComponent } from './single-task-view-dialog.component'; +import {HttpClientTestingModule} from "@angular/common/http/testing"; +import {NoopAnimationsModule} from "@angular/platform-browser/animations"; +import {PanelComponentModule} from "../../panel/panel.module"; +import {RouterTestingModule} from "@angular/router/testing"; +import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from "@angular/material/dialog"; +import {NO_ERRORS_SCHEMA} from "@angular/core"; +import { + MaterialModule, + TranslateLibModule, + MockAuthenticationMethodService, + ConfigurationService, + TestConfigurationService, + AuthenticationMethodService, + AuthenticationService, + MockAuthenticationService, + UserResourceService, + MockUserResourceService +} from '@netgrif/components-core'; +import {HeaderComponentModule} from "../../header/header.module"; + +describe('SingleTaskViewDialogComponent', () => { + let component: SingleTaskViewDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SingleTaskViewDialogComponent], + imports: [ + MaterialModule, + HttpClientTestingModule, + NoopAnimationsModule, + PanelComponentModule, + TranslateLibModule, + RouterTestingModule.withRoutes([]), + HeaderComponentModule, + MatDialogModule + ], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MatDialogRef, useValue: {} }, + { provide: AuthenticationMethodService, useClass: MockAuthenticationMethodService }, + { provide: ConfigurationService, useClass: TestConfigurationService }, + { provide: AuthenticationService, useClass: MockAuthenticationService }, + { provide: UserResourceService, useClass: MockUserResourceService } + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SingleTaskViewDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.ts b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.ts new file mode 100644 index 0000000000..3e6fac6c26 --- /dev/null +++ b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.ts @@ -0,0 +1,86 @@ +import {AfterViewInit, Component, Inject, ViewChild} from '@angular/core'; +import { + AllowedNetsService, + AllowedNetsServiceFactory, + BaseFilter, + LoggerService, + NAE_BASE_FILTER, + SearchService, + SimpleFilter, + TaskEventNotification, + TaskViewService, + TaskViewInjectionData, + AbstractSingleTaskViewComponent, + TaskEvent +} from '@netgrif/components-core'; +import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; +import {ActivatedRoute} from "@angular/router"; +import {HeaderComponent} from "../../header/header.component"; +import { + localAllowedNetsFactory +} from "../../filter-field-content/filter-field-tabbed-case-view/filter-field-tabbed-case-view.component"; + +export function singleTaskViewFilterFactory(dialogControl: TaskViewInjectionData): BaseFilter { + if (!dialogControl) { + throw new Error('NewFilterCaseId was not provided in the side menu injection data'); + } + const injectedData = dialogControl as TaskViewInjectionData; + + return { + filter: SimpleFilter.fromTaskQuery(injectedData.searchBody) + }; +} + +@Component({ + selector: 'nc-single-task-view-dialog', + templateUrl: './single-task-view-dialog.component.html', + styleUrls: ['./single-task-view-dialog.component.scss'], + providers: [ + TaskViewService, + SearchService, + { + provide: NAE_BASE_FILTER, + useFactory: singleTaskViewFilterFactory, + deps: [MAT_DIALOG_DATA] + }, + { + provide: AllowedNetsService, + useFactory: localAllowedNetsFactory, + deps: [AllowedNetsServiceFactory] + } + ] +}) +export class SingleTaskViewDialogComponent extends AbstractSingleTaskViewComponent implements AfterViewInit { + + @ViewChild('header') public taskHeaderComponent: HeaderComponent; + protected _injectedData: TaskViewInjectionData; + + constructor(protected _dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) protected _data: TaskViewInjectionData, + protected _log: LoggerService, + taskViewService: TaskViewService, + _activatedRoute?: ActivatedRoute) { + super(taskViewService, _activatedRoute); + if (this._data) { + this._injectedData = this._data as TaskViewInjectionData; + } + } + + ngAfterViewInit(): void { + this.initializeHeader(this.taskHeaderComponent); + } + + public processTaskEvents(notification: TaskEventNotification): void { + if (!notification.success) { + return; + } + + if (notification.event === TaskEvent.FINISH) { + this._dialogRef.close({opened: false, message: 'Task finished'}); + } + + if (notification.event === TaskEvent.CANCEL) { + this._dialogRef.close({opened: false, message: 'Task canceled'}); + } + } +} diff --git a/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.html b/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.html index 9b8c7930d3..8b9bc90dd1 100644 --- a/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.html +++ b/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.html @@ -1,16 +1,5 @@ -
- {{!!taskPanelData?.task?.title ? taskPanelData?.task?.title : taskPanelData?.task?.transitionId}} -
-
- - - +
+ +
diff --git a/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.spec.ts b/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.spec.ts index 9a21979d58..75bff111a9 100644 --- a/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.spec.ts +++ b/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { TaskViewDialogComponent } from './task-view-dialog.component'; import {HttpClientTestingModule} from "@angular/common/http/testing"; import {NoopAnimationsModule} from "@angular/platform-browser/animations"; import {PanelComponentModule} from "../../panel/panel.module"; @@ -19,6 +18,7 @@ import { MockUserResourceService } from '@netgrif/components-core'; import {HeaderComponentModule} from "../../header/header.module"; +import {TaskViewDialogComponent} from "./task-view-dialog.component"; describe('TaskViewDialogComponent', () => { let component: TaskViewDialogComponent; diff --git a/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.ts b/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.ts index 29cd137f22..a594aa16f3 100644 --- a/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.ts +++ b/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.ts @@ -7,11 +7,10 @@ import { NAE_BASE_FILTER, SearchService, SimpleFilter, - TaskEventNotification, TaskViewService, TaskViewInjectionData, - AbstractSingleTaskViewComponent, - TaskEvent + TaskEventNotification, + AbstractTaskViewComponent, TaskEvent, } from '@netgrif/components-core'; import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; import {ActivatedRoute} from "@angular/router"; @@ -22,14 +21,12 @@ import { export function taskViewFilterFactory(dialogControl: TaskViewInjectionData): BaseFilter { if (!dialogControl) { - throw new Error('NewFilterCaseId was not provided in the sidemenu injectio data'); + throw new Error('NewFilterCaseId was not provided in the side menu injection data'); } const injectedData = dialogControl as TaskViewInjectionData; return { - filter: SimpleFilter.fromTaskQuery({ - stringId: injectedData.taskIds - }) + filter: SimpleFilter.fromTaskQuery(injectedData.searchBody) }; } @@ -52,7 +49,7 @@ export function taskViewFilterFactory(dialogControl: TaskViewInjectionData): Bas } ] }) -export class TaskViewDialogComponent extends AbstractSingleTaskViewComponent implements AfterViewInit { +export class TaskViewDialogComponent extends AbstractTaskViewComponent implements AfterViewInit { @ViewChild('header') public taskHeaderComponent: HeaderComponent; protected _injectedData: TaskViewInjectionData; @@ -73,7 +70,7 @@ export class TaskViewDialogComponent extends AbstractSingleTaskViewComponent imp } public processTaskEvents(notification: TaskEventNotification): void { - if (!notification.success) { + if (!notification.success || !this._injectedData['autoCloseOnEvent']) { return; } diff --git a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts index 0317e03c99..446d06a61f 100644 --- a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts +++ b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts @@ -1,4 +1,4 @@ -import {Component} from '@angular/core'; +import {Component, Injector} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {BreakpointObserver} from '@angular/cdk/layout'; import { @@ -20,6 +20,7 @@ import { } from '@netgrif/components-core'; import {animate, state, style, transition, trigger} from "@angular/animations"; import {TranslateService} from "@ngx-translate/core"; +import {MatDialog} from "@angular/material/dialog"; @Component({ selector: 'nc-navigation-double-drawer', @@ -76,10 +77,12 @@ export class NavigationDoubleDrawerComponent extends AbstractNavigationDoubleDra _dynamicRouteProviderService: DynamicNavigationRouteProviderService, _redirectService: RedirectService, _snackBarService: SnackBarService, + _dialog: MatDialog, + _injector: Injector, _navigationService: DoubleDrawerNavigationService) { super(_router, _activatedRoute, _breakpoint, _languageService, _translateService, _userService, _accessService, _log, _config, _uriService, _processService, _caseResourceService, _impersonationUserSelect, _impersonation, - _dynamicRouteProviderService, _redirectService, _snackBarService, _navigationService) + _dynamicRouteProviderService, _redirectService, _snackBarService, _dialog, _injector, _navigationService) } public toggleSection(section: string): void { From efc9ec5472d247e0125fd35d8f446d0e2df5e848 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 29 Jun 2026 16:48:05 +0200 Subject: [PATCH 05/18] [NAE-2454] View Configuration 2.0 - fix edit btn position --- .../navigation-double-drawer.component.scss | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.scss b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.scss index 203931b160..22a3038a5d 100644 --- a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.scss +++ b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.scss @@ -282,3 +282,12 @@ .padding-2px { padding: 2px; } + +.width-menu { + width: 100%; + +} + +.menu-tree-node { + margin: 5px 0; +} From 1220675e7d1dc6e3a647432e04d737bffae9eb18 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 30 Jun 2026 14:01:21 +0200 Subject: [PATCH 06/18] [NAE-2454] View Configuration 2.0 - rework loading of folders --- .../navigation/model/group-navigation-constants.ts | 4 ++-- .../service/double-drawer-navigation.service.ts | 12 +++++++++++- .../util/double-drawer-utils.ts | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.ts b/projects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.ts index ed430384d9..77c5ec0208 100644 --- a/projects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.ts +++ b/projects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.ts @@ -182,9 +182,9 @@ export enum GroupNavigationConstants { ITEM_FIELD_ID_NODE_PATH = 'nodePath', /** - * Boolean field, that is true if item contains child items + * Boolean field, that is true if the item represents a folder * */ - ITEM_FIELD_ID_HAS_CHILDREN = 'hasChildren', + ITEM_FIELD_ID_IS_FOLDER = 'is_folder', /** * CaseField containing stringIds of child instances of process preference_item diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts index 2ce5bf36e6..a5bd2d502a 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts @@ -265,7 +265,7 @@ export class DoubleDrawerNavigationService implements OnDestroy { this.itemClicked.emit({uriNode: this._uriService.activeNode, isHome: false}); } else { const path = item.resource.immediateData.find(f => f.stringId === GroupNavigationConstants.ITEM_FIELD_ID_NODE_PATH)?.value; - if (DoubleDrawerUtils.hasItemChildren(item) && !this._leftLoading$.isActive && !this._rightLoading$.isActive) { + if (DoubleDrawerUtils.isFolder(item) && !this._leftLoading$.isActive && !this._rightLoading$.isActive) { this._uriService.getNodeByPath(path).subscribe(node => { this._uriService.activeNode = node; this.itemClicked.emit({uriNode: this._uriService.activeNode, isHome: false}); @@ -454,6 +454,16 @@ export class DoubleDrawerNavigationService implements OnDestroy { } protected getItemCasesByIds(caseIds: string[], pageNumber: number, pageSize: string | number): Observable> { + if (!caseIds || caseIds.length === 0) { + return of({ + content: [], pagination: { + number: -1, + size: 0, + totalPages: 0, + totalElements: 0 + } + }); + } const searchBody: CaseSearchRequestBody = { stringId: caseIds, process: MENU_IDENTIFIERS.map(id => ({identifier: id} as PetriNetSearchRequest)), diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/util/double-drawer-utils.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/util/double-drawer-utils.ts index 16b25c94ba..a9bb571160 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/util/double-drawer-utils.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/util/double-drawer-utils.ts @@ -8,8 +8,8 @@ export class DoubleDrawerUtils { constructor() {} - public static hasItemChildren(item: NavigationItem): boolean { - return item.resource?.immediateData.find(f => f.stringId === GroupNavigationConstants.ITEM_FIELD_ID_HAS_CHILDREN)?.value; + public static isFolder(item: NavigationItem): boolean { + return item.resource?.immediateData.find(f => f.stringId === GroupNavigationConstants.ITEM_FIELD_ID_IS_FOLDER)?.value; } public static hasItemAutoOpenView(item: NavigationItem): boolean { From 8cc7d68c1132b93d0b55ff1d1b53e8debfb63758 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 30 Jun 2026 16:02:02 +0200 Subject: [PATCH 07/18] [NAE-2454] View Configuration 2.0 - fix dialog overflow --- projects/netgrif-components/src/lib/dialog/dialog.theme.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/projects/netgrif-components/src/lib/dialog/dialog.theme.scss b/projects/netgrif-components/src/lib/dialog/dialog.theme.scss index 1d2bd8f2f8..9e6f0edd85 100644 --- a/projects/netgrif-components/src/lib/dialog/dialog.theme.scss +++ b/projects/netgrif-components/src/lib/dialog/dialog.theme.scss @@ -21,8 +21,13 @@ .dialog-task-responsive { width: 40% !important; height: auto !important; + max-height: 95vh !important; min-height: 300px !important; min-width: 300px !important; + mat-mdc-dialog-container, + .mdc-dialog__surface { + overflow-y: auto !important; + } } @media only screen and (max-width: 599px) { From 7ca84cf2fd6f2bef27987707296bc7c087ed39c0 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 30 Jun 2026 16:17:26 +0200 Subject: [PATCH 08/18] [NAE-2454] View Configuration 2.0 - improve memory usage --- .../abstract-navigation-double-drawer.ts | 2 +- .../service/double-drawer-navigation.service.ts | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts index 1798d1f8e7..4407881e81 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts @@ -245,7 +245,7 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, } public logout(): void { - this._userService.logout().subscribe(response => { + this._userService.logout().pipe(take(1)).subscribe(response => { this._log.debug('User is logged out'); this.loggedOut.emit(response); if (this._config.get().services && this._config.get().services.auth && this._config.getOnLogoutPath()) { diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts index a5bd2d502a..31ac75f2be 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts @@ -140,7 +140,7 @@ export class DoubleDrawerNavigationService implements OnDestroy { node.parent = this._uriService.root; } else { this._nodeLoading$.on(); - this._uriService.getNodeByPath(this._uriService.resolveParentPath(node)).subscribe(n => { + this._uriService.getNodeByPath(this._uriService.resolveParentPath(node)).pipe(take(1)).subscribe(n => { node.parent = !n ? this._uriService.root : n; this._nodeLoading$.off(); }, error => { @@ -150,7 +150,10 @@ export class DoubleDrawerNavigationService implements OnDestroy { } } if (this._nodeLoading$.isActive) { - this._nodeLoading$.subscribe(() => { + this._nodeLoading$.pipe( + filter(isActive => !isActive), + take(1) + ).subscribe(() => { this.loadNavigationItems(node); }); } else { @@ -266,7 +269,7 @@ export class DoubleDrawerNavigationService implements OnDestroy { } else { const path = item.resource.immediateData.find(f => f.stringId === GroupNavigationConstants.ITEM_FIELD_ID_NODE_PATH)?.value; if (DoubleDrawerUtils.isFolder(item) && !this._leftLoading$.isActive && !this._rightLoading$.isActive) { - this._uriService.getNodeByPath(path).subscribe(node => { + this._uriService.getNodeByPath(path).pipe(take(1)).subscribe(node => { this._uriService.activeNode = node; this.itemClicked.emit({uriNode: this._uriService.activeNode, isHome: false}); this._rightLoading$.pipe( @@ -368,7 +371,7 @@ export class DoubleDrawerNavigationService implements OnDestroy { return; } this._leftLoading$.on(); - this._uriService.getItemCaseByNodePath(this.currentNode.parent).subscribe(page => { + this._uriService.getItemCaseByNodePath(this.currentNode.parent).pipe(take(1)).subscribe(page => { let childCases$; let targetItem; let orderedChildCaseIds; @@ -383,7 +386,7 @@ export class DoubleDrawerNavigationService implements OnDestroy { ); } - childCases$.subscribe(result => { + childCases$.pipe(take(1)).subscribe(result => { result = result.map(folder => this.resolveItemCaseToNavigationItem(folder)).filter(i => !!i); this._leftItems$.next(result.sort((a, b) => orderedChildCaseIds.indexOf(a.resource.stringId) - orderedChildCaseIds.indexOf(b.resource.stringId))); this.resolveCustomViewsInLeftSide(); @@ -406,7 +409,7 @@ export class DoubleDrawerNavigationService implements OnDestroy { protected loadRightSide() { this._rightLoading$.on(); this._moreItems$.next([]) - this._uriService.getItemCaseByNodePath(this.currentNode).subscribe(page => { + this._uriService.getItemCaseByNodePath(this.currentNode).pipe(take(1)).subscribe(page => { let childCases$; let targetItem; let orderedChildCaseIds; @@ -421,7 +424,7 @@ export class DoubleDrawerNavigationService implements OnDestroy { ); } - childCases$.subscribe(result => { + childCases$.pipe(take(1)).subscribe(result => { result = (result as Case[]).sort((a, b) => orderedChildCaseIds.indexOf(a.stringId) - orderedChildCaseIds.indexOf(b.stringId)); if (result.length > RIGHT_SIDE_INIT_PAGE_SIZE) { const rawRightItems: Case[] = result.splice(0, RIGHT_SIDE_INIT_PAGE_SIZE); From 0279592d26dd3b05cd8239fd32ba837b7cc3fde9 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 30 Jun 2026 17:37:00 +0200 Subject: [PATCH 09/18] [NAE-2454] View Configuration 2.0 - fix user and double drawer race condition --- .../double-drawer-navigation.service.ts | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts index 31ac75f2be..d65d43aea0 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts @@ -2,7 +2,7 @@ import {EventEmitter, Injectable, OnDestroy} from '@angular/core'; import {BehaviorSubject, Observable, of, Subscription} from 'rxjs'; import {UriService} from "../../service/uri.service"; import {LoadingEmitter} from "../../../utility/loading-emitter"; -import {filter, map, take} from "rxjs/operators"; +import {filter, map, take, switchMap} from "rxjs/operators"; import {Case} from "../../../resources/interface/case"; import {LoggerService} from "../../../logger/services/logger.service"; import {DoubleDrawerUtils} from "../util/double-drawer-utils"; @@ -32,6 +32,7 @@ import { import { UriNodeResource } from '../../model/uri-resource'; import {MenuItemClickEvent, MenuItemLoadedEvent} from '../../model/navigation-menu-events'; import {GroupNavigationConstants} from "../../model/group-navigation-constants"; +import {UserService} from "../../../user/services/user.service"; /** * Service for managing navigation in double-drawer @@ -66,6 +67,7 @@ export class DoubleDrawerNavigationService implements OnDestroy { protected _rightLoading$: LoadingEmitter; protected _nodeLoading$: LoadingEmitter; protected _currentNodeSubscription: Subscription; + protected _userSubscription: Subscription; /** * Currently display uri * Siblings of the node are on the left, children are on the right @@ -83,6 +85,7 @@ export class DoubleDrawerNavigationService implements OnDestroy { constructor(protected _uriService: UriService, protected _log: LoggerService, + protected _userService: UserService, protected _config: ConfigurationService, protected _activatedRoute: ActivatedRoute, protected _caseResourceService: CaseResourceService, @@ -105,13 +108,17 @@ export class DoubleDrawerNavigationService implements OnDestroy { this.itemClicked = new EventEmitter(); this.itemLoaded = new EventEmitter(); - this._currentNodeSubscription = this._uriService.activeNode$.subscribe(node => { + this._currentNodeSubscription = this._userService.user$.pipe( + filter(user => !!user && user.id !== ''), + switchMap(() => this._uriService.activeNode$) + ).subscribe(node => { this.currentNode = node; }); } public ngOnDestroy(): void { this._currentNodeSubscription?.unsubscribe(); + this._userSubscription?.unsubscribe(); this._leftLoading$.complete(); this._rightLoading$.complete(); this._nodeLoading$.complete(); @@ -334,17 +341,19 @@ export class DoubleDrawerNavigationService implements OnDestroy { public initializeCustomViewsOfView(view: View, viewConfigPath: string): void { if (!view || this.customItemsInitialized || this.hiddenCustomItemsInitialized) return; - Object.entries(view.children).forEach(([key, childView]) => { - const childViewConfigPath: string = viewConfigPath + '/' + key; - this.resolveUriForChildViews(childViewConfigPath, childView); - this.resolveHiddenMenuItemFromChildViews(childViewConfigPath, childView); - }); + this._userSubscription = this._userService.user$.pipe(filter(user => !!user && user.id !== '')).subscribe(user => { + Object.entries(view.children).forEach(([key, childView]) => { + const childViewConfigPath: string = viewConfigPath + '/' + key; + this.resolveUriForChildViews(childViewConfigPath, childView); + this.resolveHiddenMenuItemFromChildViews(childViewConfigPath, childView); + }); - this.resolveCustomViewsInRightSide(); - this.resolveCustomViewsInLeftSide(); + this.resolveCustomViewsInRightSide(); + this.resolveCustomViewsInLeftSide(); - this.customItemsInitialized = true; - this.hiddenCustomItemsInitialized = true; + this.customItemsInitialized = true; + this.hiddenCustomItemsInitialized = true; + }); } public switchOrder(): void { From 377de3ff25914b19034c8c68fa285d55757b69ae Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 6 Jul 2026 10:16:24 +0200 Subject: [PATCH 10/18] [NAE-2454] View Configuration 2.0 - implement handling of dynamic default sorting in menu item views --- .../header/case-header/case-header.service.ts | 14 +-- .../sort-mode/abstract-sort-mode.component.ts | 2 +- .../lib/header/models/meta-fields-factory.ts | 42 +++++++++ .../src/lib/header/models/public-api.ts | 1 + .../header/task-header/task-header.service.ts | 14 +-- .../workflow-header.service.ts | 13 +-- .../model/group-navigation-constants.ts | 20 ++++ .../models/dynamic-default-sort-token.ts | 5 + .../case-view/service/case-view-service.ts | 26 +++++- .../src/lib/view/public-api.ts | 1 + .../task-view/service/task-view.service.ts | 34 ++++++- .../src/lib/header/header.component.ts | 4 +- .../model/factory-methods.ts | 91 ++++++++++++++++++- .../default-simple-case-view.component.ts | 8 +- .../default-simple-task-view.component.ts | 8 +- .../default-tabbed-case-view.component.ts | 10 +- .../default-tabbed-task-view.component.ts | 8 +- 17 files changed, 252 insertions(+), 49 deletions(-) create mode 100644 projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts create mode 100644 projects/netgrif-components-core/src/lib/view/case-view/models/dynamic-default-sort-token.ts diff --git a/projects/netgrif-components-core/src/lib/header/case-header/case-header.service.ts b/projects/netgrif-components-core/src/lib/header/case-header/case-header.service.ts index 440d88cab6..1d58733cc5 100644 --- a/projects/netgrif-components-core/src/lib/header/case-header/case-header.service.ts +++ b/projects/netgrif-components-core/src/lib/header/case-header/case-header.service.ts @@ -1,8 +1,7 @@ import {Inject, Injectable, OnDestroy, Optional} from '@angular/core'; import {AbstractHeaderService} from '../abstract-header-service'; import {HeaderType} from '../models/header-type'; -import {HeaderColumn, HeaderColumnType} from '../models/header-column'; -import {CaseMetaField} from './case-menta-enum'; +import {HeaderColumn} from '../models/header-column'; import {UserPreferenceService} from '../../user/services/user-preference.service'; import {LoggerService} from '../../logger/services/logger.service'; import {NAE_DEFAULT_HEADERS} from '../models/default-headers-token'; @@ -10,6 +9,7 @@ import {Subscription} from 'rxjs'; import {OverflowService} from '../services/overflow.service'; import {ViewIdService} from '../../user/services/view-id.service'; import {AllowedNetsService} from '../../allowed-nets/services/allowed-nets.service'; +import {getCaseMetaHeaders} from "../models/meta-fields-factory"; @Injectable() @@ -36,15 +36,7 @@ export class CaseHeaderService extends AbstractHeaderService implements OnDestro } protected createMetaHeaders(): Array { - return [ - new HeaderColumn(HeaderColumnType.META, CaseMetaField.VISUAL_ID, 'headers.caseMeta.visualID', 'text'), - new HeaderColumn(HeaderColumnType.META, CaseMetaField.MONGO_ID, 'headers.caseMeta.mongoID', 'text', false), - new HeaderColumn(HeaderColumnType.META, CaseMetaField.TITLE, 'headers.caseMeta.title', 'text'), - new HeaderColumn(HeaderColumnType.META, CaseMetaField.AUTHOR, 'headers.caseMeta.author', 'user'), - new HeaderColumn(HeaderColumnType.META, CaseMetaField.CREATION_DATE, 'headers.caseMeta.creationDate', 'date'), - new HeaderColumn(HeaderColumnType.META, CaseMetaField.PROCESS_IDENTIFIER, 'headers.caseMeta.processIdentifier', 'text'), - new HeaderColumn(HeaderColumnType.META, CaseMetaField.PETRI_NET_ID, 'headers.caseMeta.petriNetId', 'text'), - ]; + return getCaseMetaHeaders(); } ngOnDestroy(): void { diff --git a/projects/netgrif-components-core/src/lib/header/header-modes/sort-mode/abstract-sort-mode.component.ts b/projects/netgrif-components-core/src/lib/header/header-modes/sort-mode/abstract-sort-mode.component.ts index 0e27c089ce..0612a0959a 100644 --- a/projects/netgrif-components-core/src/lib/header/header-modes/sort-mode/abstract-sort-mode.component.ts +++ b/projects/netgrif-components-core/src/lib/header/header-modes/sort-mode/abstract-sort-mode.component.ts @@ -13,7 +13,7 @@ export abstract class AbstractSortModeComponent extends AbstractHeaderModeCompon @Input() public headerService: AbstractHeaderService; - constructor() { + protected constructor() { super(); } diff --git a/projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts b/projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts new file mode 100644 index 0000000000..77f90f137d --- /dev/null +++ b/projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts @@ -0,0 +1,42 @@ +import {HeaderColumn, HeaderColumnType} from "./header-column"; +import {CaseMetaField} from "../case-header/case-menta-enum"; +import {TaskMetaField} from "../task-header/task-meta-enum"; +import {WorkflowMetaField} from "../workflow-header/workflow-meta-enum"; + +// todo 2454 doc +export function getCaseMetaHeaders(): HeaderColumn[] { + return [ + new HeaderColumn(HeaderColumnType.META, CaseMetaField.VISUAL_ID, 'headers.caseMeta.visualID', 'text'), + new HeaderColumn(HeaderColumnType.META, CaseMetaField.MONGO_ID, 'headers.caseMeta.mongoID', 'text', false), + new HeaderColumn(HeaderColumnType.META, CaseMetaField.TITLE, 'headers.caseMeta.title', 'text'), + new HeaderColumn(HeaderColumnType.META, CaseMetaField.AUTHOR, 'headers.caseMeta.author', 'user'), + new HeaderColumn(HeaderColumnType.META, CaseMetaField.CREATION_DATE, 'headers.caseMeta.creationDate', 'date'), + new HeaderColumn(HeaderColumnType.META, CaseMetaField.PROCESS_IDENTIFIER, 'headers.caseMeta.processIdentifier', 'text'), + new HeaderColumn(HeaderColumnType.META, CaseMetaField.PETRI_NET_ID, 'headers.caseMeta.petriNetId', 'text'), + ]; +} + +// todo 2454 doc +export function getTaskMetaHeaders(): HeaderColumn[] { + return [ + new HeaderColumn(HeaderColumnType.META, TaskMetaField.CASE, 'headers.taskMeta.case', 'text'), + new HeaderColumn(HeaderColumnType.META, TaskMetaField.CASE_ID, 'headers.taskMeta.caseID', 'text', false), + new HeaderColumn(HeaderColumnType.META, TaskMetaField.TASK_ID, 'headers.taskMeta.taskID', 'text', false), + new HeaderColumn(HeaderColumnType.META, TaskMetaField.TITLE, 'headers.caseMeta.title', 'text'), + new HeaderColumn(HeaderColumnType.META, TaskMetaField.PRIORITY, 'headers.taskMeta.priority', 'enumeration'), + new HeaderColumn(HeaderColumnType.META, TaskMetaField.USER, 'headers.taskMeta.user', 'text'), + new HeaderColumn(HeaderColumnType.META, TaskMetaField.ASSIGN_DATE, 'headers.taskMeta.assignDate', 'date'), + ]; +} + +// todo 2454 doc +export function getWorkflowMetaHeaders(): HeaderColumn[] { + return [ + new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.INITIALS, 'headers.workflowMeta.initials', 'text'), + new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.TITLE, 'headers.workflowMeta.title', 'text'), + new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.NET_ID, 'headers.workflowMeta.netId', 'text', false), + new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.VERSION, 'headers.workflowMeta.version', 'text'), + new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.AUTHOR, 'headers.workflowMeta.author', 'text'), + new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.CREATION_DATE, 'headers.workflowMeta.creationDate', 'date'), + ]; +} diff --git a/projects/netgrif-components-core/src/lib/header/models/public-api.ts b/projects/netgrif-components-core/src/lib/header/models/public-api.ts index 99d746c793..74de53c1e1 100644 --- a/projects/netgrif-components-core/src/lib/header/models/public-api.ts +++ b/projects/netgrif-components-core/src/lib/header/models/public-api.ts @@ -2,6 +2,7 @@ export * from './header-column'; export * from './header-mode'; export * from './header-type'; export * from './default-headers-token'; +export * from './meta-fields-factory' export * from './user-changes/header-change'; export * from './user-changes/header-change-description'; diff --git a/projects/netgrif-components-core/src/lib/header/task-header/task-header.service.ts b/projects/netgrif-components-core/src/lib/header/task-header/task-header.service.ts index d6630dd643..931edf517d 100644 --- a/projects/netgrif-components-core/src/lib/header/task-header/task-header.service.ts +++ b/projects/netgrif-components-core/src/lib/header/task-header/task-header.service.ts @@ -1,8 +1,7 @@ import {Inject, Injectable, OnDestroy, Optional} from '@angular/core'; import {AbstractHeaderService} from '../abstract-header-service'; import {HeaderType} from '../models/header-type'; -import {HeaderColumn, HeaderColumnType} from '../models/header-column'; -import {TaskMetaField} from './task-meta-enum'; +import {HeaderColumn} from '../models/header-column'; import {UserPreferenceService} from '../../user/services/user-preference.service'; import {LoggerService} from '../../logger/services/logger.service'; import {Subscription} from 'rxjs'; @@ -10,6 +9,7 @@ import {NAE_DEFAULT_HEADERS} from '../models/default-headers-token'; import {ViewIdService} from '../../user/services/view-id.service'; import {AllowedNetsService} from '../../allowed-nets/services/allowed-nets.service'; import {OverflowService} from '../services/overflow.service'; +import {getTaskMetaHeaders} from "../models/meta-fields-factory"; @Injectable() @@ -36,15 +36,7 @@ export class TaskHeaderService extends AbstractHeaderService implements OnDestro } protected createMetaHeaders(): Array { - return [ - new HeaderColumn(HeaderColumnType.META, TaskMetaField.CASE, 'headers.taskMeta.case', 'text'), - new HeaderColumn(HeaderColumnType.META, TaskMetaField.CASE_ID, 'headers.taskMeta.caseID', 'text', false), - new HeaderColumn(HeaderColumnType.META, TaskMetaField.TASK_ID, 'headers.taskMeta.taskID', 'text', false), - new HeaderColumn(HeaderColumnType.META, TaskMetaField.TITLE, 'headers.caseMeta.title', 'text'), - new HeaderColumn(HeaderColumnType.META, TaskMetaField.PRIORITY, 'headers.taskMeta.priority', 'enumeration'), - new HeaderColumn(HeaderColumnType.META, TaskMetaField.USER, 'headers.taskMeta.user', 'text'), - new HeaderColumn(HeaderColumnType.META, TaskMetaField.ASSIGN_DATE, 'headers.taskMeta.assignDate', 'date'), - ]; + return getTaskMetaHeaders(); } protected saveState() { diff --git a/projects/netgrif-components-core/src/lib/header/workflow-header/workflow-header.service.ts b/projects/netgrif-components-core/src/lib/header/workflow-header/workflow-header.service.ts index 806d01d003..b78fc7e92b 100644 --- a/projects/netgrif-components-core/src/lib/header/workflow-header/workflow-header.service.ts +++ b/projects/netgrif-components-core/src/lib/header/workflow-header/workflow-header.service.ts @@ -1,12 +1,12 @@ import {Injectable, OnDestroy, Optional} from '@angular/core'; import {AbstractHeaderService} from '../abstract-header-service'; import {HeaderType} from '../models/header-type'; -import {HeaderColumn, HeaderColumnType} from '../models/header-column'; +import {HeaderColumn} from '../models/header-column'; import {UserPreferenceService} from '../../user/services/user-preference.service'; import {LoggerService} from '../../logger/services/logger.service'; -import {WorkflowMetaField} from './workflow-meta-enum'; import {ViewIdService} from '../../user/services/view-id.service'; import {OverflowService} from '../services/overflow.service'; +import {getWorkflowMetaHeaders} from "../models/meta-fields-factory"; @Injectable() export class WorkflowHeaderService extends AbstractHeaderService implements OnDestroy { @@ -21,14 +21,7 @@ export class WorkflowHeaderService extends AbstractHeaderService implements OnDe } protected createMetaHeaders(): Array { - return [ - new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.INITIALS, 'headers.workflowMeta.initials', 'text'), - new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.TITLE, 'headers.workflowMeta.title', 'text'), - new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.NET_ID, 'headers.workflowMeta.netId', 'text', false), - new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.VERSION, 'headers.workflowMeta.version', 'text'), - new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.AUTHOR, 'headers.workflowMeta.author', 'text'), - new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.CREATION_DATE, 'headers.workflowMeta.creationDate', 'date'), - ]; + return getWorkflowMetaHeaders(); } ngOnDestroy(): void { diff --git a/projects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.ts b/projects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.ts index 77c5ec0208..a7d347e9c0 100644 --- a/projects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.ts +++ b/projects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.ts @@ -246,6 +246,26 @@ export enum GroupNavigationConstants { */ ITEM_FIELD_TASK_INHERIT_ALLOWED_NETS = "task_inherit_allowed_nets", + /** + * Text field, which contains identifier of active column in header for sorting + */ + ITEM_FIELD_CASE_HEADERS_SORT_MODE_ACTIVE = "case_headers_sort_mode_active", + + /** + * Enumeration map field, which contains default direction for sorting + */ + ITEM_FIELD_CASE_HEADERS_SORT_MODE_DIRECTION = "case_headers_sort_mode_direction", + + /** + * Text field, which contains identifier of active column in header for sorting + */ + ITEM_FIELD_TASK_HEADERS_SORT_MODE_ACTIVE = "task_headers_sort_mode_active", + + /** + * Enumeration map field, which contains default direction for sorting + */ + ITEM_FIELD_TASK_HEADERS_SORT_MODE_DIRECTION = "task_headers_sort_mode_direction", + /** * Transition ID, which contains all configuration data */ diff --git a/projects/netgrif-components-core/src/lib/view/case-view/models/dynamic-default-sort-token.ts b/projects/netgrif-components-core/src/lib/view/case-view/models/dynamic-default-sort-token.ts new file mode 100644 index 0000000000..682bb78cc2 --- /dev/null +++ b/projects/netgrif-components-core/src/lib/view/case-view/models/dynamic-default-sort-token.ts @@ -0,0 +1,5 @@ +import {InjectionToken} from "@angular/core"; +import {SortChangeDescription} from "../../../header/models/user-changes/sort-change-description"; +import {Observable} from "rxjs"; + +export const NAE_DYNAMIC_DEFAULT_SORT = new InjectionToken>('NaeDynamicDefaultSort'); diff --git a/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts b/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts index ac84fcfc6e..6d34764899 100644 --- a/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts +++ b/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts @@ -37,6 +37,8 @@ import {PaginationParams} from '../../../utility/pagination/pagination-params'; import {createSortParam, PaginationSort} from '../../../utility/pagination/pagination-sort'; import {MatDialog} from '@angular/material/dialog'; import {NAE_NEW_CASE_DIALOG_COMPONENT} from '../../../dialog/injection-tokens'; +import {NAE_DYNAMIC_DEFAULT_SORT} from "../models/dynamic-default-sort-token"; +import {SortChangeDescription} from "../../../header/models/user-changes/sort-change-description"; @Injectable() export class CaseViewService extends AbstractSortableViewComponent implements OnDestroy { @@ -65,6 +67,7 @@ export class CaseViewService extends AbstractSortableViewComponent implements On protected _processService: ProcessService, resolver: SearchIndexResolverService, @Optional() @Inject(NAE_NEW_CASE_DIALOG_COMPONENT) protected _newCaseComponent: any, + @Optional() @Inject(NAE_DYNAMIC_DEFAULT_SORT) protected _dynamicDefaultSort$: Observable, @Optional() @Inject(NAE_NEW_CASE_CONFIGURATION) newCaseConfig: NewCaseConfiguration, protected _permissionService: PermissionService) { super(resolver); @@ -74,6 +77,7 @@ export class CaseViewService extends AbstractSortableViewComponent implements On Object.assign(this._newCaseConfiguration, newCaseConfig); } this._loading$ = new LoadingWithFilterEmitter(); + // todo 2454 sub leak this._searchService.activeFilter$.subscribe(() => { this.reload(); }); @@ -84,9 +88,7 @@ export class CaseViewService extends AbstractSortableViewComponent implements On totalPages: undefined, number: -1 }; - this._nextPage$ = new BehaviorSubject( - new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0})) - ); + this._nextPage$ = new BehaviorSubject(null); const casesMap = this._nextPage$.pipe( mergeMap(p => this.loadPage(p)), @@ -117,6 +119,15 @@ export class CaseViewService extends AbstractSortableViewComponent implements On map(v => Object.values(v) as Array), tap(cases => this._cases = cases as Array), ); + + if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { + this._dynamicDefaultSort$.subscribe(sortChangeDesc => { + this._lastHeaderSearchState = sortChangeDesc; + this._nextPage$.next(new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0}))); + }); + } else { + this._nextPage$.next(new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0}))); + } } ngOnDestroy(): void { @@ -220,7 +231,14 @@ export class CaseViewService extends AbstractSortableViewComponent implements On if (this.isLoadingRelevantFilter(requestContext) || this._endOfData) { return; } - this._nextPage$.next(requestContext); + if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { + this._dynamicDefaultSort$.subscribe(sortChangeDesc => { + this._lastHeaderSearchState = sortChangeDesc; + this._nextPage$.next(requestContext); + }); + } else { + this._nextPage$.next(requestContext); + } } private isLoadingRelevantFilter(requestContext?: PageLoadRequestContext): boolean { diff --git a/projects/netgrif-components-core/src/lib/view/public-api.ts b/projects/netgrif-components-core/src/lib/view/public-api.ts index 1dbd20e931..dc68d9794e 100644 --- a/projects/netgrif-components-core/src/lib/view/public-api.ts +++ b/projects/netgrif-components-core/src/lib/view/public-api.ts @@ -31,6 +31,7 @@ export * from './abstract/page-load-request-context'; export * from './case-view/models/autoswitch-token' export * from './case-view/models/open-existing-tab-token' export * from './case-view/models/new-case-configuration-injection-token'; +export * from './case-view/models/dynamic-default-sort-token'; export * from './task-view/models/injection-token-task-endpoint'; export * from './task-view/models/task-view-configuration-injection-token'; export * from './workflow-view/models/injection-token-workflow-service'; diff --git a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts index f4192142a5..caaa7632d4 100644 --- a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts +++ b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts @@ -28,6 +28,8 @@ import {TaskViewConfiguration} from '../models/task-view-configuration'; import {ChangedFieldsMap} from '../../../event/services/interfaces/changed-fields-map'; import {PaginationParams} from '../../../utility/pagination/pagination-params'; import {createSortParam, PaginationSort} from '../../../utility/pagination/pagination-sort'; +import {NAE_DYNAMIC_DEFAULT_SORT} from "../../case-view/models/dynamic-default-sort-token"; +import {SortChangeDescription} from "../../../header/models/user-changes/sort-change-description"; @Injectable() @@ -61,6 +63,7 @@ export class TaskViewService extends AbstractSortableViewComponent implements On private _userComparator: UserComparatorService, resolver: SearchIndexResolverService, @Optional() @Inject(NAE_PREFERRED_TASK_ENDPOINT) protected readonly _preferredEndpoint: TaskEndpoint = null, + @Optional() @Inject(NAE_DYNAMIC_DEFAULT_SORT) protected _dynamicDefaultSort$: Observable, @Optional() @Inject(NAE_TASK_VIEW_CONFIGURATION) taskViewConfig: TaskViewConfiguration = null) { super(resolver); this._tasks$ = new Subject>(); @@ -74,9 +77,7 @@ export class TaskViewService extends AbstractSortableViewComponent implements On totalPages: undefined, number: -1 }; - this._requestedPage$ = new BehaviorSubject( - new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0})) - ); + this._requestedPage$ = new BehaviorSubject(null); this._panelUpdate$ = new BehaviorSubject>([]); this._closeTab$ = new ReplaySubject(1); this._preferredEndpoint = taskViewConfig?.preferredEndpoint ?? (this._preferredEndpoint ?? TaskEndpoint.MONGO); @@ -149,6 +150,15 @@ export class TaskViewService extends AbstractSortableViewComponent implements On this._subCloseTask = (taskViewConfig?.closeTaskTabOnNoTasks ?? of(true)).subscribe(bool => { this._closeTaskTabOnNoTasks = bool; }); + + if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { + this._dynamicDefaultSort$.subscribe(sortChangeDesc => { + this._lastHeaderSearchState = sortChangeDesc; + this._requestedPage$.next(new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0}))); + }); + } else { + this._requestedPage$.next(new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0}))); + } } ngOnDestroy(): void { @@ -297,7 +307,14 @@ export class TaskViewService extends AbstractSortableViewComponent implements On } if (renderedRange.end === totalLoaded) { - this._requestedPage$.next(requestContext); + if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { + this._dynamicDefaultSort$.subscribe(sortChangeDesc => { + this._lastHeaderSearchState = sortChangeDesc; + this._requestedPage$.next(requestContext); + }); + } else { + this._requestedPage$.next(requestContext); + } } } @@ -311,7 +328,14 @@ export class TaskViewService extends AbstractSortableViewComponent implements On if (this.isLoadingRelevantFilter(requestContext) || this._endOfData) { return; } - this._requestedPage$.next(requestContext); + if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { + this._dynamicDefaultSort$.subscribe(sortChangeDesc => { + this._lastHeaderSearchState = sortChangeDesc; + this._requestedPage$.next(requestContext); + }); + } else { + this._requestedPage$.next(requestContext); + } } private isLoadingRelevantFilter(requestContext?: PageLoadRequestContext): boolean { diff --git a/projects/netgrif-components/src/lib/header/header.component.ts b/projects/netgrif-components/src/lib/header/header.component.ts index f3ed1e8e54..8c390d5349 100644 --- a/projects/netgrif-components/src/lib/header/header.component.ts +++ b/projects/netgrif-components/src/lib/header/header.component.ts @@ -1,4 +1,4 @@ -import {Component, Inject, Injector, OnInit, Optional} from '@angular/core'; +import {Component, Inject, Injector, Optional} from '@angular/core'; import { AbstractHeaderComponent, CaseHeaderService, @@ -6,7 +6,7 @@ import { HeaderSearchService, TaskHeaderService, WorkflowHeaderService, - OverflowService, MultichoiceField, DATA_FIELD_PORTAL_DATA, DataFieldPortalData, EnumerationField, CaseViewService + OverflowService, MultichoiceField, DATA_FIELD_PORTAL_DATA, DataFieldPortalData, CaseViewService } from '@netgrif/components-core'; import {TranslateService} from '@ngx-translate/core'; diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts index 82a8e64406..b4eae8f9e1 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts @@ -6,10 +6,19 @@ import { navigationItemTaskAllowedNetsServiceFactory, navigationItemTaskFilterFactory, FilterType, - FilterExtractionService, GroupNavigationConstants + HeaderColumnType, + DataGroup, + SortChangeDescription, + HeaderColumn, + getCaseMetaHeaders, + getTaskMetaHeaders, + FilterExtractionService, GroupNavigationConstants, ProcessService, extractFieldValueFromData } from '@netgrif/components-core'; import {InjectedTabbedCaseViewDataWithNavigationItemTaskData} from './injected-tabbed-case-view-data-with-navigation-item-task-data'; import {ActivatedRoute} from '@angular/router'; +import {take, map} from "rxjs/operators"; +import {of, Observable} from "rxjs"; +import {SortDirection} from '@angular/material/sort'; /** * Converts a navigation item case task data injected by the {@link NAE_TAB_DATA} injection token into a {@link BaseFilter} instance @@ -37,3 +46,83 @@ export function filterCaseTabbedDataAllowedNetsServiceFactory(allowedNetsService return navigationItemTaskAllowedNetsServiceFactory(allowedNetsServiceFactory, baseAllowedNets, true, tabData.navigationItemTaskData); } + +// todo 2454 doc +export function buildDynamicSortChangeDescriptionForCase$(menuItemData: Array, processService: ProcessService): Observable | undefined { + return buildDynamicSortChangeDescription$(menuItemData, processService, 'case'); +} + +// todo 2454 doc +export function buildDynamicSortChangeDescriptionForTask$(menuItemData: Array, processService: ProcessService): Observable | undefined { + return buildDynamicSortChangeDescription$(menuItemData, processService, 'task'); +} + +function buildDynamicSortChangeDescription$(menuItemData: Array, processService: ProcessService, + viewType: 'case' | 'task' ): Observable | undefined { + const activeColumn: string = extractFieldValueFromData(menuItemData, + viewType === 'task' ? GroupNavigationConstants.ITEM_FIELD_TASK_HEADERS_SORT_MODE_ACTIVE : GroupNavigationConstants.ITEM_FIELD_CASE_HEADERS_SORT_MODE_ACTIVE); + if (!activeColumn || activeColumn === '') { + return undefined; + } + + const firstDashIdx = activeColumn.indexOf('-'); + if (firstDashIdx === -1) { + return undefined; + } + + let direction: SortDirection = extractFieldValueFromData(menuItemData, + viewType === 'task' ? GroupNavigationConstants.ITEM_FIELD_TASK_HEADERS_SORT_MODE_DIRECTION : GroupNavigationConstants.ITEM_FIELD_CASE_HEADERS_SORT_MODE_DIRECTION); + if (!direction) { + direction = ''; + } + + const colTypeRaw: string = activeColumn.substring(0, firstDashIdx); + let colType: HeaderColumnType; + let processIdentifier: string; + if (!!colTypeRaw && colTypeRaw === HeaderColumnType.META) { + colType = HeaderColumnType.META + } else if (!!colTypeRaw && colTypeRaw !== '') { + processIdentifier = colTypeRaw; + colType = HeaderColumnType.IMMEDIATE; + } + + const colIdentifier: string = activeColumn.substring(firstDashIdx + 1, activeColumn.length); + + if (!!processIdentifier) { + return processService.getNet(processIdentifier).pipe( + take(1), + map(net => { + let fieldType: string = net.immediateData.find(data => data.stringId === colIdentifier)?.type + return { + columnType: colType, + fieldIdentifier: colIdentifier, + sortDirection: direction, + columnIdentifier: -1, + fieldType: !!fieldType ? fieldType : 'text' + } + }) + ) + } else { + return of({ + columnType: colType, + fieldIdentifier: colIdentifier, + sortDirection: direction, + columnIdentifier: -1, + fieldType: determineMetaFieldType(viewType, colIdentifier) + }); + } +} + +function determineMetaFieldType(viewType: 'case' | 'task', colIdentifier: string): string { + let metaFields: HeaderColumn[]; + if (viewType === 'case') { + metaFields = getCaseMetaHeaders(); + } else if (viewType === 'task') { + metaFields = getTaskMetaHeaders(); + } + if (!metaFields) { + return 'text'; + } + const fieldType: string = metaFields.find(headerCol => headerCol.fieldIdentifier === colIdentifier)?.fieldType + return !!fieldType ? fieldType : 'text'; +} diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-case-view/default-simple-case-view.component.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-case-view/default-simple-case-view.component.ts index a7839b5f59..915abf2d66 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-case-view/default-simple-case-view.component.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-case-view/default-simple-case-view.component.ts @@ -25,12 +25,13 @@ import { LoadingEmitter, HeaderColumn, LoggerService, - SnackBarService + SnackBarService, NAE_DYNAMIC_DEFAULT_SORT, ProcessService } from '@netgrif/components-core'; import {HeaderComponent} from '../../../../../header/header.component'; import {ActivatedRoute} from "@angular/router"; import {Subscription} from "rxjs"; import {TranslateService} from "@ngx-translate/core"; +import {buildDynamicSortChangeDescriptionForCase$} from "../../model/factory-methods"; function baseFilterFactory(extractionService: FilterExtractionService, @@ -55,6 +56,11 @@ function allowedNetsFactory(factory: AllowedNetsServiceFactory, CaseViewService, SearchService, OverflowService, + { + provide: NAE_DYNAMIC_DEFAULT_SORT, + useFactory: buildDynamicSortChangeDescriptionForCase$, + deps: [NAE_NAVIGATION_ITEM_TASK_DATA, ProcessService] + }, { provide: NAE_BASE_FILTER, useFactory: baseFilterFactory, diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-task-view/default-simple-task-view.component.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-task-view/default-simple-task-view.component.ts index daef5db4c7..6c4d34bf3b 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-task-view/default-simple-task-view.component.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-task-view/default-simple-task-view.component.ts @@ -22,10 +22,11 @@ import { FilterType, SearchMode, extractSearchTypeFromData, extractFieldValueFromData, SearchComponentConfiguration, GroupNavigationConstants, HeaderMode, DataGroup, NAE_DEFAULT_HEADERS, I18nFieldValue, - navigationItemTaskViewDefaultHeadersFactory + navigationItemTaskViewDefaultHeadersFactory, NAE_DYNAMIC_DEFAULT_SORT, ProcessService } from '@netgrif/components-core'; import {HeaderComponent} from '../../../../../header/header.component'; import {ActivatedRoute} from '@angular/router'; +import {buildDynamicSortChangeDescriptionForTask$} from "../../model/factory-methods"; function baseFilterFactory(extractionService: FilterExtractionService, activatedRoute?: ActivatedRoute, @@ -50,6 +51,11 @@ function allowedNetsFactory(factory: AllowedNetsServiceFactory, SearchService, ViewIdService, ChangedFieldsService, + { + provide: NAE_DYNAMIC_DEFAULT_SORT, + useFactory: buildDynamicSortChangeDescriptionForTask$, + deps: [NAE_NAVIGATION_ITEM_TASK_DATA, ProcessService] + }, { provide: NAE_VIEW_ID_SEGMENT, useFactory: groupNavigationViewIdSegmentFactory, deps: [ActivatedRoute]}, { provide: NAE_BASE_FILTER, diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.ts index ed706ddf68..44a64f2b75 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.ts @@ -32,13 +32,16 @@ import { LoadingEmitter, SnackBarService, HeaderColumn, - ExportService + ExportService, + NAE_DYNAMIC_DEFAULT_SORT, + ProcessService } from '@netgrif/components-core'; import {HeaderComponent} from '../../../../../header/header.component'; import { InjectedTabbedCaseViewDataWithNavigationItemTaskData } from '../../model/injected-tabbed-case-view-data-with-navigation-item-task-data'; import { + buildDynamicSortChangeDescriptionForCase$, filterCaseTabbedDataAllowedNetsServiceFactory, filterCaseTabbedDataFilterFactory } from '../../model/factory-methods'; @@ -66,6 +69,11 @@ import {TranslateService} from "@ngx-translate/core"; useFactory: filterCaseTabbedDataAllowedNetsServiceFactory, deps: [AllowedNetsServiceFactory, BaseAllowedNetsService, NAE_TAB_DATA] }, + { + provide: NAE_DYNAMIC_DEFAULT_SORT, + useFactory: buildDynamicSortChangeDescriptionForCase$, + deps: [NAE_NAVIGATION_ITEM_TASK_DATA, ProcessService] + }, { provide: NAE_SEARCH_CATEGORIES, useExisting: NAE_DEFAULT_CASE_SEARCH_CATEGORIES }, diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-task-view/default-tabbed-task-view.component.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-task-view/default-tabbed-task-view.component.ts index b823d8d1c1..d68ab9dbd6 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-task-view/default-tabbed-task-view.component.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-task-view/default-tabbed-task-view.component.ts @@ -23,12 +23,13 @@ import { OverflowService, extractFieldValueFromData, I18nFieldValue, - GroupNavigationConstants + GroupNavigationConstants, NAE_DYNAMIC_DEFAULT_SORT, ProcessService } from '@netgrif/components-core'; import {HeaderComponent} from '../../../../../header/header.component'; import { InjectedTabbedTaskViewDataWithNavigationItemTaskData } from "../../model/injected-tabbed-task-view-data-with-navigation-item-task-data"; +import {buildDynamicSortChangeDescriptionForTask$} from "../../model/factory-methods"; export function baseFilterFactory(injectedTabData: InjectedTabbedTaskViewDataWithNavigationItemTaskData) { return { @@ -47,6 +48,11 @@ export function baseFilterFactory(injectedTabData: InjectedTabbedTaskViewDataWit ViewIdService, ChangedFieldsService, OverflowService, + { + provide: NAE_DYNAMIC_DEFAULT_SORT, + useFactory: buildDynamicSortChangeDescriptionForTask$, + deps: [NAE_NAVIGATION_ITEM_TASK_DATA, ProcessService] + }, { provide: NAE_BASE_FILTER, useFactory: baseFilterFactory, From 17e309cbc08ed54ce0edfc083f1c421b6411f63b Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 6 Jul 2026 10:34:16 +0200 Subject: [PATCH 11/18] [NAE-2454] View Configuration 2.0 - fix memory leak in case-view-service.ts - add docs --- .../src/lib/header/models/meta-fields-factory.ts | 6 +++--- .../view/case-view/service/case-view-service.ts | 8 +++++--- .../default-components/model/factory-methods.ts | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts b/projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts index 77f90f137d..40851933b3 100644 --- a/projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts +++ b/projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts @@ -3,7 +3,7 @@ import {CaseMetaField} from "../case-header/case-menta-enum"; import {TaskMetaField} from "../task-header/task-meta-enum"; import {WorkflowMetaField} from "../workflow-header/workflow-meta-enum"; -// todo 2454 doc +/** Returns meta-headers for case view */ export function getCaseMetaHeaders(): HeaderColumn[] { return [ new HeaderColumn(HeaderColumnType.META, CaseMetaField.VISUAL_ID, 'headers.caseMeta.visualID', 'text'), @@ -16,7 +16,7 @@ export function getCaseMetaHeaders(): HeaderColumn[] { ]; } -// todo 2454 doc +/** Returns meta-headers for task view */ export function getTaskMetaHeaders(): HeaderColumn[] { return [ new HeaderColumn(HeaderColumnType.META, TaskMetaField.CASE, 'headers.taskMeta.case', 'text'), @@ -29,7 +29,7 @@ export function getTaskMetaHeaders(): HeaderColumn[] { ]; } -// todo 2454 doc +/** Returns meta-headers for workflow view */ export function getWorkflowMetaHeaders(): HeaderColumn[] { return [ new HeaderColumn(HeaderColumnType.META, WorkflowMetaField.INITIALS, 'headers.workflowMeta.initials', 'text'), diff --git a/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts b/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts index 6d34764899..682194b9e9 100644 --- a/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts +++ b/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts @@ -1,6 +1,6 @@ import {Inject, Injectable, OnDestroy, Optional} from '@angular/core'; import {CaseResourceService} from '../../../resources/engine-endpoint/case-resource.service'; -import {BehaviorSubject, Observable, of, Subject} from 'rxjs'; +import {BehaviorSubject, Observable, of, Subject, Subscription} from 'rxjs'; import {HttpParams} from '@angular/common/http'; import {Case} from '../../../resources/interface/case'; import {LoggerService} from '../../../logger/services/logger.service'; @@ -56,6 +56,8 @@ export class CaseViewService extends AbstractSortableViewComponent implements On protected _newCaseConfiguration: NewCaseConfiguration; protected _paginationView: boolean = false; + protected _activeFilterSub: Subscription; + constructor(protected _allowedNetsService: AllowedNetsService, protected _dialog: MatDialog, protected _caseResourceService: CaseResourceService, @@ -77,8 +79,7 @@ export class CaseViewService extends AbstractSortableViewComponent implements On Object.assign(this._newCaseConfiguration, newCaseConfig); } this._loading$ = new LoadingWithFilterEmitter(); - // todo 2454 sub leak - this._searchService.activeFilter$.subscribe(() => { + this._activeFilterSub = this._searchService.activeFilter$.subscribe(() => { this.reload(); }); this._endOfData = false; @@ -134,6 +135,7 @@ export class CaseViewService extends AbstractSortableViewComponent implements On super.ngOnDestroy(); this._loading$.complete(); this._nextPage$.complete(); + this._activeFilterSub?.unsubscribe(); } public get loading(): boolean { diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts index b4eae8f9e1..8fa161c1ab 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts @@ -47,12 +47,22 @@ export function filterCaseTabbedDataAllowedNetsServiceFactory(allowedNetsService return navigationItemTaskAllowedNetsServiceFactory(allowedNetsServiceFactory, baseAllowedNets, true, tabData.navigationItemTaskData); } -// todo 2454 doc +/** + * Builds {@link SortChangeDescription} based on dynamic menu item data for case view. This can be used for default + * sorting when the view is being initialized + * + * @return Observable of sort change description or undefined if the data is wrong or no data provided + */ export function buildDynamicSortChangeDescriptionForCase$(menuItemData: Array, processService: ProcessService): Observable | undefined { return buildDynamicSortChangeDescription$(menuItemData, processService, 'case'); } -// todo 2454 doc +/** + * Builds {@link SortChangeDescription} based on dynamic menu item data for task view. This can be used for default + * sorting when the view is being initialized + * + * @return Observable of sort change description or undefined if the data is wrong or no data provided + */ export function buildDynamicSortChangeDescriptionForTask$(menuItemData: Array, processService: ProcessService): Observable | undefined { return buildDynamicSortChangeDescription$(menuItemData, processService, 'task'); } From 64980619d355f68e5b1ee0f97e7cb06d35038844 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 6 Jul 2026 11:00:07 +0200 Subject: [PATCH 12/18] [NAE-2454] View Configuration 2.0 - fix tests --- .../abstract-navigation-double-drawer.spec.ts | 13 +++++++-- .../single-task-view-dialog.component.spec.ts | 2 +- .../task-view-dialog.component.spec.ts | 2 +- ...default-simple-case-view.component.spec.ts | 13 +++++++++ ...default-simple-task-view.component.spec.ts | 13 +++++++++ .../default-tab-view.component.spec.ts | 26 +++++++++++++++++ ...default-tabbed-case-view.component.spec.ts | 28 ++++++++++++++++++- ...default-tabbed-task-view.component.spec.ts | 17 +++++++++-- 8 files changed, 106 insertions(+), 8 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts index 9daddab6c6..81daea37f6 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts @@ -1,7 +1,7 @@ import {BreakpointObserver} from '@angular/cdk/layout'; import {CommonModule} from '@angular/common'; import {HttpClientTestingModule} from '@angular/common/http/testing'; -import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import {Component, CUSTOM_ELEMENTS_SCHEMA, Injector} from '@angular/core'; import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {FlexLayoutModule, FlexModule} from '@ngbracket/ngx-layout'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; @@ -40,6 +40,9 @@ import {CaseResourceService} from "../../resources/engine-endpoint/case-resource import {MockCaseResourceService} from "../../utility/tests/mocks/mock-case-resource.service"; import {DoubleDrawerNavigationService} from "./service/double-drawer-navigation.service"; import {RedirectService} from "../../routing/redirect-service/redirect.service"; +import {ProcessService} from "../../process/process.service"; +import {SnackBarService} from "../../snack-bar/services/snack-bar.service"; +import {MatDialog} from "@angular/material/dialog"; xdescribe('AbstractNavigationDoubleDrawerComponent', () => { let component: TestDrawerComponent; @@ -150,15 +153,19 @@ class TestDrawerComponent extends AbstractNavigationDoubleDrawerComponent { _log: LoggerService, _config: ConfigurationService, _uriService: UriService, + _processService: ProcessService, _caseResourceService: CaseResourceService, _impersonationUserSelect: ImpersonationUserSelectService, _impersonation: ImpersonationService, _dynamicRouteProviderService: DynamicNavigationRouteProviderService, _redirectService: RedirectService, + _snackBarService: SnackBarService, + _dialog: MatDialog, + _injector: Injector, _navigationService: DoubleDrawerNavigationService) { super(_router, _activatedRoute, _breakpoint, _languageService, _translateService, _userService, _accessService, - _log, _config, _uriService, _caseResourceService, _impersonationUserSelect, _impersonation, - _dynamicRouteProviderService, _redirectService, _navigationService); + _log, _config, _uriService, _processService, _caseResourceService, _impersonationUserSelect, _impersonation, + _dynamicRouteProviderService, _redirectService, _snackBarService, _dialog, _injector, _navigationService); } } diff --git a/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.spec.ts b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.spec.ts index 2406cad636..f84af1b356 100644 --- a/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.spec.ts +++ b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.spec.ts @@ -38,7 +38,7 @@ describe('SingleTaskViewDialogComponent', () => { MatDialogModule ], providers: [ - { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MAT_DIALOG_DATA, useValue: {searchBody: {}} }, { provide: MatDialogRef, useValue: {} }, { provide: AuthenticationMethodService, useClass: MockAuthenticationMethodService }, { provide: ConfigurationService, useClass: TestConfigurationService }, diff --git a/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.spec.ts b/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.spec.ts index 75bff111a9..cba0510b16 100644 --- a/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.spec.ts +++ b/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.spec.ts @@ -38,7 +38,7 @@ describe('TaskViewDialogComponent', () => { MatDialogModule ], providers: [ - { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MAT_DIALOG_DATA, useValue: { searchBody: {}} }, { provide: MatDialogRef, useValue: {} }, { provide: AuthenticationMethodService, useClass: MockAuthenticationMethodService }, { provide: ConfigurationService, useClass: TestConfigurationService }, diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-case-view/default-simple-case-view.component.spec.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-case-view/default-simple-case-view.component.spec.ts index 73a5bbbabb..cd566f4165 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-case-view/default-simple-case-view.component.spec.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-case-view/default-simple-case-view.component.spec.ts @@ -133,6 +133,19 @@ describe('SimpleCaseViewComponent', () => { true, {visible: true} ), + new TextField( + GroupNavigationConstants.ITEM_FIELD_CASE_HEADERS_SORT_MODE_ACTIVE, + '', + '', + {visible: true} + ), + new EnumerationField( + GroupNavigationConstants.ITEM_FIELD_CASE_HEADERS_SORT_MODE_DIRECTION, + '', + '', + [], + {visible: true} + ), new StringCollectionField( GroupNavigationConstants.ITEM_FIELD_CASE_ALLOWED_NETS, '', diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-task-view/default-simple-task-view.component.spec.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-task-view/default-simple-task-view.component.spec.ts index 5448433c7d..40ce0cfefb 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-task-view/default-simple-task-view.component.spec.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/simple-views/default-simple-task-view/default-simple-task-view.component.spec.ts @@ -94,6 +94,19 @@ describe('DefaultSimpleTaskViewComponent', () => { '', {visible: true} ), + new TextField( + GroupNavigationConstants.ITEM_FIELD_TASK_HEADERS_SORT_MODE_ACTIVE, + '', + '', + {visible: true} + ), + new EnumerationField( + GroupNavigationConstants.ITEM_FIELD_TASK_HEADERS_SORT_MODE_DIRECTION, + '', + '', + [], + {visible: true} + ), new StringCollectionField( GroupNavigationConstants.ITEM_FIELD_TASK_ALLOWED_NETS, '', diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tab-view/default-tab-view.component.spec.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tab-view/default-tab-view.component.spec.ts index 706313f3b5..f44ae08664 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tab-view/default-tab-view.component.spec.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tab-view/default-tab-view.component.spec.ts @@ -205,6 +205,32 @@ describe('DefaultTabViewComponent', () => { GroupNavigationConstants.ITEM_FIELD_ID_CASE_ALLOW_EXPORT, '',true,{visible: true} ), + new TextField( + GroupNavigationConstants.ITEM_FIELD_CASE_HEADERS_SORT_MODE_ACTIVE, + '', + '', + {visible: true} + ), + new EnumerationField( + GroupNavigationConstants.ITEM_FIELD_CASE_HEADERS_SORT_MODE_DIRECTION, + '', + '', + [], + {visible: true} + ), + new TextField( + GroupNavigationConstants.ITEM_FIELD_TASK_HEADERS_SORT_MODE_ACTIVE, + '', + '', + {visible: true} + ), + new EnumerationField( + GroupNavigationConstants.ITEM_FIELD_TASK_HEADERS_SORT_MODE_DIRECTION, + '', + '', + [], + {visible: true} + ), ] } ] diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.spec.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.spec.ts index 634dca0322..76039820b4 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.spec.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.spec.ts @@ -15,7 +15,7 @@ import { TextField, I18nField, AuthenticationModule, - StringCollectionField, + StringCollectionField, EnumerationField, } from '@netgrif/components-core'; import {of} from 'rxjs'; import {DefaultTabbedTaskViewComponent} from '../default-tabbed-task-view/default-tabbed-task-view.component'; @@ -91,6 +91,19 @@ describe('DefaultTabbedCaseViewComponent', () => { [], {visible: true} ), + new TextField( + GroupNavigationConstants.ITEM_FIELD_CASE_HEADERS_SORT_MODE_ACTIVE, + '', + '', + {visible: true} + ), + new EnumerationField( + GroupNavigationConstants.ITEM_FIELD_CASE_HEADERS_SORT_MODE_DIRECTION, + '', + '', + [], + {visible: true} + ), new StringCollectionField( GroupNavigationConstants.ITEM_FIELD_TASK_ALLOWED_NETS, '', @@ -136,6 +149,19 @@ describe('DefaultTabbedCaseViewComponent', () => { [], {visible: true} ), + new TextField( + GroupNavigationConstants.ITEM_FIELD_CASE_HEADERS_SORT_MODE_ACTIVE, + '', + '', + {visible: true} + ), + new EnumerationField( + GroupNavigationConstants.ITEM_FIELD_CASE_HEADERS_SORT_MODE_DIRECTION, + '', + '', + [], + {visible: true} + ), new StringCollectionField( GroupNavigationConstants.ITEM_FIELD_TASK_ALLOWED_NETS, '', diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-task-view/default-tabbed-task-view.component.spec.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-task-view/default-tabbed-task-view.component.spec.ts index 7de662e9e8..7c8f93240a 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-task-view/default-tabbed-task-view.component.spec.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-task-view/default-tabbed-task-view.component.spec.ts @@ -2,7 +2,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {DefaultTabbedTaskViewComponent} from './default-tabbed-task-view.component'; import {NavigationComponentModule} from '../../../../navigation.module'; import { - BooleanField, + BooleanField, EnumerationField, GroupNavigationConstants, HeaderMode, NAE_NAVIGATION_ITEM_TASK_DATA, @@ -69,7 +69,20 @@ describe('DefaultTabbedTaskViewComponent', () => { '', '', {visible: true} - ) + ), + new TextField( + GroupNavigationConstants.ITEM_FIELD_TASK_HEADERS_SORT_MODE_ACTIVE, + '', + '', + {visible: true} + ), + new EnumerationField( + GroupNavigationConstants.ITEM_FIELD_TASK_HEADERS_SORT_MODE_DIRECTION, + '', + '', + [], + {visible: true} + ), ] } ] From 616d027f81656541fd7fab757b98112515c96762 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 7 Jul 2026 10:37:54 +0200 Subject: [PATCH 13/18] [NAE-2454] View Configuration 2.0 - resolve PR comments --- .../models/task-view-dialog-injection-data.ts | 6 + .../src/lib/dialog/public-api.ts | 3 + .../case-header/case-header.service.spec.ts | 5 +- .../{case-menta-enum.ts => case-meta-enum.ts} | 0 .../abstract-edit-mode.component.spec.ts | 2 +- .../lib/header/models/meta-fields-factory.ts | 2 +- .../src/lib/header/public-api.ts | 2 +- .../abstract-navigation-double-drawer.ts | 60 +++--- .../double-drawer-navigation.service.ts | 178 +++++++++--------- .../abstract-case-panel.component.spec.ts | 2 +- .../abstract-case-panel.component.ts | 3 +- .../header-search.service.ts | 2 +- .../content-components/public-api.ts | 1 - .../model/task-view-injection-data.ts | 6 - .../case-view/service/case-view-service.ts | 34 ++-- .../task-view/service/task-view.service.ts | 41 ++-- .../src/lib/dialog/dialog.module.ts | 8 +- .../src/lib/dialog/dialog.theme.scss | 2 +- .../src/lib/dialog/model/dialog-actions.ts | 5 +- .../single-task-view-dialog.component.ts | 12 +- .../task-view-dialog.component.ts | 14 +- .../model/factory-methods.ts | 6 +- .../navigation-double-drawer.component.ts | 10 +- 23 files changed, 200 insertions(+), 204 deletions(-) create mode 100644 projects/netgrif-components-core/src/lib/dialog/models/task-view-dialog-injection-data.ts rename projects/netgrif-components-core/src/lib/header/case-header/{case-menta-enum.ts => case-meta-enum.ts} (100%) delete mode 100644 projects/netgrif-components-core/src/lib/side-menu/content-components/task-view/model/task-view-injection-data.ts diff --git a/projects/netgrif-components-core/src/lib/dialog/models/task-view-dialog-injection-data.ts b/projects/netgrif-components-core/src/lib/dialog/models/task-view-dialog-injection-data.ts new file mode 100644 index 0000000000..5b50847c74 --- /dev/null +++ b/projects/netgrif-components-core/src/lib/dialog/models/task-view-dialog-injection-data.ts @@ -0,0 +1,6 @@ +import {TaskSearchRequestBody} from "netgrif-components-core"; + +export interface TaskViewDialogInjectionData { + searchBody: TaskSearchRequestBody; + autoCloseOnEvent: boolean; +} diff --git a/projects/netgrif-components-core/src/lib/dialog/public-api.ts b/projects/netgrif-components-core/src/lib/dialog/public-api.ts index 27e88ba18c..20ebb4b374 100644 --- a/projects/netgrif-components-core/src/lib/dialog/public-api.ts +++ b/projects/netgrif-components-core/src/lib/dialog/public-api.ts @@ -12,3 +12,6 @@ export * from './dialog.module'; // SERVICES export * from './services/dialog.service'; export * from './injection-tokens'; + +// MODELS +export * from './models/task-view-dialog-injection-data'; diff --git a/projects/netgrif-components-core/src/lib/header/case-header/case-header.service.spec.ts b/projects/netgrif-components-core/src/lib/header/case-header/case-header.service.spec.ts index b8e33d4d18..d641b0ce87 100644 --- a/projects/netgrif-components-core/src/lib/header/case-header/case-header.service.spec.ts +++ b/projects/netgrif-components-core/src/lib/header/case-header/case-header.service.spec.ts @@ -19,10 +19,7 @@ import {MockUserResourceService} from '../../utility/tests/mocks/mock-user-resou import {AuthenticationMethodService} from '../../authentication/services/authentication-method.service'; import {ViewService} from '../../routing/view-service/view.service'; import {TestViewService} from '../../utility/tests/test-view-service'; -import {ErrorSnackBarComponent} from '../../snack-bar/components/error-snack-bar/error-snack-bar.component'; -import {SuccessSnackBarComponent} from '../../snack-bar/components/success-snack-bar/success-snack-bar.component'; -import {BrowserDynamicTestingModule} from '@angular/platform-browser-dynamic/testing'; -import {CaseMetaField} from './case-menta-enum'; +import {CaseMetaField} from './case-meta-enum'; import {MatSnackBarModule} from '@angular/material/snack-bar'; import {MatIconModule} from '@angular/material/icon'; import {HeaderChangeType} from '../models/user-changes/header-change-type'; diff --git a/projects/netgrif-components-core/src/lib/header/case-header/case-menta-enum.ts b/projects/netgrif-components-core/src/lib/header/case-header/case-meta-enum.ts similarity index 100% rename from projects/netgrif-components-core/src/lib/header/case-header/case-menta-enum.ts rename to projects/netgrif-components-core/src/lib/header/case-header/case-meta-enum.ts diff --git a/projects/netgrif-components-core/src/lib/header/header-modes/edit-mode/abstract-edit-mode.component.spec.ts b/projects/netgrif-components-core/src/lib/header/header-modes/edit-mode/abstract-edit-mode.component.spec.ts index 6c8f65b1eb..8c6b40dfde 100644 --- a/projects/netgrif-components-core/src/lib/header/header-modes/edit-mode/abstract-edit-mode.component.spec.ts +++ b/projects/netgrif-components-core/src/lib/header/header-modes/edit-mode/abstract-edit-mode.component.spec.ts @@ -7,7 +7,7 @@ import {AbstractEditModeComponent} from './abstract-edit-mode.component'; import {TranslateService} from '@ngx-translate/core'; import {CaseHeaderService} from '../../case-header/case-header.service'; import {HeaderColumn, HeaderColumnType} from '../../models/header-column'; -import {CaseMetaField} from '../../case-header/case-menta-enum'; +import {CaseMetaField} from '../../case-header/case-meta-enum'; import {ViewService} from '../../../routing/view-service/view.service'; import {ConfigurationService} from '../../../configuration/configuration.service'; import {MaterialModule} from '../../../material/material.module'; diff --git a/projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts b/projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts index 40851933b3..9da3c1f3a3 100644 --- a/projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts +++ b/projects/netgrif-components-core/src/lib/header/models/meta-fields-factory.ts @@ -1,5 +1,5 @@ import {HeaderColumn, HeaderColumnType} from "./header-column"; -import {CaseMetaField} from "../case-header/case-menta-enum"; +import {CaseMetaField} from "../case-header/case-meta-enum"; import {TaskMetaField} from "../task-header/task-meta-enum"; import {WorkflowMetaField} from "../workflow-header/workflow-meta-enum"; diff --git a/projects/netgrif-components-core/src/lib/header/public-api.ts b/projects/netgrif-components-core/src/lib/header/public-api.ts index 0f51e8c22a..ac0977708b 100644 --- a/projects/netgrif-components-core/src/lib/header/public-api.ts +++ b/projects/netgrif-components-core/src/lib/header/public-api.ts @@ -11,7 +11,7 @@ export * from './header-modes/search-mode/abstract-search-mode.component'; /* MODELS */ export * from './models/public-api'; -export * from './case-header/case-menta-enum'; +export * from './case-header/case-meta-enum'; export * from './task-header/task-meta-enum'; export * from './workflow-header/workflow-meta-enum'; diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts index 4407881e81..9440284241 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts @@ -1,5 +1,5 @@ import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout'; -import {Component, EventEmitter, Injector, Input, OnDestroy, OnInit, Output, TemplateRef} from '@angular/core'; +import {Component, EventEmitter, Inject, Injector, Input, OnDestroy, OnInit, Output, TemplateRef} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {TranslateService} from '@ngx-translate/core'; import {ResizeEvent} from 'angular-resizable-element'; @@ -43,7 +43,8 @@ import {CreateCaseEventOutcome} from "../../event/model/event-outcomes/case-outc import {SnackBarService} from "../../snack-bar/services/snack-bar.service"; import {MatDialog} from "@angular/material/dialog"; import {NAE_TASK_VIEW_COMPONENT} from "../../side-menu/content-components/injection-tokens"; -import {TaskViewInjectionData} from "../../side-menu/content-components/task-view/model/task-view-injection-data"; +import {ComponentType} from '@angular/cdk/portal'; +import {TaskViewDialogInjectionData} from "../../dialog/models/task-view-dialog-injection-data"; @Component({ selector: 'ncc-abstract-navigation-double-drawer', @@ -106,16 +107,17 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, protected _log: LoggerService, protected _config: ConfigurationService, protected _uriService: UriService, - protected _processService: ProcessService, protected _caseResourceService: CaseResourceService, protected _impersonationUserSelect: ImpersonationUserSelectService, protected _impersonation: ImpersonationService, protected _dynamicRoutingService: DynamicNavigationRouteProviderService, protected _redirectService: RedirectService, + protected _navigationService: DoubleDrawerNavigationService, + protected _processService: ProcessService, protected _snackBarService: SnackBarService, protected _dialog: MatDialog, protected _injector: Injector, - protected _navigationService: DoubleDrawerNavigationService) { + @Inject(NAE_TASK_VIEW_COMPONENT) protected _taskView: ComponentType) { let configUrl: string = this._config.getServicesConfiguration()?.doubleDrawer?.url; if (configUrl !== undefined && !configUrl.startsWith('/')) { configUrl = '/' + configUrl; @@ -353,8 +355,7 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, } public editMenuItem(menuItemCase: Case) { - const taskView = this._injector.get(NAE_TASK_VIEW_COMPONENT); - this._dialog.open(taskView, { + this._dialog.open(this._taskView, { panelClass: "dialog-task-responsive", data: { autoCloseOnEvent: false, @@ -362,7 +363,7 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, transitionId: ["item_settings", "move_item", "duplicate_item", "children_order"], case: { id: menuItemCase.stringId } } - } as TaskViewInjectionData, + } as TaskViewDialogInjectionData, }); } @@ -376,28 +377,29 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, }); }), take(1) - ).subscribe(outcome => { - if (outcome.error) { - this._log.error(`Could not create menu item case`, outcome.error); - this._snackBarService.openErrorSnackBar(this._translateService.instant('dynamicNavigation.snackbar.failedItemCreation')) - return; - } - const _case = (outcome.outcome as CreateCaseEventOutcome).aCase; - const taskView = this._injector.get(NAE_TASK_VIEW_COMPONENT); - this._dialog.open(taskView, { - panelClass: "dialog-task-responsive", - data: { - autoCloseOnEvent: true, - searchBody: { - transitionId: "initialize", - case: { id: _case.stringId } - } - } as TaskViewInjectionData, - }); - }, error => { - this._log.error(`Could not create menu item case`, error); - this._snackBarService.openErrorSnackBar(this._translateService.instant('dynamicNavigation.snackbar.failedItemCreation')); - }); + ).subscribe({ + next: outcome => { + if (outcome.error) { + this._log.error(`Could not create menu item case`, outcome.error); + this._snackBarService.openErrorSnackBar(this._translateService.instant('dynamicNavigation.snackbar.failedItemCreation')) + return; + } + const _case = (outcome.outcome as CreateCaseEventOutcome).aCase; + this._dialog.open(this._taskView, { + panelClass: "dialog-task-responsive", + data: { + autoCloseOnEvent: true, + searchBody: { + transitionId: "initialize", + case: { id: _case.stringId } + } + } as TaskViewDialogInjectionData, + }); + }, + error: error => { + this._log.error(`Could not create menu item case`, error); + this._snackBarService.openErrorSnackBar(this._translateService.instant('dynamicNavigation.snackbar.failedItemCreation')); + }}); } public canEditMenu(): boolean { diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts index d65d43aea0..a8f5a6180d 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/service/double-drawer-navigation.service.ts @@ -67,7 +67,6 @@ export class DoubleDrawerNavigationService implements OnDestroy { protected _rightLoading$: LoadingEmitter; protected _nodeLoading$: LoadingEmitter; protected _currentNodeSubscription: Subscription; - protected _userSubscription: Subscription; /** * Currently display uri * Siblings of the node are on the left, children are on the right @@ -109,7 +108,7 @@ export class DoubleDrawerNavigationService implements OnDestroy { this.itemLoaded = new EventEmitter(); this._currentNodeSubscription = this._userService.user$.pipe( - filter(user => !!user && user.id !== ''), + filter(user => !this._userService.isUserEmpty(user)), switchMap(() => this._uriService.activeNode$) ).subscribe(node => { this.currentNode = node; @@ -118,7 +117,6 @@ export class DoubleDrawerNavigationService implements OnDestroy { public ngOnDestroy(): void { this._currentNodeSubscription?.unsubscribe(); - this._userSubscription?.unsubscribe(); this._leftLoading$.complete(); this._rightLoading$.complete(); this._nodeLoading$.complete(); @@ -147,13 +145,15 @@ export class DoubleDrawerNavigationService implements OnDestroy { node.parent = this._uriService.root; } else { this._nodeLoading$.on(); - this._uriService.getNodeByPath(this._uriService.resolveParentPath(node)).pipe(take(1)).subscribe(n => { - node.parent = !n ? this._uriService.root : n; - this._nodeLoading$.off(); - }, error => { - this._log.error(error); - this._nodeLoading$.off(); - }); + this._uriService.getNodeByPath(this._uriService.resolveParentPath(node)).pipe(take(1)).subscribe({ + next: n => { + node.parent = !n ? this._uriService.root : n; + this._nodeLoading$.off(); + }, + error: error => { + this._log.error(error); + this._nodeLoading$.off(); + }}); } } if (this._nodeLoading$.isActive) { @@ -276,18 +276,20 @@ export class DoubleDrawerNavigationService implements OnDestroy { } else { const path = item.resource.immediateData.find(f => f.stringId === GroupNavigationConstants.ITEM_FIELD_ID_NODE_PATH)?.value; if (DoubleDrawerUtils.isFolder(item) && !this._leftLoading$.isActive && !this._rightLoading$.isActive) { - this._uriService.getNodeByPath(path).pipe(take(1)).subscribe(node => { - this._uriService.activeNode = node; - this.itemClicked.emit({uriNode: this._uriService.activeNode, isHome: false}); - this._rightLoading$.pipe( - filter(isRightLoading => isRightLoading === false), - take(1) - ).subscribe(()=> { - this.openAvailableView(); - }) - }, error => { - this._log.error(error); - }); + this._uriService.getNodeByPath(path).pipe(take(1)).subscribe({ + next: node => { + this._uriService.activeNode = node; + this.itemClicked.emit({uriNode: this._uriService.activeNode, isHome: false}); + this._rightLoading$.pipe( + filter(isRightLoading => isRightLoading === false), + take(1) + ).subscribe(()=> { + this.openAvailableView(); + }) + }, + error: error => { + this._log.error(error); + }}); } else if (!path.includes(this.currentNode.uriPath)) { this._uriService.activeNode = this._currentNode.parent; this.itemClicked.emit({uriNode: this._uriService.activeNode, isHome: false}); @@ -341,7 +343,7 @@ export class DoubleDrawerNavigationService implements OnDestroy { public initializeCustomViewsOfView(view: View, viewConfigPath: string): void { if (!view || this.customItemsInitialized || this.hiddenCustomItemsInitialized) return; - this._userSubscription = this._userService.user$.pipe(filter(user => !!user && user.id !== '')).subscribe(user => { + this._userService.user$.pipe(filter(user => !this._userService.isUserEmpty(user)), take(1)).subscribe(user => { Object.entries(view.children).forEach(([key, childView]) => { const childViewConfigPath: string = viewConfigPath + '/' + key; this.resolveUriForChildViews(childViewConfigPath, childView); @@ -380,85 +382,91 @@ export class DoubleDrawerNavigationService implements OnDestroy { return; } this._leftLoading$.on(); - this._uriService.getItemCaseByNodePath(this.currentNode.parent).pipe(take(1)).subscribe(page => { - let childCases$; - let targetItem; - let orderedChildCaseIds; - - if (page?.pagination?.totalElements === 0) { - childCases$ = of([]); - } else { - targetItem = page.content[0]; - orderedChildCaseIds = DoubleDrawerUtils.extractChildCaseIds(targetItem); - childCases$ = this.getItemCasesByIdsInOnePage(orderedChildCaseIds).pipe( - map(p => p.content), - ); - } + this._uriService.getItemCaseByNodePath(this.currentNode.parent).pipe(take(1)).subscribe({ + next: page => { + let childCases$; + let targetItem; + let orderedChildCaseIds; + + if (page?.pagination?.totalElements === 0) { + childCases$ = of([]); + } else { + targetItem = page.content[0]; + orderedChildCaseIds = DoubleDrawerUtils.extractChildCaseIds(targetItem); + childCases$ = this.getItemCasesByIdsInOnePage(orderedChildCaseIds).pipe( + map(p => p.content), + ); + } - childCases$.pipe(take(1)).subscribe(result => { - result = result.map(folder => this.resolveItemCaseToNavigationItem(folder)).filter(i => !!i); - this._leftItems$.next(result.sort((a, b) => orderedChildCaseIds.indexOf(a.resource.stringId) - orderedChildCaseIds.indexOf(b.resource.stringId))); - this.resolveCustomViewsInLeftSide(); - this._leftLoading$.off(); - this.itemLoaded.emit({menu: 'left', items: this.leftItems}); - }, error => { + childCases$.pipe(take(1)).subscribe({ + next: result => { + result = result.map(folder => this.resolveItemCaseToNavigationItem(folder)).filter(i => !!i); + this._leftItems$.next(result.sort((a, b) => orderedChildCaseIds.indexOf(a.resource.stringId) - orderedChildCaseIds.indexOf(b.resource.stringId))); + this.resolveCustomViewsInLeftSide(); + this._leftLoading$.off(); + this.itemLoaded.emit({menu: 'left', items: this.leftItems}); + }, + error: error => { + this._log.error(error); + this._leftItems$.next([]) + this.resolveCustomViewsInLeftSide(); + this._leftLoading$.off(); + }}); + }, + error: error => { this._log.error(error); this._leftItems$.next([]) this.resolveCustomViewsInLeftSide(); this._leftLoading$.off(); - }); - }, error => { - this._log.error(error); - this._leftItems$.next([]) - this.resolveCustomViewsInLeftSide(); - this._leftLoading$.off(); - }); + }}); } protected loadRightSide() { this._rightLoading$.on(); this._moreItems$.next([]) - this._uriService.getItemCaseByNodePath(this.currentNode).pipe(take(1)).subscribe(page => { - let childCases$; - let targetItem; - let orderedChildCaseIds; - - if (page?.pagination?.totalElements === 0) { - childCases$ = of([]); - } else { - targetItem = page.content[0]; - orderedChildCaseIds = DoubleDrawerUtils.extractChildCaseIds(targetItem); - childCases$ = this.getItemCasesByIdsInOnePage(orderedChildCaseIds).pipe( - map(p => p.content), - ); - } - - childCases$.pipe(take(1)).subscribe(result => { - result = (result as Case[]).sort((a, b) => orderedChildCaseIds.indexOf(a.stringId) - orderedChildCaseIds.indexOf(b.stringId)); - if (result.length > RIGHT_SIDE_INIT_PAGE_SIZE) { - const rawRightItems: Case[] = result.splice(0, RIGHT_SIDE_INIT_PAGE_SIZE); - this._rightItems$.next(rawRightItems.map(folder => this.resolveItemCaseToNavigationItem(folder)).filter(i => !!i)); - this._moreItems$.next(result.map(folder => this.resolveItemCaseToNavigationItem(folder)).filter(i => !!i)); + this._uriService.getItemCaseByNodePath(this.currentNode).pipe(take(1)).subscribe({ + next: page => { + let childCases$; + let targetItem; + let orderedChildCaseIds; + + if (page?.pagination?.totalElements === 0) { + childCases$ = of([]); } else { - this._rightItems$.next(result.map(folder => this.resolveItemCaseToNavigationItem(folder)).filter(i => !!i)); + targetItem = page.content[0]; + orderedChildCaseIds = DoubleDrawerUtils.extractChildCaseIds(targetItem); + childCases$ = this.getItemCasesByIdsInOnePage(orderedChildCaseIds).pipe( + map(p => p.content), + ); } - this.resolveCustomViewsInRightSide(); - this._rightLoading$.off(); - this.itemLoaded.emit({menu: 'right', items: this.rightItems}); - }, error => { + + childCases$.pipe(take(1)).subscribe(result => { + result = (result as Case[]).sort((a, b) => orderedChildCaseIds.indexOf(a.stringId) - orderedChildCaseIds.indexOf(b.stringId)); + if (result.length > RIGHT_SIDE_INIT_PAGE_SIZE) { + const rawRightItems: Case[] = result.splice(0, RIGHT_SIDE_INIT_PAGE_SIZE); + this._rightItems$.next(rawRightItems.map(folder => this.resolveItemCaseToNavigationItem(folder)).filter(i => !!i)); + this._moreItems$.next(result.map(folder => this.resolveItemCaseToNavigationItem(folder)).filter(i => !!i)); + } else { + this._rightItems$.next(result.map(folder => this.resolveItemCaseToNavigationItem(folder)).filter(i => !!i)); + } + this.resolveCustomViewsInRightSide(); + this._rightLoading$.off(); + this.itemLoaded.emit({menu: 'right', items: this.rightItems}); + }, error => { + this._log.error(error); + this._rightItems$.next([]); + this._moreItems$.next([]); + this.resolveCustomViewsInRightSide(); + this._rightLoading$.off(); + }); + }, + error: error => { this._log.error(error); this._rightItems$.next([]); this._moreItems$.next([]); this.resolveCustomViewsInRightSide(); this._rightLoading$.off(); - }); - }, error => { - this._log.error(error); - this._rightItems$.next([]); - this._moreItems$.next([]); - this.resolveCustomViewsInRightSide(); - this._rightLoading$.off(); - }); + }}); } protected getItemCasesByIdsInOnePage(caseIds: string[]): Observable> { diff --git a/projects/netgrif-components-core/src/lib/panel/case-panel/abstract-case-panel.component.spec.ts b/projects/netgrif-components-core/src/lib/panel/case-panel/abstract-case-panel.component.spec.ts index f114b5b7bc..0567c2a38a 100644 --- a/projects/netgrif-components-core/src/lib/panel/case-panel/abstract-case-panel.component.spec.ts +++ b/projects/netgrif-components-core/src/lib/panel/case-panel/abstract-case-panel.component.spec.ts @@ -7,7 +7,7 @@ import {Component, NO_ERRORS_SCHEMA, Optional} from '@angular/core'; import {of} from 'rxjs'; import {AbstractCasePanelComponent} from './abstract-case-panel.component'; import {HeaderColumn, HeaderColumnType} from '../../header/models/header-column'; -import {CaseMetaField} from '../../header/case-header/case-menta-enum'; +import {CaseMetaField} from '../../header/case-header/case-meta-enum'; import {TranslateLibModule} from '../../translate/translate-lib.module'; import {MaterialModule} from '../../material/material.module'; import {CaseViewService} from '../../view/case-view/service/case-view-service'; diff --git a/projects/netgrif-components-core/src/lib/panel/case-panel/abstract-case-panel.component.ts b/projects/netgrif-components-core/src/lib/panel/case-panel/abstract-case-panel.component.ts index 13345e9306..4d985db325 100644 --- a/projects/netgrif-components-core/src/lib/panel/case-panel/abstract-case-panel.component.ts +++ b/projects/netgrif-components-core/src/lib/panel/case-panel/abstract-case-panel.component.ts @@ -1,10 +1,9 @@ import {Component, Input, Optional} from '@angular/core'; -import {Observable} from 'rxjs'; import {Case} from '../../resources/interface/case'; import {toMoment} from '../../resources/types/nae-date-type'; import {HeaderColumn} from '../../header/models/header-column'; import {DATE_TIME_FORMAT_STRING} from '../../moment/time-formats'; -import {CaseMetaField} from '../../header/case-header/case-menta-enum'; +import {CaseMetaField} from '../../header/case-header/case-meta-enum'; import {CaseResourceService} from '../../resources/engine-endpoint/case-resource.service'; import {CaseViewService} from '../../view/case-view/service/case-view-service'; import {SnackBarService} from '../../snack-bar/services/snack-bar.service'; diff --git a/projects/netgrif-components-core/src/lib/search/header-search-service/header-search.service.ts b/projects/netgrif-components-core/src/lib/search/header-search-service/header-search.service.ts index fe97d37983..4069bdeb3c 100644 --- a/projects/netgrif-components-core/src/lib/search/header-search-service/header-search.service.ts +++ b/projects/netgrif-components-core/src/lib/search/header-search-service/header-search.service.ts @@ -8,7 +8,7 @@ import {HeaderMode} from '../../header/models/header-mode'; import {ModeChangeDescription} from '../../header/models/user-changes/mode-change-description'; import {SearchChangeDescription} from '../../header/models/user-changes/search-change-description'; import {HeaderColumnType} from '../../header/models/header-column'; -import {CaseMetaField} from '../../header/case-header/case-menta-enum'; +import {CaseMetaField} from '../../header/case-header/case-meta-enum'; import {CategoryFactory} from '../category-factory/category-factory'; import {CaseVisualId} from '../models/category/case/case-visual-id'; import {Category} from '../models/category/category'; diff --git a/projects/netgrif-components-core/src/lib/side-menu/content-components/public-api.ts b/projects/netgrif-components-core/src/lib/side-menu/content-components/public-api.ts index 73de26daf2..3ac9d78696 100644 --- a/projects/netgrif-components-core/src/lib/side-menu/content-components/public-api.ts +++ b/projects/netgrif-components-core/src/lib/side-menu/content-components/public-api.ts @@ -20,4 +20,3 @@ export * from './user-assign/model/user-list-injected-data'; export * from './injection-tokens'; export * from './new-case/model/new-case-injection-data'; export * from './user-impersonate/model/user-impersonate-injection-data'; -export * from './task-view/model/task-view-injection-data'; diff --git a/projects/netgrif-components-core/src/lib/side-menu/content-components/task-view/model/task-view-injection-data.ts b/projects/netgrif-components-core/src/lib/side-menu/content-components/task-view/model/task-view-injection-data.ts deleted file mode 100644 index 186bbb65b9..0000000000 --- a/projects/netgrif-components-core/src/lib/side-menu/content-components/task-view/model/task-view-injection-data.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {SideMenuInjectionData} from "../../../models/side-menu-injection-data"; -import {TaskSearchRequestBody} from "../../../../filter/models/task-search-request-body"; - -export interface TaskViewInjectionData extends SideMenuInjectionData { - searchBody: TaskSearchRequestBody -} diff --git a/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts b/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts index 682194b9e9..cf7331d507 100644 --- a/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts +++ b/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts @@ -9,7 +9,7 @@ import {SearchService} from '../../../search/search-service/search.service'; import {TranslateService} from '@ngx-translate/core'; import {catchError, concatMap, filter, map, mergeMap, scan, switchMap, tap} from 'rxjs/operators'; import {Pagination} from '../../../resources/interface/pagination'; -import {CaseMetaField} from '../../../header/case-header/case-menta-enum'; +import {CaseMetaField} from '../../../header/case-header/case-meta-enum'; import {PageLoadRequestContext} from '../../abstract/page-load-request-context'; import {Filter} from '../../../filter/models/filter'; import {ListRange} from '@angular/cdk/collections'; @@ -89,7 +89,8 @@ export class CaseViewService extends AbstractSortableViewComponent implements On totalPages: undefined, number: -1 }; - this._nextPage$ = new BehaviorSubject(null); + + this.requestPageWithDynamicSort(new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0}))); const casesMap = this._nextPage$.pipe( mergeMap(p => this.loadPage(p)), @@ -120,15 +121,6 @@ export class CaseViewService extends AbstractSortableViewComponent implements On map(v => Object.values(v) as Array), tap(cases => this._cases = cases as Array), ); - - if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { - this._dynamicDefaultSort$.subscribe(sortChangeDesc => { - this._lastHeaderSearchState = sortChangeDesc; - this._nextPage$.next(new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0}))); - }); - } else { - this._nextPage$.next(new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0}))); - } } ngOnDestroy(): void { @@ -233,14 +225,7 @@ export class CaseViewService extends AbstractSortableViewComponent implements On if (this.isLoadingRelevantFilter(requestContext) || this._endOfData) { return; } - if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { - this._dynamicDefaultSort$.subscribe(sortChangeDesc => { - this._lastHeaderSearchState = sortChangeDesc; - this._nextPage$.next(requestContext); - }); - } else { - this._nextPage$.next(requestContext); - } + this.requestPageWithDynamicSort(requestContext); } private isLoadingRelevantFilter(requestContext?: PageLoadRequestContext): boolean { @@ -384,4 +369,15 @@ export class CaseViewService extends AbstractSortableViewComponent implements On public viewEnabled(aCase: Case): boolean { return this._permissionService.hasCasePermission(aCase, PermissionType.VIEW); } + + protected requestPageWithDynamicSort(requestContext: PageLoadRequestContext) { + if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { + this._dynamicDefaultSort$.subscribe(sortChangeDesc => { + this._lastHeaderSearchState = sortChangeDesc; + this._nextPage$.next(requestContext); + }); + } else { + this._nextPage$.next(requestContext); + } + } } diff --git a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts index caaa7632d4..057cf094eb 100644 --- a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts +++ b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts @@ -77,7 +77,6 @@ export class TaskViewService extends AbstractSortableViewComponent implements On totalPages: undefined, number: -1 }; - this._requestedPage$ = new BehaviorSubject(null); this._panelUpdate$ = new BehaviorSubject>([]); this._closeTab$ = new ReplaySubject(1); this._preferredEndpoint = taskViewConfig?.preferredEndpoint ?? (this._preferredEndpoint ?? TaskEndpoint.MONGO); @@ -88,6 +87,8 @@ export class TaskViewService extends AbstractSortableViewComponent implements On this.reload(); }); + this.requestPageWithDynamicSort(new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0}))); + const tasksMap$ = this._requestedPage$.pipe( mergeMap(p => this.loadPage(p)), map(pageLoadResult => { @@ -150,15 +151,6 @@ export class TaskViewService extends AbstractSortableViewComponent implements On this._subCloseTask = (taskViewConfig?.closeTaskTabOnNoTasks ?? of(true)).subscribe(bool => { this._closeTaskTabOnNoTasks = bool; }); - - if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { - this._dynamicDefaultSort$.subscribe(sortChangeDesc => { - this._lastHeaderSearchState = sortChangeDesc; - this._requestedPage$.next(new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0}))); - }); - } else { - this._requestedPage$.next(new PageLoadRequestContext(this.activeFilter, Object.assign({}, this._pagination, {number: 0}))); - } } ngOnDestroy(): void { @@ -307,14 +299,7 @@ export class TaskViewService extends AbstractSortableViewComponent implements On } if (renderedRange.end === totalLoaded) { - if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { - this._dynamicDefaultSort$.subscribe(sortChangeDesc => { - this._lastHeaderSearchState = sortChangeDesc; - this._requestedPage$.next(requestContext); - }); - } else { - this._requestedPage$.next(requestContext); - } + this.requestPageWithDynamicSort(requestContext); } } @@ -328,14 +313,7 @@ export class TaskViewService extends AbstractSortableViewComponent implements On if (this.isLoadingRelevantFilter(requestContext) || this._endOfData) { return; } - if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { - this._dynamicDefaultSort$.subscribe(sortChangeDesc => { - this._lastHeaderSearchState = sortChangeDesc; - this._requestedPage$.next(requestContext); - }); - } else { - this._requestedPage$.next(requestContext); - } + this.requestPageWithDynamicSort(requestContext); } private isLoadingRelevantFilter(requestContext?: PageLoadRequestContext): boolean { @@ -387,4 +365,15 @@ export class TaskViewService extends AbstractSortableViewComponent implements On params = params.set(PaginationParams.PAGE_NUMBER, `${pagination.number}`); return params; } + + protected requestPageWithDynamicSort(requestContext: PageLoadRequestContext) { + if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { + this._dynamicDefaultSort$.subscribe(sortChangeDesc => { + this._lastHeaderSearchState = sortChangeDesc; + this._requestedPage$.next(requestContext); + }); + } else { + this._requestedPage$.next(requestContext); + } + } } diff --git a/projects/netgrif-components/src/lib/dialog/dialog.module.ts b/projects/netgrif-components/src/lib/dialog/dialog.module.ts index 8bf605eba2..908d1bfcc4 100644 --- a/projects/netgrif-components/src/lib/dialog/dialog.module.ts +++ b/projects/netgrif-components/src/lib/dialog/dialog.module.ts @@ -11,7 +11,11 @@ import { NAE_NEW_CASE_DIALOG_COMPONENT, NAE_USER_IMPERSONATE_DIALOG_COMPONENT, FrontActionRegistryService, - NAE_TASK_VIEW_COMPONENT, UtilityModule, NAE_MULTI_USER_ASSIGN_DIALOG_COMPONENT, NAE_IMPORT_NET_DIALOG_COMPONENT, NAE_SINGLE_TASK_VIEW_COMPONENT + NAE_TASK_VIEW_COMPONENT, + UtilityModule, + NAE_MULTI_USER_ASSIGN_DIALOG_COMPONENT, + NAE_IMPORT_NET_DIALOG_COMPONENT, + NAE_SINGLE_TASK_VIEW_COMPONENT } from '@netgrif/components-core'; import {A11yModule} from '@angular/cdk/a11y'; import { @@ -42,7 +46,6 @@ import { MultiUserAssignDialogComponent } from './multi-user-assign-dialog/multi import { SingleTaskViewDialogComponent } from './single-task-view-dialog/single-task-view-dialog.component'; import { TaskViewDialogComponent } from './task-view-dialog/task-view-dialog.component'; import {openTaskDialog} from "./model/dialog-actions"; -import {TranslateModule} from "@ngx-translate/core"; @NgModule({ declarations: [ @@ -89,7 +92,6 @@ import {TranslateModule} from "@ngx-translate/core"; PanelComponentModule, HotkeyModule.forRoot(), UtilityModule, - TranslateModule ], providers: [ {provide: NAE_USER_ASSIGN_DIALOG_COMPONENT, useValue: UserAssignDialogComponent}, diff --git a/projects/netgrif-components/src/lib/dialog/dialog.theme.scss b/projects/netgrif-components/src/lib/dialog/dialog.theme.scss index 9e6f0edd85..2696b13bcd 100644 --- a/projects/netgrif-components/src/lib/dialog/dialog.theme.scss +++ b/projects/netgrif-components/src/lib/dialog/dialog.theme.scss @@ -24,7 +24,7 @@ max-height: 95vh !important; min-height: 300px !important; min-width: 300px !important; - mat-mdc-dialog-container, + .mat-mdc-dialog-container, .mdc-dialog__surface { overflow-y: auto !important; } diff --git a/projects/netgrif-components/src/lib/dialog/model/dialog-actions.ts b/projects/netgrif-components/src/lib/dialog/model/dialog-actions.ts index 3a5b585f54..41b6004308 100644 --- a/projects/netgrif-components/src/lib/dialog/model/dialog-actions.ts +++ b/projects/netgrif-components/src/lib/dialog/model/dialog-actions.ts @@ -3,7 +3,8 @@ import { FrontActionDefinition, FrontAction, NAE_TASK_VIEW_COMPONENT, - TaskViewInjectionData, reloadTaskAction + TaskViewDialogInjectionData, + reloadTaskAction } from '@netgrif/components-core'; import {MatDialog} from "@angular/material/dialog"; @@ -15,7 +16,7 @@ export const openTaskDialog: FrontActionDefinition = { panelClass: "dialog-task-responsive", data: { searchBody: { stringId: frontAction.args[0] } - } as TaskViewInjectionData, + } as TaskViewDialogInjectionData, }); ref.afterClosed().subscribe(event => { reloadTaskAction.call(injector, frontAction) diff --git a/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.ts b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.ts index 3e6fac6c26..e37f1d4573 100644 --- a/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.ts +++ b/projects/netgrif-components/src/lib/dialog/single-task-view-dialog/single-task-view-dialog.component.ts @@ -9,7 +9,7 @@ import { SimpleFilter, TaskEventNotification, TaskViewService, - TaskViewInjectionData, + TaskViewDialogInjectionData, AbstractSingleTaskViewComponent, TaskEvent } from '@netgrif/components-core'; @@ -20,11 +20,11 @@ import { localAllowedNetsFactory } from "../../filter-field-content/filter-field-tabbed-case-view/filter-field-tabbed-case-view.component"; -export function singleTaskViewFilterFactory(dialogControl: TaskViewInjectionData): BaseFilter { +export function singleTaskViewFilterFactory(dialogControl: TaskViewDialogInjectionData): BaseFilter { if (!dialogControl) { throw new Error('NewFilterCaseId was not provided in the side menu injection data'); } - const injectedData = dialogControl as TaskViewInjectionData; + const injectedData = dialogControl as TaskViewDialogInjectionData; return { filter: SimpleFilter.fromTaskQuery(injectedData.searchBody) @@ -53,16 +53,16 @@ export function singleTaskViewFilterFactory(dialogControl: TaskViewInjectionData export class SingleTaskViewDialogComponent extends AbstractSingleTaskViewComponent implements AfterViewInit { @ViewChild('header') public taskHeaderComponent: HeaderComponent; - protected _injectedData: TaskViewInjectionData; + protected _injectedData: TaskViewDialogInjectionData; constructor(protected _dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) protected _data: TaskViewInjectionData, + @Inject(MAT_DIALOG_DATA) protected _data: TaskViewDialogInjectionData, protected _log: LoggerService, taskViewService: TaskViewService, _activatedRoute?: ActivatedRoute) { super(taskViewService, _activatedRoute); if (this._data) { - this._injectedData = this._data as TaskViewInjectionData; + this._injectedData = this._data as TaskViewDialogInjectionData; } } diff --git a/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.ts b/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.ts index a594aa16f3..4b236bd147 100644 --- a/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.ts +++ b/projects/netgrif-components/src/lib/dialog/task-view-dialog/task-view-dialog.component.ts @@ -8,7 +8,7 @@ import { SearchService, SimpleFilter, TaskViewService, - TaskViewInjectionData, + TaskViewDialogInjectionData, TaskEventNotification, AbstractTaskViewComponent, TaskEvent, } from '@netgrif/components-core'; @@ -19,11 +19,11 @@ import { localAllowedNetsFactory } from "../../filter-field-content/filter-field-tabbed-case-view/filter-field-tabbed-case-view.component"; -export function taskViewFilterFactory(dialogControl: TaskViewInjectionData): BaseFilter { +export function taskViewFilterFactory(dialogControl: TaskViewDialogInjectionData): BaseFilter { if (!dialogControl) { throw new Error('NewFilterCaseId was not provided in the side menu injection data'); } - const injectedData = dialogControl as TaskViewInjectionData; + const injectedData = dialogControl as TaskViewDialogInjectionData; return { filter: SimpleFilter.fromTaskQuery(injectedData.searchBody) @@ -52,16 +52,16 @@ export function taskViewFilterFactory(dialogControl: TaskViewInjectionData): Bas export class TaskViewDialogComponent extends AbstractTaskViewComponent implements AfterViewInit { @ViewChild('header') public taskHeaderComponent: HeaderComponent; - protected _injectedData: TaskViewInjectionData; + protected _injectedData: TaskViewDialogInjectionData; constructor(protected _dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) protected _data: TaskViewInjectionData, + @Inject(MAT_DIALOG_DATA) protected _data: TaskViewDialogInjectionData, protected _log: LoggerService, taskViewService: TaskViewService, _activatedRoute?: ActivatedRoute) { super(taskViewService, _activatedRoute); if (this._data) { - this._injectedData = this._data as TaskViewInjectionData; + this._injectedData = this._data as TaskViewDialogInjectionData; } } @@ -70,7 +70,7 @@ export class TaskViewDialogComponent extends AbstractTaskViewComponent implement } public processTaskEvents(notification: TaskEventNotification): void { - if (!notification.success || !this._injectedData['autoCloseOnEvent']) { + if (!notification.success || !this._injectedData.autoCloseOnEvent) { return; } diff --git a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts index 8fa161c1ab..f22a7aaa02 100644 --- a/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts +++ b/projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts @@ -100,7 +100,6 @@ function buildDynamicSortChangeDescription$(menuItemData: Array, proc if (!!processIdentifier) { return processService.getNet(processIdentifier).pipe( - take(1), map(net => { let fieldType: string = net.immediateData.find(data => data.stringId === colIdentifier)?.type return { @@ -110,7 +109,8 @@ function buildDynamicSortChangeDescription$(menuItemData: Array, proc columnIdentifier: -1, fieldType: !!fieldType ? fieldType : 'text' } - }) + }), + take(1) ) } else { return of({ @@ -119,7 +119,7 @@ function buildDynamicSortChangeDescription$(menuItemData: Array, proc sortDirection: direction, columnIdentifier: -1, fieldType: determineMetaFieldType(viewType, colIdentifier) - }); + }).pipe(take(1)); } } diff --git a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts index 446d06a61f..026e02c4ee 100644 --- a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts +++ b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts @@ -70,19 +70,19 @@ export class NavigationDoubleDrawerComponent extends AbstractNavigationDoubleDra _log: LoggerService, _config: ConfigurationService, _uriService: UriService, - _processService: ProcessService, _caseResourceService: CaseResourceService, _impersonationUserSelect: ImpersonationUserSelectService, _impersonation: ImpersonationService, _dynamicRouteProviderService: DynamicNavigationRouteProviderService, _redirectService: RedirectService, + _navigationService: DoubleDrawerNavigationService, + _processService: ProcessService, _snackBarService: SnackBarService, _dialog: MatDialog, - _injector: Injector, - _navigationService: DoubleDrawerNavigationService) { + _injector: Injector) { super(_router, _activatedRoute, _breakpoint, _languageService, _translateService, _userService, _accessService, - _log, _config, _uriService, _processService, _caseResourceService, _impersonationUserSelect, _impersonation, - _dynamicRouteProviderService, _redirectService, _snackBarService, _dialog, _injector, _navigationService) + _log, _config, _uriService, _caseResourceService, _impersonationUserSelect, _impersonation, + _dynamicRouteProviderService, _redirectService, _navigationService, _processService, _snackBarService, _dialog, _injector); } public toggleSection(section: string): void { From a2b711408afd3d7e5d502696dd33df7c123a3656 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 7 Jul 2026 11:26:35 +0200 Subject: [PATCH 14/18] [NAE-2454] View Configuration 2.0 - resolve PR comments --- projects/netgrif-components/src/lib/dialog/dialog.module.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/projects/netgrif-components/src/lib/dialog/dialog.module.ts b/projects/netgrif-components/src/lib/dialog/dialog.module.ts index 908d1bfcc4..089b54d5a3 100644 --- a/projects/netgrif-components/src/lib/dialog/dialog.module.ts +++ b/projects/netgrif-components/src/lib/dialog/dialog.module.ts @@ -82,7 +82,6 @@ import {openTaskDialog} from "./model/dialog-actions"; FlexModule, FormsModule, SnackBarModule, - TranslateLibModule, SideMenuUserAssignComponentModule, SideMenuAdminImpersonateComponentModule, SideMenuFilterSelectorComponentModule, From 9e7f44b0b88b9831560e386a8084e24e15615e0e Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 7 Jul 2026 11:37:10 +0200 Subject: [PATCH 15/18] [NAE-2454] View Configuration 2.0 - fix observable initialization - fix import --- .../dialog/models/task-view-dialog-injection-data.ts | 2 +- .../lib/view/case-view/service/case-view-service.ts | 12 ++++++++++-- .../lib/view/task-view/service/task-view.service.ts | 12 ++++++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/dialog/models/task-view-dialog-injection-data.ts b/projects/netgrif-components-core/src/lib/dialog/models/task-view-dialog-injection-data.ts index 5b50847c74..5c9eeec548 100644 --- a/projects/netgrif-components-core/src/lib/dialog/models/task-view-dialog-injection-data.ts +++ b/projects/netgrif-components-core/src/lib/dialog/models/task-view-dialog-injection-data.ts @@ -1,4 +1,4 @@ -import {TaskSearchRequestBody} from "netgrif-components-core"; +import {TaskSearchRequestBody} from '../../filter/models/task-search-request-body'; export interface TaskViewDialogInjectionData { searchBody: TaskSearchRequestBody; diff --git a/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts b/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts index cf7331d507..7bbc098b48 100644 --- a/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts +++ b/projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts @@ -374,10 +374,18 @@ export class CaseViewService extends AbstractSortableViewComponent implements On if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { this._dynamicDefaultSort$.subscribe(sortChangeDesc => { this._lastHeaderSearchState = sortChangeDesc; - this._nextPage$.next(requestContext); + this.requestNextPage(requestContext); }); } else { - this._nextPage$.next(requestContext); + this.requestNextPage(requestContext); + } + } + + protected requestNextPage(page: PageLoadRequestContext) { + if (!this._nextPage$) { + this._nextPage$ = new BehaviorSubject(page); + } else { + this._nextPage$.next(page); } } } diff --git a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts index 057cf094eb..a67cc74e71 100644 --- a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts +++ b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts @@ -370,10 +370,18 @@ export class TaskViewService extends AbstractSortableViewComponent implements On if (!!this._dynamicDefaultSort$ && this._lastHeaderSearchState.fieldIdentifier === '') { this._dynamicDefaultSort$.subscribe(sortChangeDesc => { this._lastHeaderSearchState = sortChangeDesc; - this._requestedPage$.next(requestContext); + this.requestNextPage(requestContext); }); } else { - this._requestedPage$.next(requestContext); + this.requestNextPage(requestContext); + } + } + + protected requestNextPage(page: PageLoadRequestContext) { + if (!this._requestedPage$) { + this._requestedPage$ = new BehaviorSubject(page); + } else { + this._requestedPage$.next(page); } } } From 5d1c0b8d9fc7b7a42805c106d821d1cda45ae090 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 7 Jul 2026 12:38:07 +0200 Subject: [PATCH 16/18] [NAE-2454] View Configuration 2.0 - fix tests --- .../abstract-navigation-double-drawer.spec.ts | 11 ++++++----- .../navigation-double-drawer.component.spec.ts | 17 +++++++++++++++-- .../navigation-double-drawer.component.ts | 12 ++++++++---- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts index 81daea37f6..8f6acb56d4 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts @@ -153,19 +153,20 @@ class TestDrawerComponent extends AbstractNavigationDoubleDrawerComponent { _log: LoggerService, _config: ConfigurationService, _uriService: UriService, - _processService: ProcessService, _caseResourceService: CaseResourceService, _impersonationUserSelect: ImpersonationUserSelectService, _impersonation: ImpersonationService, _dynamicRouteProviderService: DynamicNavigationRouteProviderService, _redirectService: RedirectService, + _navigationService: DoubleDrawerNavigationService, + _processService: ProcessService, _snackBarService: SnackBarService, _dialog: MatDialog, - _injector: Injector, - _navigationService: DoubleDrawerNavigationService) { + _injector: Injector) { super(_router, _activatedRoute, _breakpoint, _languageService, _translateService, _userService, _accessService, - _log, _config, _uriService, _processService, _caseResourceService, _impersonationUserSelect, _impersonation, - _dynamicRouteProviderService, _redirectService, _snackBarService, _dialog, _injector, _navigationService); + _log, _config, _uriService, _caseResourceService, _impersonationUserSelect, _impersonation, + _dynamicRouteProviderService, _redirectService, _navigationService, _processService, _snackBarService, + _dialog, _injector, undefined); } } diff --git a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.spec.ts b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.spec.ts index 99032a8a7e..c91b3d1a5f 100644 --- a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.spec.ts +++ b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.spec.ts @@ -18,7 +18,11 @@ import { TestConfigurationService, TranslateLibModule, UserPreferenceService, - UserResourceService + SearchService, + UserResourceService, + NAE_TASK_VIEW_COMPONENT, + NAE_BASE_FILTER, + TaskViewService, TestTaskBaseFilterProvider } from '@netgrif/components-core'; import {FlexLayoutModule, FlexModule} from '@ngbracket/ngx-layout'; import {QuickPanelComponentModule} from '../quick-panel/quick-panel.module'; @@ -27,6 +31,8 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {HttpClientTestingModule} from '@angular/common/http/testing'; import {ResizableModule} from 'angular-resizable-element'; import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import {TaskViewDialogComponent} from "../../dialog/task-view-dialog/task-view-dialog.component"; +import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from "@angular/material/dialog"; describe('NavigationDoubleDrawerComponent', () => { let component: NavigationDoubleDrawerComponent; @@ -47,6 +53,7 @@ describe('NavigationDoubleDrawerComponent', () => { TranslateLibModule, HttpClientTestingModule, ResizableModule, + MatDialogModule, AuthenticationModule ], providers: [ @@ -54,7 +61,13 @@ describe('NavigationDoubleDrawerComponent', () => { {provide: ConfigurationService, useClass: TestConfigurationService}, {provide: AuthenticationService, useClass: MockAuthenticationService}, {provide: UserResourceService, useClass: MockUserResourceService}, - {provide: UserPreferenceService, useClass: MockUserPreferenceService} + {provide: UserPreferenceService, useClass: MockUserPreferenceService}, + {provide: NAE_TASK_VIEW_COMPONENT, useClass: TaskViewDialogComponent}, + {provide: MatDialogRef, useValue: {}}, + {provide: MAT_DIALOG_DATA, useValue: {searchBody: {}}}, + TaskViewService, + SearchService, + {provide: NAE_BASE_FILTER, useFactory: TestTaskBaseFilterProvider}, ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts index 026e02c4ee..d8ce849e13 100644 --- a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts +++ b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts @@ -1,4 +1,4 @@ -import {Component, Injector} from '@angular/core'; +import {Component, Inject, Injector} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {BreakpointObserver} from '@angular/cdk/layout'; import { @@ -16,11 +16,13 @@ import { ImpersonationService, CaseResourceService, RedirectService, - DoubleDrawerNavigationService + DoubleDrawerNavigationService, + NAE_TASK_VIEW_COMPONENT } from '@netgrif/components-core'; import {animate, state, style, transition, trigger} from "@angular/animations"; import {TranslateService} from "@ngx-translate/core"; import {MatDialog} from "@angular/material/dialog"; +import {ComponentType} from "@angular/cdk/portal"; @Component({ selector: 'nc-navigation-double-drawer', @@ -79,10 +81,12 @@ export class NavigationDoubleDrawerComponent extends AbstractNavigationDoubleDra _processService: ProcessService, _snackBarService: SnackBarService, _dialog: MatDialog, - _injector: Injector) { + _injector: Injector, + @Inject(NAE_TASK_VIEW_COMPONENT) _taskView: ComponentType) { super(_router, _activatedRoute, _breakpoint, _languageService, _translateService, _userService, _accessService, _log, _config, _uriService, _caseResourceService, _impersonationUserSelect, _impersonation, - _dynamicRouteProviderService, _redirectService, _navigationService, _processService, _snackBarService, _dialog, _injector); + _dynamicRouteProviderService, _redirectService, _navigationService, _processService, _snackBarService, _dialog, + _injector, _taskView); } public toggleSection(section: string): void { From 524bfd28d49918f4a3142a6decad5659c50e84a2 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 7 Jul 2026 13:18:02 +0200 Subject: [PATCH 17/18] [NAE-2454] View Configuration 2.0 - remove unused injector --- .../abstract-navigation-double-drawer.spec.ts | 7 +++---- .../abstract-navigation-double-drawer.ts | 3 +-- .../navigation-double-drawer.component.ts | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts index 8f6acb56d4..a255722e0f 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.spec.ts @@ -1,7 +1,7 @@ import {BreakpointObserver} from '@angular/cdk/layout'; import {CommonModule} from '@angular/common'; import {HttpClientTestingModule} from '@angular/common/http/testing'; -import {Component, CUSTOM_ELEMENTS_SCHEMA, Injector} from '@angular/core'; +import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {FlexLayoutModule, FlexModule} from '@ngbracket/ngx-layout'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; @@ -161,12 +161,11 @@ class TestDrawerComponent extends AbstractNavigationDoubleDrawerComponent { _navigationService: DoubleDrawerNavigationService, _processService: ProcessService, _snackBarService: SnackBarService, - _dialog: MatDialog, - _injector: Injector) { + _dialog: MatDialog) { super(_router, _activatedRoute, _breakpoint, _languageService, _translateService, _userService, _accessService, _log, _config, _uriService, _caseResourceService, _impersonationUserSelect, _impersonation, _dynamicRouteProviderService, _redirectService, _navigationService, _processService, _snackBarService, - _dialog, _injector, undefined); + _dialog, undefined); } } diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts index 9440284241..1a4180b255 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts @@ -1,5 +1,5 @@ import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout'; -import {Component, EventEmitter, Inject, Injector, Input, OnDestroy, OnInit, Output, TemplateRef} from '@angular/core'; +import {Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output, TemplateRef} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {TranslateService} from '@ngx-translate/core'; import {ResizeEvent} from 'angular-resizable-element'; @@ -116,7 +116,6 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, protected _processService: ProcessService, protected _snackBarService: SnackBarService, protected _dialog: MatDialog, - protected _injector: Injector, @Inject(NAE_TASK_VIEW_COMPONENT) protected _taskView: ComponentType) { let configUrl: string = this._config.getServicesConfiguration()?.doubleDrawer?.url; if (configUrl !== undefined && !configUrl.startsWith('/')) { diff --git a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts index d8ce849e13..ad245d7796 100644 --- a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts +++ b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.ts @@ -1,4 +1,4 @@ -import {Component, Inject, Injector} from '@angular/core'; +import {Component, Inject} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {BreakpointObserver} from '@angular/cdk/layout'; import { @@ -81,12 +81,11 @@ export class NavigationDoubleDrawerComponent extends AbstractNavigationDoubleDra _processService: ProcessService, _snackBarService: SnackBarService, _dialog: MatDialog, - _injector: Injector, @Inject(NAE_TASK_VIEW_COMPONENT) _taskView: ComponentType) { super(_router, _activatedRoute, _breakpoint, _languageService, _translateService, _userService, _accessService, _log, _config, _uriService, _caseResourceService, _impersonationUserSelect, _impersonation, _dynamicRouteProviderService, _redirectService, _navigationService, _processService, _snackBarService, _dialog, - _injector, _taskView); + _taskView); } public toggleSection(section: string): void { From 0892c32c90f663ba39d9e3202c6b2fc925cf9f9d Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 13 Jul 2026 15:37:10 +0200 Subject: [PATCH 18/18] [NAE-2454] View Configuration 2.0 - add attribute to createMenuItem method --- .../abstract-navigation-double-drawer.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts index 1a4180b255..3b6047889b 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts @@ -366,14 +366,12 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, }); } - public createMenuItem() { + public createMenuItem(additionalParams?: {[k: string]: string}) { + const baseParams = { "dest_uri_path": this.currentNode.uriPath }; + let mergedParams: {[k: string]: string} = !!additionalParams ? {...baseParams, ...additionalParams} : baseParams; this._processService.getNet('menu_item').pipe( switchMap(net => { - return this._caseResourceService.createCase({ - netId: net.stringId, - title: 'New menu item', - params: { "dest_uri_path": this.currentNode.uriPath } - }); + return this._caseResourceService.createCase({ netId: net.stringId, title: 'New menu item', params: mergedParams }); }), take(1) ).subscribe({