From 23769434503318e26b399dfb0db5ca3edfd97a87 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 4 May 2026 08:08:28 +0200 Subject: [PATCH 01/49] [ETASK-23] Dynamic view configuration - update text translations --- .../menu/domain/configurations/TabbedCaseViewBody.java | 5 +++-- .../petriNets/engine-processes/menu/menu_item.xml | 8 +++++--- .../menu/tabbed_case_view_configuration.xml | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java index 5f18b2920cb..da2fb5314d6 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java @@ -2,6 +2,7 @@ import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import lombok.Data; import lombok.EqualsAndHashCode; @@ -15,7 +16,7 @@ @EqualsAndHashCode(callSuper = true) public class TabbedCaseViewBody extends ViewBody { private String viewSearchType = "fulltext_advanced"; - private String createCaseButtonTitle; + private I18nString createCaseButtonTitle; private String createCaseButtonIcon = "add"; private boolean requireTitleInCreation = true; private boolean showCreateCaseButton = true; @@ -45,7 +46,7 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, this.viewSearchType); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.TEXT, + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.I18N, this.createCaseButtonTitle); outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_ICON, FieldType.TEXT, this.createCaseButtonIcon); diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 8fe4ae4c9f7..f9f9fc7335b 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -468,7 +468,7 @@ tab_icon_preview - Tab icon preview + Tab icon preview htmltextarea @@ -614,6 +614,7 @@ Bude zobrazený v menu Identifikátor ikony v karte Identifikátor Material ikony. Zoznam ikon s identifikátormi je dostupný online. + Náhľad ikony v karte Zobraziť ikonu v karte? Názov položky Bude zobrazený v karte @@ -670,6 +671,7 @@ Titel des Eintrages Wird im Menü angezeigt Ikonen Identifikator der Registerkarte + Vorschau der Registerkarte Ikone Zeige die Registerkarte Ikone an? Titel der Registerkarte Wird in der Registerkarte angezeigt @@ -1436,11 +1438,11 @@ finish - + </event> <event type="delegate"> <id>delegate</id> - <title> + </event> </transition> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 3e1adad7aa7..cdb1d1c0ecb 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -229,7 +229,7 @@ </options> <init>fulltext_advanced</init> </data> - <data type="text" immediate="true"> + <data type="i18n" immediate="true"> <id>create_case_button_title</id> <title name="create_case_button_title">"New case" button title From a94fafd4257539468f56259ae0dfc49267e056f7 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 4 May 2026 09:16:19 +0200 Subject: [PATCH 02/49] [ETASK-23] Dynamic view configuration - fix null node - add todo --- .../menu/domain/configurations/TabbedCaseViewBody.java | 6 ++++-- .../application/engine/menu/services/MenuItemService.java | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java index da2fb5314d6..661ff73eaa5 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java @@ -46,8 +46,10 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, this.viewSearchType); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.I18N, - this.createCaseButtonTitle); + if (this.createCaseButtonTitle != null) { + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.I18N, + this.createCaseButtonTitle); + } outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_ICON, FieldType.TEXT, this.createCaseButtonIcon); outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_REQUIRE_TITLE_IN_CREATION, FieldType.BOOLEAN, diff --git a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java index 23f18825609..eceb876213f 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java @@ -104,6 +104,8 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce throw new IllegalArgumentException(String.format("Menu item identifier %s is not unique!", sanitizedIdentifier)); } + // todo ETASK-23 validation + Case parentItemCase = getOrCreateFolderItem(body.getUri()); I18nString newName = body.getMenuName(); if (newName == null) { From 42b8bb3658979b3c60d698545fb7802fca34bcf2 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 4 May 2026 12:02:58 +0200 Subject: [PATCH 03/49] [ETASK-23] Dynamic view configuration - update petri net of menu_item.xml - fix handling of uri - update role permission in menu_item.xml --- .../engine/menu/domain/MenuItemConstants.java | 2 +- .../engine/menu/services/MenuItemService.java | 5 +- .../engine/menu/utils/MenuItemUtils.java | 2 +- .../engine-processes/menu/menu_item.xml | 146 +++++++++++++++--- 4 files changed, 130 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java index d6ac1a37afc..0c1912a69fc 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java @@ -30,6 +30,6 @@ public class MenuItemConstants { public static final String FIELD_VIEW_CONFIGURATION_FORM = "view_configuration_form"; public static final String TRANS_SETTINGS_ID = "item_settings"; - public static final String TRANS_INIT_ID = "initialize"; + public static final String TRANS_INIT_ID = "system_initialize"; public static final String TRANS_SYNC_ID = "data_sync"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java index eceb876213f..05ae30fd936 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java @@ -104,7 +104,7 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce throw new IllegalArgumentException(String.format("Menu item identifier %s is not unique!", sanitizedIdentifier)); } - // todo ETASK-23 validation + // todo 23 validation Case parentItemCase = getOrCreateFolderItem(body.getUri()); I18nString newName = body.getMenuName(); @@ -577,6 +577,9 @@ protected List updateNodeInChildrenFoldersRecursive(Case parentFolder) { protected void resolveAndHandleNewNodePath(Case folderItem, String destUri) { String newNodePath = resolveNewNodePath(folderItem, destUri); + if (newNodePath.startsWith("//")) { + newNodePath = newNodePath.replace("//", uriService.getUriSeparator()); + } UriNode newNode = uriService.getOrCreate(newNodePath, UriContentType.CASE); folderItem.getDataField(MenuItemConstants.FIELD_NODE_PATH).setValue(newNode.getUriPath()); } diff --git a/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java index d1fcaf8e590..277de33d43a 100644 --- a/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java +++ b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java @@ -64,7 +64,7 @@ public static String findTaskIdInCase(Case useCase, String transId) { * */ public static boolean isCyclicNodePath(Case folderItem, String destUri) { String oldNodePath = (String) folderItem.getFieldValue(MenuItemConstants.FIELD_NODE_PATH); - return destUri.contains(oldNodePath); + return oldNodePath != null && destUri.contains(oldNodePath); } /** diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index f9f9fc7335b..9f934d81974 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -25,8 +25,6 @@ default - false - false true @@ -643,6 +641,9 @@ Použiť zobrazenie v taboch? Vybrať zobrazenie Nastavenie zobrazenia + Vytvoriť menu položku + Zoradiť pod-položky + Vytvoriť menu položku Ikonevorschau @@ -692,6 +693,9 @@ Möchten Sie die Ansicht mit Registerkarten verwenden? Wählen Sie einen Ansichtstyp Die Ansichtskonfiguration + Menüeintrag erstellen + Untereintrage sortieren + Erstellen @@ -699,22 +703,96 @@ initialize 340 220 - + hourglass_empty admin true - true - true - true + + initialize_0 + 4 + grid + + menu_item_identifier + + editable + required + + + 0 + 0 + 1 + 4 + + outline + + + + move_dest_uri + + editable + required + + + 0 + 1 + 1 + 2 + + outline + + + + move_dest_uri_new_node + + editable + + + 2 + 1 + 1 + 1 + + outline + + + + move_add_node + + editable + + + 3 + 1 + 1 + 1 + + outline + + + + + finish + + + dest: f.move_dest_uri; + + String newUri = dest.value.join("/") + newUri = newUri.replace("//","/") + + changeMenuItem useCase uri { newUri } + + + Create + data_sync 340 - 340 + 112 system @@ -722,6 +800,12 @@ true + + admin + + true + + @@ -735,9 +819,12 @@ admin true + + + + default + true - true - true @@ -1178,9 +1265,6 @@ admin true - true - true - true @@ -1262,9 +1346,6 @@ admin true - true - true - true @@ -1320,16 +1401,13 @@ children_order 580 220 - + low_priority auto admin true - true - true - true @@ -1368,9 +1446,6 @@ system true - true - true - true @@ -1445,6 +1520,19 @@ </event> </transition> + <transition> + <id>system_initialize</id> + <x>336</x> + <y>304</y> + <label>System create</label> + <icon>computer</icon> + <roleRef> + <id>system</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + </transition> <!-- PLACES--> <place> @@ -1507,4 +1595,18 @@ <destinationId>children_order</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a14</id> + <type>regular</type> + <sourceId>uninitialized</sourceId> + <destinationId>system_initialize</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a15</id> + <type>regular</type> + <sourceId>system_initialize</sourceId> + <destinationId>initialized</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file From b5164e4127c5b788102f772fc7197502331d20f6 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Wed, 6 May 2026 12:23:27 +0200 Subject: [PATCH 04/49] [ETASK-23] Dynamic view configuration - refactor menu item view forms - implement MenuController.getMenuItemData - implement MenuItemServiceTest.getMenuItemDataTest --- .../logic/action/ActionDelegate.groovy | 6 +- .../startup/DefaultDashboardRunner.groovy | 2 +- .../DashboardItemServiceImpl.java | 4 +- .../DashboardManagementServiceImpl.java | 4 +- .../MenuItemService.java | 48 +- .../interfaces/DashboardItemService.java | 2 +- .../DashboardManagementService.java | 2 +- .../interfaces/IMenuItemService.java | 5 +- .../engine/menu/utils/MenuItemUtils.java | 2 +- .../engine/menu/web/MenuController.java | 45 + .../responsebodies/MenuItemDataResponse.java | 23 + .../engine/workflow/domain/Case.java | 2 +- .../workflow/service/WorkflowService.java | 6 +- .../engine-processes/menu/menu_item.xml | 802 ++++++++++-------- .../menu/tabbed_case_view_configuration.xml | 457 +++++++--- .../tabbed_single_task_view_configuration.xml | 60 +- .../menu/tabbed_task_view_configuration.xml | 274 ++++-- .../menu/tabbed_ticket_view_configuration.xml | 101 ++- .../engine/menu/MenuItemServiceTest.java | 156 ++++ 19 files changed, 1388 insertions(+), 613 deletions(-) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/DashboardItemServiceImpl.java (97%) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/DashboardManagementServiceImpl.java (98%) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/MenuItemService.java (93%) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/interfaces/DashboardItemService.java (88%) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/interfaces/DashboardManagementService.java (89%) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/interfaces/IMenuItemService.java (93%) create mode 100644 src/main/java/com/netgrif/application/engine/menu/web/MenuController.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java create mode 100644 src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 26791594b3e..d4b527dcb6c 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -31,9 +31,9 @@ import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewB import com.netgrif.application.engine.menu.domain.configurations.ViewBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody -import com.netgrif.application.engine.menu.services.interfaces.DashboardItemService -import com.netgrif.application.engine.menu.services.interfaces.DashboardManagementService -import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService +import com.netgrif.application.engine.menu.service.interfaces.DashboardItemService +import com.netgrif.application.engine.menu.service.interfaces.DashboardManagementService +import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService import com.netgrif.application.engine.orgstructure.groups.interfaces.INextGroupService import com.netgrif.application.engine.pdf.generator.config.PdfResource import com.netgrif.application.engine.pdf.generator.service.interfaces.IPdfGenerator diff --git a/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy index 7e5475ef098..1e580755668 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy @@ -2,7 +2,7 @@ package com.netgrif.application.engine.startup import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody -import com.netgrif.application.engine.menu.services.interfaces.DashboardManagementService +import com.netgrif.application.engine.menu.service.interfaces.DashboardManagementService import com.netgrif.application.engine.petrinet.domain.I18nString import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/service/DashboardItemServiceImpl.java similarity index 97% rename from src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java rename to src/main/java/com/netgrif/application/engine/menu/service/DashboardItemServiceImpl.java index cd8b2c3a09c..20dbb8dbe18 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/DashboardItemServiceImpl.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.menu.services; +package com.netgrif.application.engine.menu.service; import com.netgrif.application.engine.auth.domain.IUser; import com.netgrif.application.engine.auth.domain.LoggedUser; @@ -8,7 +8,7 @@ import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody; import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemConstants; -import com.netgrif.application.engine.menu.services.interfaces.DashboardItemService; +import com.netgrif.application.engine.menu.service.interfaces.DashboardItemService; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/service/DashboardManagementServiceImpl.java similarity index 98% rename from src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java rename to src/main/java/com/netgrif/application/engine/menu/service/DashboardManagementServiceImpl.java index c35ce0267f8..944270d33e5 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/DashboardManagementServiceImpl.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.menu.services; +package com.netgrif.application.engine.menu.service; import com.netgrif.application.engine.auth.domain.IUser; import com.netgrif.application.engine.auth.domain.LoggedUser; @@ -9,7 +9,7 @@ import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemConstants; import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody; import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementConstants; -import com.netgrif.application.engine.menu.services.interfaces.DashboardManagementService; +import com.netgrif.application.engine.menu.service.interfaces.DashboardManagementService; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; diff --git a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java similarity index 93% rename from src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java rename to src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index 05ae30fd936..4c8f3d9ae65 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.menu.services; +package com.netgrif.application.engine.menu.service; import com.netgrif.application.engine.auth.domain.IUser; import com.netgrif.application.engine.auth.domain.LoggedUser; @@ -11,11 +11,12 @@ import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.menu.domain.configurations.ViewBody; import com.netgrif.application.engine.menu.domain.configurations.ViewConstants; -import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService; +import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.UriContentType; import com.netgrif.application.engine.petrinet.domain.UriNode; +import com.netgrif.application.engine.petrinet.domain.dataset.Field; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.petrinet.service.interfaces.IUriService; @@ -23,6 +24,7 @@ import com.netgrif.application.engine.startup.FilterRunner; import com.netgrif.application.engine.startup.ImportHelper; import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.domain.DataField; import com.netgrif.application.engine.workflow.domain.Task; import com.netgrif.application.engine.workflow.service.interfaces.IDataService; import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; @@ -400,6 +402,48 @@ public Case removeChildItemFromParent(String folderId, Case childItem) { return workflowService.save(parentFolder); } + /** + * Retrieves menu item data along with its associated view configuration data. The method collects immediate + * data from the menu item case and recursively traverses through all associated view configuration cases, + * aggregating their immediate data into a single map. + * + * @param caseId identifier of the menu item case + * @return map where keys are process identifiers (with "_configuration" suffix removed for view cases) and + * values are lists of immediate data fields from the corresponding cases + */ + @Override + public Map<String, List<Field<?>>> getMenuItemData(String caseId) { + Case menuItemCase = workflowService.findOne(caseId); + Map<String, List<Field<?>>> immediateDataMap = new HashMap<>(); + immediateDataMap.put(menuItemCase.getProcessIdentifier(), menuItemCase.getImmediateData()); + Optional<String> viewCaseIdOpt = getNextViewCaseId(menuItemCase); + if (viewCaseIdOpt.isEmpty()) { + return immediateDataMap; + } + + do { + Case viewCase = workflowService.findOne(viewCaseIdOpt.get()); + immediateDataMap.put(viewCase.getProcessIdentifier().replace("_configuration", ""), + viewCase.getImmediateData()); + viewCaseIdOpt = getNextViewCaseId(viewCase); + } while (viewCaseIdOpt.isPresent()); + + return immediateDataMap; + } + + protected Optional<String> getNextViewCaseId(Case parentCase) { + DataField viewConfigurationIdDataField = parentCase.getDataField(MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID); + if (viewConfigurationIdDataField == null) { + return Optional.empty(); + } + @SuppressWarnings("unchecked") + List<String> viewConfigurationIdValue = (List<String>) viewConfigurationIdDataField.getValue(); + if (viewConfigurationIdValue == null || viewConfigurationIdValue.isEmpty()) { + return Optional.empty(); + } + return Optional.ofNullable(viewConfigurationIdValue.get(0)); + } + protected Case findCase(String processIdentifier, String query) { CaseSearchRequest request = CaseSearchRequest.builder() .process(Collections.singletonList(new CaseSearchRequest.PetriNet(processIdentifier))) diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardItemService.java similarity index 88% rename from src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardItemService.java rename to src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardItemService.java index 6555ad2c04f..3dd698d53c1 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardItemService.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.menu.services.interfaces; +package com.netgrif.application.engine.menu.service.interfaces; import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardManagementService.java similarity index 89% rename from src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java rename to src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardManagementService.java index 01efb79d055..e5765ddbf3b 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardManagementService.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.menu.services.interfaces; +package com.netgrif.application.engine.menu.service.interfaces; import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java similarity index 93% rename from src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java rename to src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java index 9de2a7f2308..929f348cf10 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java @@ -1,14 +1,16 @@ -package com.netgrif.application.engine.menu.services.interfaces; +package com.netgrif.application.engine.menu.service.interfaces; import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.UriNode; +import com.netgrif.application.engine.petrinet.domain.dataset.Field; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.workflow.domain.Case; import com.netgrif.application.engine.petrinet.domain.dataset.MapOptionsField; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -27,6 +29,7 @@ public interface IMenuItemService { void moveItem(Case item, String destUri) throws TransitionNotExecutableException; Case duplicateItem(Case originItem, I18nString newTitle, String newIdentifier) throws TransitionNotExecutableException; Case removeChildItemFromParent(String folderId, Case childItem); + Map<String, List<Field<?>>> getMenuItemData(String caseId); /** * Gets all tabbed or non-tabbed views diff --git a/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java index 277de33d43a..8472e838599 100644 --- a/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java +++ b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java @@ -3,7 +3,7 @@ import com.netgrif.application.engine.menu.domain.MenuItemConstants; import com.netgrif.application.engine.workflow.domain.Case; import com.netgrif.application.engine.workflow.domain.TaskPair; -import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService; +import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import java.text.Normalizer; import java.util.List; diff --git a/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java b/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java new file mode 100644 index 00000000000..27ffee572e9 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java @@ -0,0 +1,45 @@ +package com.netgrif.application.engine.menu.web; + +import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; +import com.netgrif.application.engine.menu.web.responsebodies.MenuItemDataResponse; +import com.netgrif.application.engine.petrinet.domain.dataset.Field; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.MediaTypes; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; + +import java.util.Base64; +import java.util.List; +import java.util.Map; + +@Slf4j +@RestController +@Tag(name = "Menu") +@RequiredArgsConstructor +@RequestMapping("/api/menu") +public class MenuController { + + private final IMenuItemService menuItemService; + + @Operation(summary = "Get relevant data for the menu item", security = {@SecurityRequirement(name = "BasicAuth")}) + @GetMapping(value = "/{encodedCaseId}", produces = MediaTypes.HAL_JSON_VALUE) + public EntityModel<MenuItemDataResponse> getMenuItemData(String encodedCaseId) { + try { + String caseId = new String(Base64.getDecoder().decode(encodedCaseId)); + Map<String, List<Field<?>>> immediateDataMap = menuItemService.getMenuItemData(caseId); + return EntityModel.of(new MenuItemDataResponse(immediateDataMap)); + } catch (Exception e) { + log.error("Getting menu item data failed", e); + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Getting menu item data failed", e); + } + } +} + diff --git a/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java b/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java new file mode 100644 index 00000000000..43f265a9547 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java @@ -0,0 +1,23 @@ +package com.netgrif.application.engine.menu.web.responsebodies; + +import com.netgrif.application.engine.petrinet.domain.dataset.Field; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RequiredArgsConstructor +public class MenuItemDataResponse { + + /** + * Map containing menu item data where key is the view type and value is a list of immediate fields. + */ + @Getter + private final Map<String, List<Field<?>>> data; + + public MenuItemDataResponse() { + this(new HashMap<>()); + } +} diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java b/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java index 1559cbe0017..cd0c8f24596 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java +++ b/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java @@ -100,7 +100,7 @@ public class Case implements Serializable { @Getter @Setter @Transient - private List<Field> immediateData; + private List<Field<?>> immediateData; @Getter @Setter diff --git a/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java b/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java index 472df01d5ae..6cd2c5e8826 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java +++ b/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java @@ -505,11 +505,11 @@ public List<Field> getData(String caseId) { } private void setImmediateDataFieldsReadOnly(Case useCase) { - List<Field> immediateData = new ArrayList<>(); + List<Field<?>> immediateData = new ArrayList<>(); useCase.getImmediateDataFields().forEach(fieldId -> { try { - Field clone = fieldFactory.buildImmediateField(useCase, fieldId); + Field<?> clone = fieldFactory.buildImmediateField(useCase, fieldId); immediateData.add(clone); } catch (Exception e) { log.error("Could not built immediate field [" + fieldId + "]"); @@ -526,7 +526,7 @@ protected Page<Case> setImmediateDataFields(Page<Case> cases) { } protected Case setImmediateDataFields(Case useCase) { - List<Field> immediateData = new ArrayList<>(); + List<Field<?>> immediateData = new ArrayList<>(); useCase.getImmediateDataFields().forEach(fieldId -> immediateData.add(fieldFactory.buildImmediateField(useCase, fieldId)) diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 9f934d81974..1f53c563974 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -591,6 +591,31 @@ ]) </action> </data> + <data type="i18n"> + <id>role_divider</id> + <title/> + <component> + <name>divider</name> + </component> + </data> + <data type="taskRef"> + <id>advanced_content_form</id> + <title/> + <component> + <name>task-list</name> + <!-- todo 23 headers--> + </component> + <event type="get"> + <id>get</id> + <actions phase="pre"> + <action> + advanced_content_form: f.advanced_content_form; + change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "role_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "view_settings"}.task] } + </action> + </actions> + </event> + </data> <!-- I18NS --> <i18n locale="sk"> @@ -629,7 +654,6 @@ <i18nString name="roles_allowed_desc">Zoznam povolených rolí, ktoré môžu zobraziť túto položku</i18nString> <i18nString name="roles_banned">Zakázané roly</i18nString> <i18nString name="roles_banned_desc">Zoznam zakázaných rolí, ktoré nemôžu zobraziť túto položku</i18nString> - <i18nString name="item_settings_general">Všeobecné</i18nString> <i18nString name="menu_item_identifier">Identifikátor položky</i18nString> <i18nString name="nodePath">URI položky</i18nString> <i18nString name="move_dest_uri_new_node">Nový uzol</i18nString> @@ -660,7 +684,6 @@ <i18nString name="custom_view_selector_desc">Beispiel: "demo-tabbed-views"</i18nString> <i18nString name="roles_management_title">Rollen</i18nString> <i18nString name="roles_allowed">Zulässige Rollen</i18nString> - <i18nString name="item_settings_general">Allgemein</i18nString> <i18nString name="menu_item_identifier">Identifikationsnummer des Menüeintrages</i18nString> <i18nString name="nodePath">Menüeintrag-URI</i18nString> <i18nString name="move_dest_uri_new_node">Neuer Knoten</i18nString> @@ -800,12 +823,6 @@ <perform>true</perform> </logic> </roleRef> - <roleRef> - <id>admin</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> </transition> <transition> @@ -831,7 +848,6 @@ <id>general_0</id> <cols>4</cols> <layout>grid</layout> - <title name="item_settings_general">General menu_item_identifier @@ -883,7 +899,7 @@ 2 1 1 - 1 + 2 outline @@ -894,10 +910,10 @@ visible - 3 - 1 + 2 + 2 1 - 1 + 2 standard @@ -908,52 +924,73 @@ editable - 1 + 0 2 1 - 1 + 2 standard - - - roles_management - 5 - grid - Roles - processes_available + advanced_content_form editable 0 - 0 - 2 - 1 - 0 + 3 + 1 + 4 outline + + + assign_0 + + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + </transition> + + <transition> + <id>move_item</id> + <x>580</x> + <y>100</y> + <label name="move_item">Move item</label> + <icon>move_down</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <dataGroup> + <id>move</id> + <cols>4</cols> + <layout>grid</layout> <dataRef> - <id>roles_available</id> + <id>move_dest_uri</id> <logic> <behavior>editable</behavior> + <behavior>required</behavior> </logic> <layout> - <x>1</x> + <x>0</x> <y>0</y> - <rows>2</rows> - <cols>1</cols> - <offset>0</offset> + <rows>1</rows> + <cols>2</cols> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>add_allowed_roles</id> + <id>move_dest_uri_new_node</id> <logic> <behavior>editable</behavior> </logic> @@ -962,12 +999,12 @@ <y>0</y> <rows>1</rows> <cols>1</cols> - <offset>0</offset> <template>material</template> + <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>allowed_roles</id> + <id>move_add_node</id> <logic> <behavior>editable</behavior> </logic> @@ -976,90 +1013,439 @@ <y>0</y> <rows>1</rows> <cols>1</cols> - <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> + </dataGroup> + <event type="finish"> + <id>finish</id> + <actions phase="pre"> + <action> + dest: f.move_dest_uri; + + if (dest.value == null || dest.value == []) { + throw new IllegalArgumentException("URI must not be empty!") + } + + String newUri = dest.value.join("/") + newUri = newUri.replace("//","/") + + changeMenuItem useCase uri { newUri } + </action> + </actions> + <title name="move_item_finish">Move + + + + + duplicate_item + 580 + 340 + + content_copy + auto + + admin + + true + + + + duplicate + 4 + grid - remove_allowed_roles + duplicate_new_title editable + required - 4 + 0 0 1 - 1 - 0 + 4 + outline - add_banned_roles + duplicate_view_identifier editable + required - 2 + 0 1 1 - 1 - 0 + 4 + outline + + + finish + + + identifier: f.duplicate_view_identifier, + title: f.duplicate_new_title; + + duplicateMenuItem(useCase, title.value, identifier.value) + + + Duplicate + + + + + children_order + 580 + 220 + + low_priority + auto + + admin + + true + + + + children_order_0 + 4 + grid - banned_roles + childItemForms editable + + forms: f.childItemForms, + ids: f.childItemIds; + + def orderedTaskIds = ids.value?.collect { id -> workflowService.findOne(id).tasks.find { it.transition == "row_for_ordering" }.task } + change forms value { orderedTaskIds } + - 3 - 1 + 0 + 0 1 - 1 - 0 + 4 + + outline + + + + + + row_for_ordering + 741 + 219 + + + system + + true + + + + row_for_ordering_0 + 6 + grid + + menu_item_identifier + + visible + + + 0 + 0 + 1 + 2 outline - remove_banned_roles + menu_name_as_visible + + visible + + + 2 + 0 + 1 + 2 + + outline + + + + order_down editable 4 - 1 + 0 1 1 - 0 + 1 + outline - - - configuration_view - 4 - grid - View configuration - use_custom_view + order_up editable - 0 + 5 0 1 1 + 1 outline - - 0 - + + + + finish + + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>system_initialize</id> + <x>336</x> + <y>304</y> + <label>System create</label> + <icon>computer</icon> + <roleRef> + <id>system</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + </transition> + <transition> + <id>role_settings</id> + <x>400</x> + <y>48</y> + <label name="todo 23">Role</label> + <icon>person</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>roles_management</id> + <cols>3</cols> + <layout>grid</layout> + <dataRef> + <id>processes_available</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>2</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>roles_available</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>1</x> + <y>0</y> + <rows>2</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>add_allowed_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + </layout> + </dataRef> + <dataRef> + <id>add_banned_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>1</y> + <rows>1</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + </layout> + </dataRef> + <dataRef> + <id>role_divider</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>2</y> + <rows>1</rows> + <cols>3</cols> + <offset>0</offset> + <template>material</template> + </layout> + </dataRef> + <dataRef> + <id>allowed_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>3</y> + <rows>1</rows> + <cols>2</cols> + <offset>0</offset> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>remove_allowed_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>3</y> + <rows>1</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + </layout> + </dataRef> + <dataRef> + <id>banned_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>4</y> + <rows>1</rows> + <cols>2</cols> + <offset>0</offset> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>remove_banned_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>4</y> + <rows>1</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + </layout> + </dataRef> + </dataGroup> + <event type="assign"> + <id>assign</id> + <title/> + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>view_settings</id> + <x>528</x> + <y>48</y> + <label name="todo 23">View</label> + <icon>image</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>configuration_view</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>use_custom_view</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + <event type="set"> + <id>0</id> + <actions phase="post"> <action> trans: t.this, useTabIcon: f.use_tab_icon, @@ -1252,267 +1638,16 @@ </layout> </dataRef> </dataGroup> - </transition> - - <transition> - <id>move_item</id> - <x>580</x> - <y>100</y> - <label name="move_item">Move item</label> - <icon>move_down</icon> - <assignPolicy>auto</assignPolicy> - <roleRef> - <id>admin</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> - <dataGroup> - <id>move</id> - <cols>4</cols> - <layout>grid</layout> - <dataRef> - <id>move_dest_uri</id> - <logic> - <behavior>editable</behavior> - <behavior>required</behavior> - </logic> - <layout> - <x>0</x> - <y>0</y> - <rows>1</rows> - <cols>2</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>move_dest_uri_new_node</id> - <logic> - <behavior>editable</behavior> - </logic> - <layout> - <x>2</x> - <y>0</y> - <rows>1</rows> - <cols>1</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>move_add_node</id> - <logic> - <behavior>editable</behavior> - </logic> - <layout> - <x>3</x> - <y>0</y> - <rows>1</rows> - <cols>1</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - </dataGroup> - <event type="finish"> - <id>finish</id> - <actions phase="pre"> - <action> - dest: f.move_dest_uri; - - if (dest.value == null || dest.value == []) { - throw new IllegalArgumentException("URI must not be empty!") - } - - String newUri = dest.value.join("/") - newUri = newUri.replace("//","/") - - changeMenuItem useCase uri { newUri } - </action> - </actions> - <title name="move_item_finish">Move + + assign + </event> - </transition> - - <transition> - <id>duplicate_item</id> - <x>580</x> - <y>340</y> - <label name="duplicate_item">Duplicate item</label> - <icon>content_copy</icon> - <assignPolicy>auto</assignPolicy> - <roleRef> - <id>admin</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> - <dataGroup> - <id>duplicate</id> - <cols>4</cols> - <layout>grid</layout> - <dataRef> - <id>duplicate_new_title</id> - <logic> - <behavior>editable</behavior> - <behavior>required</behavior> - </logic> - <layout> - <x>0</x> - <y>0</y> - <rows>1</rows> - <cols>4</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>duplicate_view_identifier</id> - <logic> - <behavior>editable</behavior> - <behavior>required</behavior> - </logic> - <layout> - <x>0</x> - <y>1</y> - <rows>1</rows> - <cols>4</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - </dataGroup> - <event type="finish"> - <id>finish</id> - <actions phase="pre"> - <action> - identifier: f.duplicate_view_identifier, - title: f.duplicate_new_title; - - duplicateMenuItem(useCase, title.value, identifier.value) - </action> - </actions> - <title name="duplicate_item_finish">Duplicate + + cancel + </event> - </transition> - - <transition> - <id>children_order</id> - <x>580</x> - <y>220</y> - <label name="children_order">Manage item order</label> - <icon>low_priority</icon> - <assignPolicy>auto</assignPolicy> - <roleRef> - <id>admin</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> - <dataGroup> - <id>children_order_0</id> - <cols>4</cols> - <layout>grid</layout> - <dataRef> - <id>childItemForms</id> - <logic> - <behavior>editable</behavior> - <action trigger="get"> - forms: f.childItemForms, - ids: f.childItemIds; - - def orderedTaskIds = ids.value?.collect { id -> workflowService.findOne(id).tasks.find { it.transition == "row_for_ordering" }.task } - change forms value { orderedTaskIds } - </action> - </logic> - <layout> - <x>0</x> - <y>0</y> - <rows>1</rows> - <cols>4</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - </dataGroup> - </transition> - <transition> - <id>row_for_ordering</id> - <x>741</x> - <y>219</y> - <label>Row for ordering [referenced]</label> - <roleRef> - <id>system</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> - <dataGroup> - <id>row_for_ordering_0</id> - <cols>6</cols> - <layout>grid</layout> - <dataRef> - <id>menu_item_identifier</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>0</x> - <y>0</y> - <rows>1</rows> - <cols>2</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>menu_name_as_visible</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>2</x> - <y>0</y> - <rows>1</rows> - <cols>2</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>order_down</id> - <logic> - <behavior>editable</behavior> - </logic> - <layout> - <x>4</x> - <y>0</y> - <rows>1</rows> - <cols>1</cols> - <offset>1</offset> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>order_up</id> - <logic> - <behavior>editable</behavior> - </logic> - <layout> - <x>5</x> - <y>0</y> - <rows>1</rows> - <cols>1</cols> - <offset>1</offset> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - </dataGroup> <event type="finish"> - <id>finish</id> + <id>finish_0</id> <title/> </event> <event type="delegate"> @@ -1520,19 +1655,6 @@ <title/> </event> </transition> - <transition> - <id>system_initialize</id> - <x>336</x> - <y>304</y> - <label>System create</label> - <icon>computer</icon> - <roleRef> - <id>system</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> - </transition> <!-- PLACES--> <place> @@ -1609,4 +1731,18 @@ <destinationId>initialized</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a16</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>view_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a17</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>role_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index cdb1d1c0ecb..39740f21fce 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -25,8 +25,6 @@ <roleRef> <id>default</id> <caseLogic> - <create>false</create> - <delete>false</delete> <view>true</view> </caseLogic> </roleRef> @@ -147,7 +145,7 @@ <name>autocomplete_dynamic</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -156,7 +154,7 @@ updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) </action> <action trigger="get"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -173,7 +171,7 @@ <name>raised</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, update_filter: f.update_filter, contains_filter: f.contains_filter, filter_case: f.filter_case, @@ -392,6 +390,26 @@ <id>view_configuration_form</id> <title/> </data> + <data type="taskRef"> + <id>advanced_content_form</id> + <title/> + <component> + <!-- todo 23 headers--> + <name>task-list</name> + </component> + <event type="get"> + <id>get</id> + <actions phase="pre"> + <action> + advanced_content_form: f.advanced_content_form; + change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "header_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "create_case_btn_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "next_view_settings"}.task] } + </action> + </actions> + </event> + </data> <!-- I18NS --> <i18n locale="sk"> @@ -420,14 +438,12 @@ <i18nString name="allow_header_table_mode">Povoliť tabuľkový mód pre hlavičky?</i18nString> <i18nString name="use_default_headers">Použiť vlastné predvolené hlavičky?</i18nString> <i18nString name="item_settings">Nastavenie položky</i18nString> - <i18nString name="filter_update_title">Filter</i18nString> <i18nString name="filter_header">Súčasný filter</i18nString> <i18nString name="view_header">Zobrazenie prípadov</i18nString> <i18nString name="item_settings_general">Všeobecné</i18nString> <i18nString name="use_case_default_headers">Použiť predvolené hlavičky</i18nString> <i18nString name="view_configuration_type">Vybrať zobrazenie</i18nString> <i18nString name="settings">Nastavenie</i18nString> - <i18nString name="associated_view">Asociované zobrazenie</i18nString> </i18n> <i18n locale="de"> <i18nString name="create_case_button_title">Schaltflächentitel "Neuer Fall"</i18nString> @@ -446,7 +462,6 @@ <i18nString name="is_header_mode_changeable">Erlaube Änderung des Kopfzeilenmodus?</i18nString> <i18nString name="allow_header_table_mode">Erlaube Tabellenmodus?</i18nString> <i18nString name="use_default_headers">Eigene Kopfzeilen verwenden?</i18nString> - <i18nString name="filter_update_title">Filter</i18nString> <i18nString name="filter_header">Aktueller Filter</i18nString> <i18nString name="item_settings_general">Allgemein</i18nString> <i18nString name="update_filter">Aktualisiere die Ansicht mit dem ausgewählten Filter</i18nString> @@ -462,7 +477,6 @@ <i18nString name="use_case_default_headers">Benutzerdefinierte Standardheader verwenden?</i18nString> <i18nString name="view_configuration_type">Wählen Sie einen Ansichtstyp</i18nString> <i18nString name="settings">Einstellungen</i18nString> - <i18nString name="associated_view">zugehörige Ansicht</i18nString> </i18n> <transition> @@ -471,12 +485,24 @@ <y>208</y> <label>initialize [await sync]</label> <icon>hourglass_empty</icon> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>data_sync</id> <x>368</x> <y>328</y> <label>Data sync</label> + <roleRef> + <id>system</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>settings</id> @@ -488,35 +514,94 @@ <id>admin</id> <logic> <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> <view>true</view> - <cancel>true</cancel> - <assign>true</assign> </logic> </roleRef> <dataGroup> - <id>form_title</id> + <id>view_dataGroup</id> <cols>4</cols> <layout>grid</layout> <dataRef> - <id>view_header</id> + <id>view_search_type</id> <logic> - <behavior>visible</behavior> + <behavior>editable</behavior> + <behavior>required</behavior> </logic> <layout> <x>0</x> <y>0</y> <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>show_more_menu</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>advanced_content_form</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>1</y> + <rows>1</rows> <cols>4</cols> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> </dataGroup> + <event type="assign"> + <id>assign_0</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + </transition> + <transition> + <id>filter_settings</id> + <x>432</x> + <y>48</y> + <label name="todo 23">Filter</label> + <icon>filter_alt</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> - <id>filter_update</id> + <id>filter_0</id> <cols>4</cols> <layout>grid</layout> - <title name="filter_update_title">Filter filter_autocomplete_selection @@ -588,48 +673,151 @@ + + assign + + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>next_view_settings</id> + <x>560</x> + <y>48</y> + <label name="todo 23">Next view</label> + <icon>queue_play_next</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> - <id>view_dataGroup</id> + <id>associated_view_0</id> <cols>4</cols> <layout>grid</layout> <dataRef> - <id>view_search_type</id> + <id>view_configuration_type</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> <x>0</x> <y>0</y> <rows>1</rows> - <cols>2</cols> + <cols>4</cols> + <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> + <event type="set"> + <id>0</id> + <actions phase="post"> + <action> + view_configuration_type: f.view_configuration_type, + view_configuration_form: f.view_configuration_form, + view_configuration_id: f.view_configuration_id; + + if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { + workflowService.deleteCase(view_configuration_id.value[0]) + } + + if (view_configuration_type.value == null || view_configuration_type.value == "") { + change view_configuration_id value { [] } + change view_configuration_form value { [] } + return + } + + def configurationCase = createCase(view_configuration_type.value + "_configuration") + def initTask = assignTask("initialize", configurationCase) + finishTask(initTask) + configurationCase = workflowService.findOne(configurationCase.stringId) + change view_configuration_id value { [configurationCase.stringId] } + change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } + </action> + </actions> + </event> </dataRef> <dataRef> - <id>show_create_case_button</id> + <id>view_configuration_form</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> - <x>2</x> - <y>0</y> + <x>0</x> + <y>1</y> <rows>1</rows> - <cols>1</cols> + <cols>4</cols> + <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> + </dataGroup> + <event type="assign"> + <id>assign</id> + <title/> + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>create_case_btn_settings</id> + <x>656</x> + <y>48</y> + <label name="todo 23">Create case button</label> + <icon>add_box</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>create_case_btn_0</id> + <cols>3</cols> + <layout>grid</layout> <dataRef> - <id>show_more_menu</id> + <id>show_create_case_button</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> - <x>3</x> + <x>0</x> <y>0</y> <rows>1</rows> <cols>1</cols> @@ -638,27 +826,27 @@ </layout> </dataRef> <dataRef> - <id>create_case_button_title</id> + <id>require_title_in_creation</id> <logic> <behavior>editable</behavior> </logic> <layout> - <x>0</x> - <y>1</y> + <x>1</x> + <y>0</y> <rows>1</rows> <cols>1</cols> <offset>0</offset> <template>material</template> - <appearance>outline</appearance> + <appearance>standard</appearance> </layout> </dataRef> <dataRef> - <id>create_case_button_icon</id> + <id>create_case_button_title</id> <logic> <behavior>editable</behavior> </logic> <layout> - <x>1</x> + <x>0</x> <y>1</y> <rows>1</rows> <cols>1</cols> @@ -668,27 +856,27 @@ </layout> </dataRef> <dataRef> - <id>create_case_button_icon_preview</id> + <id>create_case_button_icon</id> <logic> - <behavior>visible</behavior> + <behavior>editable</behavior> </logic> <layout> - <x>2</x> + <x>1</x> <y>1</y> <rows>1</rows> <cols>1</cols> <offset>0</offset> <template>material</template> - <appearance>standard</appearance> + <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>require_title_in_creation</id> + <id>create_case_button_icon_preview</id> <logic> - <behavior>editable</behavior> + <behavior>visible</behavior> </logic> <layout> - <x>3</x> + <x>2</x> <y>1</y> <rows>1</rows> <cols>1</cols> @@ -706,16 +894,52 @@ <x>0</x> <y>2</y> <rows>1</rows> - <cols>4</cols> + <cols>3</cols> <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> </dataGroup> + <event type="assign"> + <id>assign</id> + <title/> + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>header_settings</id> + <x>336</x> + <y>48</y> + <label name="todo 23">Header</label> + <icon>view_column</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> - <id>view_headers</id> - <cols>5</cols> + <id>header_0</id> + <cols>3</cols> <layout>grid</layout> <dataRef> <id>is_header_mode_changeable</id> @@ -746,10 +970,17 @@ </layout> </dataRef> <dataRef> - <id>allow_header_table_mode</id> + <id>use_case_default_headers</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> + <action trigger="set"> + trans: t.this, + use: f.use_case_default_headers, + headers: f.default_headers; + + make headers,editable on trans when { use.value } + make headers,visible on trans when { !use.value } + </action> </logic> <layout> <x>1</x> @@ -762,31 +993,14 @@ </layout> </dataRef> <dataRef> - <id>headers_mode</id> + <id>default_headers</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> <x>2</x> <y>0</y> <rows>1</rows> - <cols>2</cols> - <offset>0</offset> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>headers_default_mode</id> - <logic> - <behavior>editable</behavior> - <behavior>required</behavior> - </logic> - <layout> - <x>4</x> - <y>0</y> - <rows>1</rows> <cols>1</cols> <offset>0</offset> <template>material</template> @@ -794,17 +1008,10 @@ </layout> </dataRef> <dataRef> - <id>use_case_default_headers</id> + <id>allow_header_table_mode</id> <logic> <behavior>editable</behavior> - <action trigger="set"> - trans: t.this, - use: f.use_case_default_headers, - headers: f.default_headers; - - make headers,editable on trans when { use.value } - make headers,visible on trans when { !use.value } - </action> + <behavior>required</behavior> </logic> <layout> <x>0</x> @@ -817,84 +1024,54 @@ </layout> </dataRef> <dataRef> - <id>default_headers</id> + <id>headers_mode</id> <logic> <behavior>editable</behavior> + <behavior>required</behavior> </logic> <layout> <x>1</x> <y>1</y> <rows>1</rows> - <cols>4</cols> - <offset>0</offset> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - </dataGroup> - <dataGroup> - <id>associated_view</id> - <cols>4</cols> - <layout>grid</layout> - <title name="associated_view">Associated view - - view_configuration_type - - editable - - - 0 - 0 - 1 - 4 + 1 0 outline - - 0 - - - view_configuration_type: f.view_configuration_type, - view_configuration_form: f.view_configuration_form, - view_configuration_id: f.view_configuration_id; - - if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { - workflowService.deleteCase(view_configuration_id.value[0]) - } - - if (view_configuration_type.value == null || view_configuration_type.value == "") { - change view_configuration_id value { [] } - change view_configuration_form value { [] } - return - } - - def configurationCase = createCase(view_configuration_type.value + "_configuration") - def initTask = assignTask("initialize", configurationCase) - finishTask(initTask) - configurationCase = workflowService.findOne(configurationCase.stringId) - change view_configuration_id value { [configurationCase.stringId] } - change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } - - - - view_configuration_form + headers_default_mode editable + required - 0 + 2 1 1 - 4 + 1 0 outline + + assign + + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> </transition> <place> <id>initialized</id> @@ -933,4 +1110,32 @@ <destinationId>initialized</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a4</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>filter_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a5</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>next_view_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a6</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>header_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a7</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>create_case_btn_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml index 10c34b150e8..22f4853e17f 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml @@ -25,8 +25,6 @@ <roleRef> <id>default</id> <caseLogic> - <create>false</create> - <delete>false</delete> <view>true</view> </caseLogic> </roleRef> @@ -171,14 +169,6 @@ </data> <!-- VIEW CONFIGURATION DATA --> - <data type="i18n"> - <id>view_header</id> - <title/> - <init name="view_header">Single task view</init> - <component> - <name>divider</name> - </component> - </data> <data type="text"> <id>transition_id</id> <title name="transition_id">Transition id @@ -193,8 +183,6 @@ Súčasný filter Vybrať zobrazenie Nastavenie - Asociované zobrazenie - Zobrazenie jednej úlohy ID prechodu @@ -204,8 +192,6 @@ Aktualisiere die Ansicht mit dem ausgewählten Filter Wählen Sie einen Ansichtstyp Einstellungen - zugehörige Ansicht - Einzelaufgabenansicht Übergangs-ID @@ -215,12 +201,24 @@ 208 hourglass_empty + + admin + + true + + data_sync 368 328 + + system + + true + + settings @@ -232,30 +230,14 @@ admin true + + + + default + true - true - true - - form_title - 4 - grid - - view_header - - visible - - - 0 - 0 - 1 - 4 - - outline - - - view_dataGroup 4 @@ -275,6 +257,14 @@ + + assign_0 + + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> </transition> <place> <id>initialized</id> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml index 66caae1268f..abc71d160a9 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml @@ -25,8 +25,6 @@ <roleRef> <id>default</id> <caseLogic> - <create>false</create> - <delete>false</delete> <view>true</view> </caseLogic> </roleRef> @@ -125,7 +123,7 @@ <name>autocomplete_dynamic</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -134,7 +132,7 @@ updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) </action> <action trigger="get"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -151,7 +149,7 @@ <name>raised</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, update_filter: f.update_filter, contains_filter: f.contains_filter, filter_case: f.filter_case, @@ -175,7 +173,7 @@ <id>filter_case</id> <title/> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, filterHeader: f.filter_header, removeButton: f.remove_filter, contains_filter: f.contains_filter, @@ -224,14 +222,6 @@ </data> <!-- TASK VIEW CONFIGURATION DATA --> - <data type="i18n"> - <id>view_header</id> - <title/> - <init name="view_header">Task view</init> - <component> - <name>divider</name> - </component> - </data> <data type="enumeration_map" immediate="true"> <id>view_search_type</id> <title name="view_search_type">Search type for task view @@ -315,6 +305,24 @@ Show more menu for task item? true + + advanced_content_form + + <component> + <!-- todo 23 headers--> + <name>task-list</name> + </component> + <event type="get"> + <id>get</id> + <actions phase="pre"> + <action> + advanced_content_form: f.advanced_content_form; + change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "header_settings"}.task] } + </action> + </actions> + </event> + </data> <!-- I18NS --> <i18n locale="sk"> @@ -337,9 +345,7 @@ <i18nString name="default_headers_desc">Napríklad: "meta-title,meta-user"</i18nString> <i18nString name="show_more_menu">Zobrazovať menu pre úlohovú položku?</i18nString> <i18nString name="item_settings">Nastavenie položky</i18nString> - <i18nString name="filter_update_title">Filter</i18nString> <i18nString name="filter_header">Súčasný filter</i18nString> - <i18nString name="view_header">Zobrazenie úloh</i18nString> <i18nString name="item_settings_general">Všeobecné</i18nString> <i18nString name="remove_filter">Vymazať filter</i18nString> </i18n> @@ -357,7 +363,6 @@ <i18nString name="use_default_headers">Eigene Kopfzeilen verwenden?</i18nString> <i18nString name="default_headers">Anzuzeigende Attributmenge auswählen</i18nString> <i18nString name="default_headers_desc">Beispiel: "meta-title,meta-user"</i18nString> - <i18nString name="filter_update_title">Filter</i18nString> <i18nString name="filter_header">Aktueller Filter</i18nString> <i18nString name="item_settings_general">Allgemein</i18nString> <i18nString name="update_filter">Aktualisiere die Ansicht mit dem ausgewählten Filter</i18nString> @@ -366,7 +371,6 @@ <i18nString name="view_search_type">Suchmodus im Aufgabenansicht</i18nString> <i18nString name="show_more_menu">"Erweiterte Optionen" Taste bei einzelnen Aufgaben anzeigen</i18nString> <i18nString name="item_settings">Menüeintrageinstellungen</i18nString> - <i18nString name="view_header">Aufgabenansicht</i18nString> <i18nString name="remove_filter">Filter entfernen</i18nString> </i18n> @@ -376,12 +380,24 @@ <y>208</y> <label>initialize [await sync]</label> <icon>hourglass_empty</icon> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>data_sync</id> <x>368</x> <y>328</y> <label>Data sync</label> + <roleRef> + <id>system</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>settings</id> @@ -393,35 +409,94 @@ <id>admin</id> <logic> <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> <view>true</view> - <cancel>true</cancel> - <assign>true</assign> </logic> </roleRef> <dataGroup> - <id>form_title</id> + <id>view_dataGroup</id> <cols>4</cols> <layout>grid</layout> <dataRef> - <id>view_header</id> + <id>view_search_type</id> <logic> - <behavior>visible</behavior> + <behavior>editable</behavior> + <behavior>required</behavior> </logic> <layout> <x>0</x> <y>0</y> <rows>1</rows> + <cols>3</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>show_more_menu</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>3</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>advanced_content_form</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>1</y> + <rows>1</rows> <cols>4</cols> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> </dataGroup> + <event type="assign"> + <id>assign_0</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + </transition> + <transition> + <id>filter_settings</id> + <x>400</x> + <y>48</y> + <label name="todo 23">Filter</label> + <icon>filter_alt</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> <id>filter_update</id> <cols>4</cols> <layout>grid</layout> - <title name="filter_update_title">Filter filter_autocomplete_selection @@ -526,50 +601,50 @@ - - view_dataGroup - 4 - grid - - view_search_type - - editable - required - - - 0 - 0 - 1 - 3 - - outline - - - - show_more_menu - - editable - required - - - 3 - 0 - 1 - 1 - - outline - - - + + assign + + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>header_settings</id> + <x>592</x> + <y>48</y> + <label name="todo 23">Header</label> + <icon>view_column</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> <id>view_headers</id> - <cols>5</cols> + <cols>3</cols> <layout>grid</layout> <dataRef> <id>is_header_mode_changeable</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> <action trigger="set"> trans: t.this, isChangeable: f.is_header_mode_changeable, @@ -594,44 +669,51 @@ </layout> </dataRef> <dataRef> - <id>allow_header_table_mode</id> + <id>use_default_headers</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> + <action trigger="set"> + trans: t.this, + use: f.use_default_headers, + headers: f.default_headers; + + make headers,editable on trans when { use.value } + make headers,visible on trans when { !use.value } + </action> </logic> <layout> <x>1</x> <y>0</y> <rows>1</rows> <cols>1</cols> + <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>headers_mode</id> + <id>default_headers</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> <x>2</x> <y>0</y> <rows>1</rows> - <cols>2</cols> + <cols>1</cols> + <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>headers_default_mode</id> + <id>allow_header_table_mode</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> - <x>4</x> - <y>0</y> + <x>0</x> + <y>1</y> <rows>1</rows> <cols>1</cols> <template>material</template> @@ -639,44 +721,52 @@ </layout> </dataRef> <dataRef> - <id>use_default_headers</id> + <id>headers_mode</id> <logic> <behavior>editable</behavior> - <action trigger="set"> - trans: t.this, - use: f.use_default_headers, - headers: f.default_headers; - - make headers,editable on trans when { use.value } - make headers,visible on trans when { !use.value } - </action> + <behavior>required</behavior> </logic> <layout> - <x>0</x> + <x>1</x> <y>1</y> <rows>1</rows> <cols>1</cols> - <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>default_headers</id> + <id>headers_default_mode</id> <logic> <behavior>editable</behavior> + <behavior>required</behavior> </logic> <layout> - <x>1</x> + <x>2</x> <y>1</y> <rows>1</rows> - <cols>4</cols> - <offset>0</offset> + <cols>1</cols> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> </dataGroup> + <event type="assign"> + <id>assign</id> + <title/> + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> </transition> <place> <id>initialized</id> @@ -715,4 +805,18 @@ <destinationId>initialized</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a4</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>filter_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a5</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>header_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml index b68407bc78a..66f770c7188 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -25,8 +25,6 @@ <roleRef> <id>default</id> <caseLogic> - <create>false</create> - <delete>false</delete> <view>true</view> </caseLogic> </roleRef> @@ -222,13 +220,22 @@ <!-- END OF ASSOCIATED VIEW --> <!-- VIEW CONFIGURATION DATA --> - <data type="i18n"> - <id>view_header</id> + <data type="taskRef"> + <id>advanced_content_form</id> <title/> - <init name="view_header">Ticket view</init> <component> - <name>divider</name> + <!-- todo 23 headers--> + <name>task-list</name> </component> + <event type="get"> + <id>get</id> + <actions phase="pre"> + <action> + advanced_content_form: f.advanced_content_form; + change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "next_view_settings"}.task] } + </action> + </actions> + </event> </data> <!-- END OF VIEW CONFIGURATION DATA --> @@ -240,8 +247,6 @@ <i18nString name="filter_header">Súčasný filter</i18nString> <i18nString name="view_configuration_type">Vybrať zobrazenie</i18nString> <i18nString name="settings">Nastavenie</i18nString> - <i18nString name="associated_view">Asociované zobrazenie</i18nString> - <i18nString name="view_header">Tiketové zobrazenie</i18nString> </i18n> <i18n locale="de"> <i18nString name="filter_autocomplete_selection">Neue Filter auswählen</i18nString> @@ -250,8 +255,6 @@ <i18nString name="update_filter">Aktualisiere die Ansicht mit dem ausgewählten Filter</i18nString> <i18nString name="view_configuration_type">Wählen Sie einen Ansichtstyp</i18nString> <i18nString name="settings">Einstellungen</i18nString> - <i18nString name="associated_view">zugehörige Ansicht</i18nString> - <i18nString name="view_header">Ticketansicht</i18nString> </i18n> <transition> @@ -260,12 +263,24 @@ <y>208</y> <label>initialize [await sync]</label> <icon>hourglass_empty</icon> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>data_sync</id> <x>368</x> <y>328</y> <label>Data sync</label> + <roleRef> + <id>system</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>settings</id> @@ -277,35 +292,66 @@ <id>admin</id> <logic> <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> <view>true</view> - <cancel>true</cancel> - <assign>true</assign> </logic> </roleRef> <dataGroup> - <id>form_title</id> + <id>settings_0</id> <cols>4</cols> <layout>grid</layout> <dataRef> - <id>view_header</id> + <id>advanced_content_form</id> <logic> - <behavior>visible</behavior> + <behavior>editable</behavior> </logic> <layout> <x>0</x> <y>0</y> <rows>1</rows> <cols>4</cols> + <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> </dataGroup> + <event type="assign"> + <id>assign_0</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + </transition> + <transition> + <id>next_view_settings</id> + <x>400</x> + <y>48</y> + <label name="todo 23">Next view</label> + <icon>queue_play_next</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> <id>associated_view</id> <cols>4</cols> <layout>grid</layout> - <title name="associated_view">Associated view view_configuration_type @@ -364,6 +410,22 @@ + + assign + + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> </transition> <place> <id>initialized</id> @@ -402,4 +464,11 @@ <destinationId>initialized</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a4</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>next_view_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file diff --git a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java new file mode 100644 index 00000000000..cd965650498 --- /dev/null +++ b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java @@ -0,0 +1,156 @@ +package com.netgrif.application.engine.menu; + +import com.netgrif.application.engine.TestHelper; +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody; +import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody; +import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; +import com.netgrif.application.engine.petrinet.domain.I18nString; +import com.netgrif.application.engine.petrinet.domain.dataset.Field; +import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; +import com.netgrif.application.engine.workflow.domain.Case; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@ActiveProfiles({"test"}) +@ExtendWith(SpringExtension.class) +public class MenuItemServiceTest { + + @Autowired + private TestHelper testHelper; + + @Autowired + private IMenuItemService menuItemService; + + @BeforeEach + public void beforeEach() { + testHelper.truncateDbs(); + } + + @Test + public void createFilterTest() { + // todo + } + + @Test + public void updateFilterTest() { + // todo + } + + @Test + public void createMenuItemTest() { + // todo + } + + @Test + public void updateMenuItemTest() { + // todo + } + + @Test + public void createOrUpdateMenuItemTest() { + // todo + } + + @Test + public void createOrIgnoreMenuItemTest() { + // todo + } + + @Test + public void findMenuItemByIdentifierTest() { + // todo + } + + @Test + public void findMenuItemByUriAndNameTest() { + // todo + } + + @Test + public void findFolderCaseTest() { + // todo + } + + @Test + public void existsMenuItemTest() { + // todo + } + + @Test + public void moveItemTest() { + // todo + } + + @Test + public void duplicateItemTest() { + // todo + } + + @Test + public void removeChildItemFromParentTest() { + // todo + } + + @Test + public void getMenuItemDataTest() throws TransitionNotExecutableException { + assertThrows(IllegalArgumentException.class, () -> menuItemService.getMenuItemData("wrongCaseId")); + + Case menuItemCase = createDefaultMenuItem("my_menu_item", + new I18nString("This is name", Map.of("sk", "Toto je nazov"))); + + Map<String, List<Field<?>>> resultMap = menuItemService.getMenuItemData(menuItemCase.getStringId()); + assertEquals(3, resultMap.size()); + assertTrue(resultMap.containsKey("menu_item")); + assertTrue(resultMap.containsKey("tabbed_case_view")); + assertTrue(resultMap.containsKey("tabbed_task_view")); + } + + @Test + public void getAvailableViewsAsOptionsByIsPrimaryTest() { + // todo + } + + @Test + public void getAvailableViewsAsOptionsByViewIdentifierTest() { + // todo + } + + private Case createDefaultMenuItem(String identifier, I18nString name) throws TransitionNotExecutableException { + FilterBody filterBody = new FilterBody(); + filterBody.setTitle(new I18nString("My case view filter")); + filterBody.setQuery("processIdentifier:process1"); + filterBody.setType("Case"); + filterBody.setAllowedNets(List.of("process1")); + filterBody.setIcon("home"); + filterBody.setVisibility("private"); + + TabbedCaseViewBody caseView = new TabbedCaseViewBody(); + caseView.setFilterBody(filterBody); + caseView.setRequireTitleInCreation(false); + caseView.setChainedView(new TabbedTaskViewBody()); + + MenuItemBody menuItemBody = new MenuItemBody(); + menuItemBody.setUri("/"); + menuItemBody.setIdentifier(identifier); + menuItemBody.setMenuIcon("home"); + menuItemBody.setMenuName(name); + menuItemBody.setTabIcon("folder"); + menuItemBody.setTabName(name); + menuItemBody.setView(caseView); + + return menuItemService.createMenuItem(menuItemBody); + } +} From 21fd5c38bfcf7240da38bd893a7dd862d79b29e0 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Thu, 7 May 2026 11:41:20 +0200 Subject: [PATCH 05/49] [ETASK-23] Dynamic view configuration - fix resolving menu item in the menu - add todos --- .../engine/menu/domain/MenuItemBody.java | 2 + .../engine/menu/domain/MenuItemConstants.java | 2 + .../menu/domain/configurations/ViewBody.java | 2 + .../domain/configurations/ViewConstants.java | 2 + .../engine/menu/service/MenuItemService.java | 37 +--- .../service/interfaces/IMenuItemService.java | 5 +- .../engine/menu/web/MenuController.java | 14 +- .../responsebodies/MenuItemDataResponse.java | 10 +- .../engine-processes/menu/menu_item.xml | 174 +++++++++++++++++- .../menu/tabbed_case_view_configuration.xml | 149 +++++++++++++++ .../menu/tabbed_task_view_configuration.xml | 106 +++++++++++ .../menu/tabbed_ticket_view_configuration.xml | 35 ++++ .../engine/menu/MenuItemServiceTest.java | 24 ++- 13 files changed, 507 insertions(+), 55 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java index 9637f9ddeb8..5af588b2fa3 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -181,6 +181,8 @@ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCas List.of(viewCase.getStringId())); String taskId = MenuItemUtils.findTaskIdInCase(viewCase, ViewConstants.TRANS_SETTINGS_ID); outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_FORM, FieldType.TASK_REF, List.of(taskId)); + String allDataTaskId = MenuItemUtils.findTaskIdInCase(viewCase, ViewConstants.TRANS_ALL_MENU_DATA_ID); + outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM, FieldType.TASK_REF, List.of(allDataTaskId)); } return outcome; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java index 0c1912a69fc..320c0f08ebf 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java @@ -28,8 +28,10 @@ public class MenuItemConstants { public static final String FIELD_IS_AUTO_SELECT = "is_auto_select"; public static final String FIELD_VIEW_CONFIGURATION_ID = "view_configuration_id"; public static final String FIELD_VIEW_CONFIGURATION_FORM = "view_configuration_form"; + public static final String FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM = "view_configuration_all_data_form"; public static final String TRANS_SETTINGS_ID = "item_settings"; public static final String TRANS_INIT_ID = "system_initialize"; public static final String TRANS_SYNC_ID = "data_sync"; + public static final String TRANS_ALL_MENU_DATA = "all_menu_data"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java index d636b6e60b1..7496fa45998 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java @@ -73,6 +73,8 @@ public ToDataSetOutcome toDataSet(Case associatedViewCase, Case filterCase) { List.of(associatedViewCase.getStringId())); String taskId = MenuItemUtils.findTaskIdInCase(associatedViewCase, ViewConstants.TRANS_SETTINGS_ID); outcome.putDataSetEntry(ViewConstants.FIELD_VIEW_CONFIGURATION_FORM, FieldType.TASK_REF, List.of(taskId)); + String allDataTaskId = MenuItemUtils.findTaskIdInCase(associatedViewCase, ViewConstants.TRANS_ALL_MENU_DATA_ID); + outcome.putDataSetEntry(ViewConstants.FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM, FieldType.TASK_REF, List.of(allDataTaskId)); } if (filterCase != null) { outcome.putDataSetEntry(ViewConstants.FIELD_VIEW_FILTER_CASE, FieldType.CASE_REF, List.of(filterCase.getStringId())); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java index 74c38803f06..c0a904aa9b5 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java @@ -7,10 +7,12 @@ public class ViewConstants { public static final String FIELD_CONFIGURATION_TYPE = "view_configuration_type"; public static final String FIELD_VIEW_CONFIGURATION_ID = "view_configuration_id"; public static final String FIELD_VIEW_CONFIGURATION_FORM = "view_configuration_form"; + public static final String FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM = "view_configuration_all_data_form"; public static final String FIELD_VIEW_CONTAINS_FILTER = "contains_filter"; public static final String FIELD_VIEW_FILTER_CASE = "filter_case"; public static final String TRANS_INIT_ID = "initialize"; public static final String TRANS_SETTINGS_ID = "settings"; + public static final String TRANS_ALL_MENU_DATA_ID = "all_menu_data"; public static final String TRANS_SYNC_ID = "data_sync"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index 4c8f3d9ae65..708c05eb4a8 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -13,10 +13,10 @@ import com.netgrif.application.engine.menu.domain.configurations.ViewConstants; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.menu.utils.MenuItemUtils; +import com.netgrif.application.engine.petrinet.domain.DataGroup; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.UriContentType; import com.netgrif.application.engine.petrinet.domain.UriNode; -import com.netgrif.application.engine.petrinet.domain.dataset.Field; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.petrinet.service.interfaces.IUriService; @@ -24,7 +24,6 @@ import com.netgrif.application.engine.startup.FilterRunner; import com.netgrif.application.engine.startup.ImportHelper; import com.netgrif.application.engine.workflow.domain.Case; -import com.netgrif.application.engine.workflow.domain.DataField; import com.netgrif.application.engine.workflow.domain.Task; import com.netgrif.application.engine.workflow.service.interfaces.IDataService; import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; @@ -406,42 +405,16 @@ public Case removeChildItemFromParent(String folderId, Case childItem) { * Retrieves menu item data along with its associated view configuration data. The method collects immediate * data from the menu item case and recursively traverses through all associated view configuration cases, * aggregating their immediate data into a single map. - * + * todo 23 doc * @param caseId identifier of the menu item case * @return map where keys are process identifiers (with "_configuration" suffix removed for view cases) and * values are lists of immediate data fields from the corresponding cases */ @Override - public Map<String, List<Field<?>>> getMenuItemData(String caseId) { + public List<DataGroup> getMenuItemData(String caseId, Locale locale) { Case menuItemCase = workflowService.findOne(caseId); - Map<String, List<Field<?>>> immediateDataMap = new HashMap<>(); - immediateDataMap.put(menuItemCase.getProcessIdentifier(), menuItemCase.getImmediateData()); - Optional<String> viewCaseIdOpt = getNextViewCaseId(menuItemCase); - if (viewCaseIdOpt.isEmpty()) { - return immediateDataMap; - } - - do { - Case viewCase = workflowService.findOne(viewCaseIdOpt.get()); - immediateDataMap.put(viewCase.getProcessIdentifier().replace("_configuration", ""), - viewCase.getImmediateData()); - viewCaseIdOpt = getNextViewCaseId(viewCase); - } while (viewCaseIdOpt.isPresent()); - - return immediateDataMap; - } - - protected Optional<String> getNextViewCaseId(Case parentCase) { - DataField viewConfigurationIdDataField = parentCase.getDataField(MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID); - if (viewConfigurationIdDataField == null) { - return Optional.empty(); - } - @SuppressWarnings("unchecked") - List<String> viewConfigurationIdValue = (List<String>) viewConfigurationIdDataField.getValue(); - if (viewConfigurationIdValue == null || viewConfigurationIdValue.isEmpty()) { - return Optional.empty(); - } - return Optional.ofNullable(viewConfigurationIdValue.get(0)); + String taskId = MenuItemUtils.findTaskIdInCase(menuItemCase, MenuItemConstants.TRANS_ALL_MENU_DATA); + return dataService.getDataGroups(taskId, locale).getData(); } protected Case findCase(String processIdentifier, String query) { diff --git a/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java index 929f348cf10..90cd699331b 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java @@ -3,14 +3,15 @@ import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.petrinet.domain.DataGroup; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.UriNode; -import com.netgrif.application.engine.petrinet.domain.dataset.Field; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.workflow.domain.Case; import com.netgrif.application.engine.petrinet.domain.dataset.MapOptionsField; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; @@ -29,7 +30,7 @@ public interface IMenuItemService { void moveItem(Case item, String destUri) throws TransitionNotExecutableException; Case duplicateItem(Case originItem, I18nString newTitle, String newIdentifier) throws TransitionNotExecutableException; Case removeChildItemFromParent(String folderId, Case childItem); - Map<String, List<Field<?>>> getMenuItemData(String caseId); + List<DataGroup> getMenuItemData(String caseId, Locale locale); /** * Gets all tabbed or non-tabbed views diff --git a/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java b/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java index 27ffee572e9..32e32f3fdde 100644 --- a/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java +++ b/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java @@ -2,7 +2,7 @@ import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.menu.web.responsebodies.MenuItemDataResponse; -import com.netgrif.application.engine.petrinet.domain.dataset.Field; +import com.netgrif.application.engine.petrinet.domain.DataGroup; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; @@ -12,13 +12,14 @@ import org.springframework.hateoas.MediaTypes; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ResponseStatusException; import java.util.Base64; import java.util.List; -import java.util.Map; +import java.util.Locale; @Slf4j @RestController @@ -29,17 +30,20 @@ public class MenuController { private final IMenuItemService menuItemService; + // todo 23 menu item authorization @Operation(summary = "Get relevant data for the menu item", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/{encodedCaseId}", produces = MediaTypes.HAL_JSON_VALUE) - public EntityModel<MenuItemDataResponse> getMenuItemData(String encodedCaseId) { + public EntityModel<MenuItemDataResponse> getMenuItemData(@PathVariable("encodedCaseId") String encodedCaseId, Locale locale) { try { String caseId = new String(Base64.getDecoder().decode(encodedCaseId)); - Map<String, List<Field<?>>> immediateDataMap = menuItemService.getMenuItemData(caseId); - return EntityModel.of(new MenuItemDataResponse(immediateDataMap)); + List<DataGroup> dataGroups = menuItemService.getMenuItemData(caseId, locale); + return EntityModel.of(new MenuItemDataResponse(dataGroups)); } catch (Exception e) { log.error("Getting menu item data failed", e); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Getting menu item data failed", e); } } + + // todo 23 search with authorization } diff --git a/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java b/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java index 43f265a9547..78e7851e573 100644 --- a/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java +++ b/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java @@ -1,12 +1,10 @@ package com.netgrif.application.engine.menu.web.responsebodies; -import com.netgrif.application.engine.petrinet.domain.dataset.Field; +import com.netgrif.application.engine.petrinet.domain.DataGroup; import lombok.Getter; import lombok.RequiredArgsConstructor; -import java.util.HashMap; import java.util.List; -import java.util.Map; @RequiredArgsConstructor public class MenuItemDataResponse { @@ -15,9 +13,5 @@ public class MenuItemDataResponse { * Map containing menu item data where key is the view type and value is a list of immediate fields. */ @Getter - private final Map<String, List<Field<?>>> data; - - public MenuItemDataResponse() { - this(new HashMap<>()); - } + private final List<DataGroup> data; } diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 1f53c563974..d9e702304d2 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -380,7 +380,11 @@ <action trigger="get"> processes: f.this; - change processes options { return configurableMenuService.getNetsByAuthorAsMapOptions(loggedUser(), org.springframework.context.i18n.LocaleContextHolder.locale) } + try { + change processes options { return configurableMenuService.getNetsByAuthorAsMapOptions(loggedUser(), org.springframework.context.i18n.LocaleContextHolder.locale) } + } catch (Exception e) { + log.error("Cannot resolve processes: ", e) + } </action> <action trigger="set"> processes: f.this, @@ -509,6 +513,10 @@ <id>view_configuration_form</id> <title/> </data> + <data type="taskRef"> + <id>view_configuration_all_data_form</id> + <title/> + </data> <data type="button"> <id>order_down</id> <title/> @@ -727,6 +735,7 @@ <x>340</x> <y>220</y> <label name="initialize">Create item</label> +<!-- todo 23 tags visible--> <icon>hourglass_empty</icon> <roleRef> <id>admin</id> @@ -1601,6 +1610,7 @@ <action> view_configuration_type: f.view_configuration_type, view_configuration_form: f.view_configuration_form, + view_configuration_all_data_form: f.view_configuration_all_data_form, view_configuration_id: f.view_configuration_id; if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { @@ -1610,6 +1620,7 @@ if (view_configuration_type.value == null || view_configuration_type.value == "") { change view_configuration_id value { [] } change view_configuration_form value { [] } + change view_configuration_all_data_form value { [] } return } @@ -1619,6 +1630,7 @@ configurationCase = workflowService.findOne(configurationCase.stringId) change view_configuration_id value { [configurationCase.stringId] } change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } + change view_configuration_all_data_form value { [configurationCase.tasks.find { it.transition == "all_menu_data" }.task] } </action> </actions> </event> @@ -1655,6 +1667,166 @@ <title/> </event> </transition> + <transition> + <id>all_menu_data</id> + <x>10</x> + <y>10</y> + <label>All menu data</label> + <roleRef> + <id>system</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>all_menu_data_0</id> + <layout>flow</layout> + <dataRef> + <id>menu_item_identifier</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>nodePath</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>menu_name</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>menu_icon</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>menu_icon_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>hasChildren</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>is_auto_select</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>processes_available</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>roles_available</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>add_allowed_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>allowed_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>remove_allowed_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>add_banned_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>banned_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>remove_banned_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>use_custom_view</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>custom_view_selector</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>use_tabbed_view</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>tab_name</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>use_tab_icon</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>tab_icon</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>tab_icon_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_configuration_type</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_configuration_all_data_form</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + </dataGroup> + </transition> <!-- PLACES--> <place> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 39740f21fce..37ce910b0e3 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -390,6 +390,10 @@ <id>view_configuration_form</id> <title/> </data> + <data type="taskRef"> + <id>view_configuration_all_data_form</id> + <title/> + </data> <data type="taskRef"> <id>advanced_content_form</id> <title/> @@ -733,6 +737,7 @@ <action> view_configuration_type: f.view_configuration_type, view_configuration_form: f.view_configuration_form, + view_configuration_all_data_form: f.view_configuration_all_data_form, view_configuration_id: f.view_configuration_id; if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { @@ -742,6 +747,7 @@ if (view_configuration_type.value == null || view_configuration_type.value == "") { change view_configuration_id value { [] } change view_configuration_form value { [] } + change view_configuration_all_data_form value { [] } return } @@ -751,6 +757,7 @@ configurationCase = workflowService.findOne(configurationCase.stringId) change view_configuration_id value { [configurationCase.stringId] } change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } + change view_configuration_all_data_form value { [configurationCase.tasks.find { it.transition == "all_menu_data" }.task] } </action> </actions> </event> @@ -1073,6 +1080,148 @@ <title/> </event> </transition> + <transition> + <id>all_menu_data</id> + <x>10</x> + <y>10</y> + <label>All menu data</label> + <roleRef> + <id>system</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>all_menu_data_0</id> + <layout>flow</layout> + <dataRef> + <id>filter_autocomplete_selection</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>update_filter</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>selected_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>filter_header</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>current_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_search_type</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>show_create_case_button</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>show_more_menu</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>create_case_button_title</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>create_case_button_icon</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>create_case_button_icon_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>require_title_in_creation</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>banned_nets_in_creation</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>is_header_mode_changeable</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>allow_header_table_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>headers_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>headers_default_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>use_case_default_headers</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>default_headers</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_configuration_type</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_configuration_all_data_form</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + </dataGroup> + </transition> <place> <id>initialized</id> <x>496</x> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml index abc71d160a9..9ad685ae90a 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml @@ -768,6 +768,112 @@ <title/> </event> </transition> + <transition> + <id>all_menu_data</id> + <x>10</x> + <y>10</y> + <label>All menu data</label> + <roleRef> + <id>system</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>all_menu_data_0</id> + <layout>flow</layout> + <dataRef> + <id>filter_autocomplete_selection</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>update_filter</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>selected_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>filter_header</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>current_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>merge_filters</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>remove_filter</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_search_type</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>show_more_menu</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>is_header_mode_changeable</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>allow_header_table_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>headers_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>headers_default_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>use_default_headers</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>default_headers</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + </dataGroup> + </transition> <place> <id>initialized</id> <x>496</x> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml index 66f770c7188..35dc6a471d5 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -217,6 +217,10 @@ <id>view_configuration_form</id> <title/> </data> + <data type="taskRef"> + <id>view_configuration_all_data_form</id> + <title/> + </data> <!-- END OF ASSOCIATED VIEW --> <!-- VIEW CONFIGURATION DATA --> @@ -372,6 +376,7 @@ <action> view_configuration_type: f.view_configuration_type, view_configuration_form: f.view_configuration_form, + view_configuration_all_data_form: f.view_configuration_all_data_form, view_configuration_id: f.view_configuration_id; if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { @@ -381,6 +386,7 @@ if (view_configuration_type.value == null || view_configuration_type.value == "") { change view_configuration_id value { [] } change view_configuration_form value { [] } + change view_configuration_all_data_form value { [] } return } @@ -390,6 +396,7 @@ change view_configuration_id value { [configurationCase.stringId] } configurationCase = workflowService.findOne(configurationCase.stringId) change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } + change view_configuration_all_data_form value { [configurationCase.tasks.find { it.transition == "all_menu_data" }.task] } </action> </actions> </event> @@ -427,6 +434,34 @@ <title/> </event> </transition> + <transition> + <id>all_menu_data</id> + <x>10</x> + <y>10</y> + <label>All menu data</label> + <roleRef> + <id>system</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>all_menu_data_0</id> + <layout>flow</layout> + <dataRef> + <id>view_configuration_type</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_configuration_all_data_form</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + </dataGroup> + </transition> <place> <id>initialized</id> <x>496</x> diff --git a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java index cd965650498..0f711caf38c 100644 --- a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java +++ b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java @@ -7,18 +7,22 @@ import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.petrinet.domain.I18nString; -import com.netgrif.application.engine.petrinet.domain.dataset.Field; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; +import com.netgrif.application.engine.startup.SuperCreator; import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.domain.eventoutcomes.dataoutcomes.GetDataGroupsEventOutcome; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.List; +import java.util.Locale; import java.util.Map; import static org.junit.jupiter.api.Assertions.*; @@ -33,6 +37,9 @@ public class MenuItemServiceTest { @Autowired private IMenuItemService menuItemService; + + @Autowired + private SuperCreator superCreator; @BeforeEach public void beforeEach() { @@ -106,16 +113,15 @@ public void removeChildItemFromParentTest() { @Test public void getMenuItemDataTest() throws TransitionNotExecutableException { - assertThrows(IllegalArgumentException.class, () -> menuItemService.getMenuItemData("wrongCaseId")); + // todo 23 fix test + assertThrows(IllegalArgumentException.class, () -> menuItemService.getMenuItemData("wrongCaseId", Locale.getDefault())); Case menuItemCase = createDefaultMenuItem("my_menu_item", new I18nString("This is name", Map.of("sk", "Toto je nazov"))); - Map<String, List<Field<?>>> resultMap = menuItemService.getMenuItemData(menuItemCase.getStringId()); - assertEquals(3, resultMap.size()); - assertTrue(resultMap.containsKey("menu_item")); - assertTrue(resultMap.containsKey("tabbed_case_view")); - assertTrue(resultMap.containsKey("tabbed_task_view")); + login(); + GetDataGroupsEventOutcome result = menuItemService.getMenuItemData(menuItemCase.getStringId(), Locale.getDefault()); + assertTrue(result != null); } @Test @@ -153,4 +159,8 @@ private Case createDefaultMenuItem(String identifier, I18nString name) throws Tr return menuItemService.createMenuItem(menuItemBody); } + + private void login() { + SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(superCreator.getLoggedSuper(), null)); + } } From 341ea391c58fde41533c059a7ce5004b9cef0034 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Mon, 11 May 2026 09:17:50 +0200 Subject: [PATCH 06/49] [ETASK-23] Dynamic view configuration - fix changing menu item by ActionDelegate - fix menu_item.manageBehaviorOfTabFields - fix menu_item move_add_node set action - improve menu_item initialization when creating by forms - fix next view selection in ticket view --- .../logic/action/ActionDelegate.groovy | 10 ++++----- .../engine-processes/menu/menu_item.xml | 7 +++++- .../tabbed_single_task_view_configuration.xml | 22 +++++++++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index d4b527dcb6c..9b8b0c338bd 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -1899,31 +1899,31 @@ class ActionDelegate { title : { cl -> def value = cl() I18nString newName = (value instanceof I18nString) ? value : new I18nString(value as String) - setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + setData(MenuItemConstants.TRANS_SYNC_ID, item, [ (MenuItemConstants.FIELD_MENU_NAME): ["type": "i18n", "value": newName] ]) }, menuIcon : { cl -> def value = cl() - setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + setData(MenuItemConstants.TRANS_SYNC_ID, item, [ (MenuItemConstants.FIELD_MENU_ICON): ["type": "text", "value": value] ]) }, tabIcon : { cl -> def value = cl() - setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + setData(MenuItemConstants.TRANS_SYNC_ID, item, [ (MenuItemConstants.FIELD_TAB_ICON): ["type": "text", "value": value] ]) }, useCustomView : { cl -> def value = cl() - setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + setData(MenuItemConstants.TRANS_SYNC_ID, item, [ (MenuItemConstants.FIELD_USE_CUSTOM_VIEW): ["type": "boolean", "value": value] ]) }, customViewSelector: { cl -> def value = cl() - setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + setData(MenuItemConstants.TRANS_SYNC_ID, item, [ (MenuItemConstants.FIELD_CUSTOM_VIEW_SELECTOR): ["type": "text", "value": value] ]) }] diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index d9e702304d2..fc9bdfddbc6 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -93,7 +93,7 @@ } </function> <function scope="process" name="manageBehaviorOfTabFields"> - { boolean useTabIcon, boolean useTabbedView, String transId = "item_settings" -> + { boolean useTabIcon, boolean useTabbedView, String transId = "view_settings" -> def settingsTrans = useCase.petriNet.transitions[transId] make [useCase.getField("use_tab_icon"), useCase.getField("tab_icon"), useCase.getField("tab_name")], @@ -193,6 +193,7 @@ prefixUri = prefixUri.replace("//","/") String newUri = prefixUri + uriService.getUriSeparator() + newNodeName.value + newUri = newUri.replace("//","/") def newNode = uriService.getOrCreate(newUri, com.netgrif.application.engine.petrinet.domain.UriContentType.CASE) change selectedUri value { splitUriPath(newNode.uriPath) } @@ -737,6 +738,7 @@ <label name="initialize">Create item</label> <!-- todo 23 tags visible--> <icon>hourglass_empty</icon> + <assignPolicy>auto</assignPolicy> <roleRef> <id>admin</id> <logic> @@ -810,12 +812,15 @@ <id>finish</id> <actions phase="pre"> <action> + name: f.menu_name, + identifier: f.menu_item_identifier, dest: f.move_dest_uri; String newUri = dest.value.join("/") newUri = newUri.replace("//","/") changeMenuItem useCase uri { newUri } + change name value { new com.netgrif.application.engine.petrinet.domain.I18nString(identifier.value) } </action> </actions> <title name="create_item_finish">Create diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml index 22f4853e17f..2c869531c05 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml @@ -266,6 +266,28 @@ </event> </transition> + <transition> + <id>all_menu_data</id> + <x>10</x> + <y>10</y> + <label>All menu data</label> + <roleRef> + <id>system</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>all_menu_data_0</id> + <layout>flow</layout> + <dataRef> + <id>transition_id</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + </dataGroup> + </transition> <place> <id>initialized</id> <x>496</x> From 400bf4307fd2575e757cd779d434eeb8dbbf9ae3 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Mon, 11 May 2026 13:15:17 +0200 Subject: [PATCH 07/49] [ETASK-23] Dynamic view configuration - make single task view as a primary view - update init values in menu_item.xml - update tabbed_single_task_view_configuration configurations --- .../engine/menu/domain/MenuItemView.java | 2 +- .../engine-processes/menu/menu_item.xml | 14 +- .../menu/tabbed_case_view_configuration.xml | 2 + .../tabbed_single_task_view_configuration.xml | 235 ++++++++++++++++-- .../menu/tabbed_task_view_configuration.xml | 10 +- .../menu/tabbed_ticket_view_configuration.xml | 106 +------- 6 files changed, 239 insertions(+), 130 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 1bdf5a43d54..6a1ba78b206 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -23,7 +23,7 @@ public enum MenuItemView { "de", "Ticketansicht mit Registerkarten")), "tabbed_ticket_view", List.of("tabbed_single_task_view"), true, true), TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of("sk", "Zobrazenie jednej úlohy v taboch", - "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true, false); + "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true, true); private final I18nString name; private final String identifier; diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index fc9bdfddbc6..5c1d8f85c3a 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -448,6 +448,7 @@ <data type="boolean" immediate="true"> <id>use_tabbed_view</id> <title name="use_tabbed_view">Do you want to use view with tabs? + true @@ -490,7 +491,7 @@ view_configuration_type Pick view type - menuItemService.getAvailableViewsAsOptions(false, true) + menuItemService.getAvailableViewsAsOptions(true, true) use_tabbed_view: f.use_tabbed_view, @@ -508,6 +509,7 @@ tabbed_case_view_configuration tabbed_task_view_configuration tabbed_ticket_view_configuration + tabbed_single_task_view_configuration @@ -1532,7 +1534,7 @@ tab_name - hidden + editable 2 @@ -1546,7 +1548,7 @@ use_tab_icon - hidden + editable 3 @@ -1571,7 +1573,7 @@ tab_icon - hidden + editable 0 @@ -1585,7 +1587,7 @@ tab_icon_preview - hidden + visible 1 @@ -1686,6 +1688,8 @@ all_menu_data_0 flow + start + true menu_item_identifier diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 37ce910b0e3..14626ff6bab 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -1094,6 +1094,8 @@ all_menu_data_0 flow + start + true filter_autocomplete_selection diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml index 2c869531c05..6a346161c40 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml @@ -53,7 +53,7 @@ } else { change filterAutocomplete options { def findAllPredicate = { filterCase -> !selectedFilterRef.value.contains(filterCase.stringId) - && filterCase.dataSet["filter_type"].value == "Case" } + && filterCase.dataSet["filter_type"].value == "Task" } return findFilters(filterAutocomplete.value != null ? filterAutocomplete.value : "") .findAll(findAllPredicate) .collectEntries({filterCase -> [filterCase.stringId, filterCase.title]}) @@ -70,7 +70,7 @@ </data> <data type="taskRef"> - <id>current_filter_preview</id> + <id>current_task_filter_preview</id> <title/> </data> <data type="i18n"> @@ -97,7 +97,7 @@ <name>autocomplete_dynamic</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -106,7 +106,7 @@ updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) </action> <action trigger="get"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -123,7 +123,7 @@ <name>raised</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, update_filter: f.update_filter, contains_filter: f.contains_filter, filter_case: f.filter_case, @@ -147,7 +147,7 @@ <id>filter_case</id> <title/> <action trigger="set"> - filterTaskRef: f.current_filter_preview, + filterTaskRef: f.current_task_filter_preview, contains_filter: f.contains_filter, filterCaseRef: f.filter_case; @@ -167,13 +167,33 @@ <allowedNet>filter</allowedNet> </allowedNets> </data> - - <!-- VIEW CONFIGURATION DATA --> - <data type="text"> - <id>transition_id</id> - <title name="transition_id">Transition id + + advanced_content_form + + <component> + <!-- todo 23 headers--> + <name>task-list</name> + </component> + <event type="get"> + <id>get</id> + <actions phase="pre"> + <action> + advanced_content_form: f.advanced_content_form; + change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task] } + </action> + </actions> + </event> + </data> + <data type="boolean"> + <id>show_page_header</id> + <title name="todo 23">Show page header + true + + + show_page_footer + Show page footer + false - @@ -183,7 +203,6 @@ Súčasný filter Vybrať zobrazenie Nastavenie - ID prechodu Neue Filter auswählen @@ -192,7 +211,6 @@ Aktualisiere die Ansicht mit dem ausgewählten Filter Wählen Sie einen Ansichtstyp Einstellungen - Übergangs-ID @@ -226,6 +244,7 @@ 112 settings + admin @@ -243,7 +262,7 @@ 4 grid - transition_id + show_page_header editable @@ -251,6 +270,34 @@ 0 0 1 + 2 + + outline + + + + show_page_footer + + editable + + + 2 + 0 + 1 + 2 + + outline + + + + advanced_content_form + + editable + + + 0 + 1 + 1 4 outline @@ -266,6 +313,117 @@ </event> </transition> + <transition> + <id>filter_settings</id> + <x>432</x> + <y>48</y> + <label name="todo 23">Filter</label> + <icon>filter_alt</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>filter_0</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>filter_autocomplete_selection</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>3</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>update_filter</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>3</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>standard</appearance> + </layout> + </dataRef> + <dataRef> + <id>selected_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>1</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>standard</appearance> + </layout> + </dataRef> + <dataRef> + <id>filter_header</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>2</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>current_task_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>3</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>standard</appearance> + </layout> + </dataRef> + </dataGroup> + <event type="assign"> + <id>assign</id> + <title/> + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> <transition> <id>all_menu_data</id> <x>10</x> @@ -280,8 +438,46 @@ <dataGroup> <id>all_menu_data_0</id> <layout>flow</layout> + <alignment>start</alignment> + <stretch>true</stretch> + <dataRef> + <id>show_page_header</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>show_page_footer</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>filter_autocomplete_selection</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>update_filter</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>selected_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> <dataRef> - <id>transition_id</id> + <id>filter_header</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>current_task_filter_preview</id> <logic> <behavior>visible</behavior> </logic> @@ -325,4 +521,11 @@ <destinationId>initialized</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a4</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>filter_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml index 9ad685ae90a..e89d84d1448 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml @@ -96,7 +96,7 @@ <title/> </data> <data type="taskRef"> - <id>current_filter_preview</id> + <id>current_task_filter_preview</id> <title/> </data> <data type="i18n"> @@ -178,7 +178,7 @@ removeButton: f.remove_filter, contains_filter: f.contains_filter, mergeFilters: f.merge_filters, - filterTaskRef: f.current_filter_preview, + filterTaskRef: f.current_task_filter_preview, filterCaseRef: f.filter_case; if (filterCaseRef.value == null || filterCaseRef.value == []) { @@ -559,7 +559,7 @@ </layout> </dataRef> <dataRef> - <id>current_filter_preview</id> + <id>current_task_filter_preview</id> <logic> <behavior>visible</behavior> </logic> @@ -782,6 +782,8 @@ <dataGroup> <id>all_menu_data_0</id> <layout>flow</layout> + <alignment>start</alignment> + <stretch>true</stretch> <dataRef> <id>filter_autocomplete_selection</id> <logic> @@ -807,7 +809,7 @@ </logic> </dataRef> <dataRef> - <id>current_filter_preview</id> + <id>current_task_filter_preview</id> <logic> <behavior>visible</behavior> </logic> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml index 35dc6a471d5..32fc9e33a2b 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -87,110 +87,6 @@ } </function> - <!-- FILTER configuration data --> - <data type="taskRef"> - <id>selected_filter_preview</id> - <title/> - </data> - <data type="taskRef"> - <id>current_filter_preview</id> - <title/> - </data> - <data type="i18n"> - <id>filter_header</id> - <title/> - <init name="filter_header">Current filter</init> - <component> - <name>divider</name> - </component> - </data> - <data type="text"> - <id>new_filter_id</id> - <title/> - </data> - <data type="boolean" immediate="true"> - <id>contains_filter</id> - <title/> - <init>false</init> - </data> - <data type="enumeration_map"> - <id>filter_autocomplete_selection</id> - <title name="filter_autocomplete_selection">Select new filter - - autocomplete_dynamic - - - trans: t.settings, - filterAutocomplete: f.this, - filter_case: f.filter_case, - update_filter: f.update_filter, - previewTaskRef: f.selected_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) - - - trans: t.settings, - filterAutocomplete: f.this, - filter_case: f.filter_case, - update_filter: f.update_filter, - previewTaskRef: f.selected_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) - - - - update_filter - - <placeholder name="update_filter">Update view with selected filter</placeholder> - <component> - <name>raised</name> - </component> - <action trigger="set"> - trans: t.settings, - update_filter: f.update_filter, - contains_filter: f.contains_filter, - filter_case: f.filter_case, - filterAutocomplete: f.filter_autocomplete_selection; - - boolean containsFilter = filterAutocomplete.value != null && filterAutocomplete.value != "" - if (containsFilter) { - def filterCase = findCase({it._id.eq(filterAutocomplete.value)}) - if (filterCase.dataSet["filter_type"].value != "Case") { - throw new IllegalArgumentException("Filter is of wrong type. Only filter of Case type allowed.") - } - } - - change contains_filter value { containsFilter } - change filter_case value { [filterAutocomplete.value] } - change filterAutocomplete value { "" } - make update_filter,visible on trans when { true } - </action> - </data> - <data type="caseRef"> - <id>filter_case</id> - <title/> - <action trigger="set"> - filterTaskRef: f.current_filter_preview, - contains_filter: f.contains_filter, - filterCaseRef: f.filter_case; - - if (filterCaseRef.value == null || filterCaseRef.value == []) { - change filterTaskRef value { [] } - change contains_filter value { false } - return - } - - def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) - change filterTaskRef value { - return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId] - } - change contains_filter value { true } - </action> - <allowedNets> - <allowedNet>filter</allowedNet> - </allowedNets> - </data> - <!-- ASSOCIATED VIEW: this section can be removed if needed --> <data type="enumeration_map" immediate="true"> <id>view_configuration_type</id> @@ -448,6 +344,8 @@ <dataGroup> <id>all_menu_data_0</id> <layout>flow</layout> + <alignment>start</alignment> + <stretch>true</stretch> <dataRef> <id>view_configuration_type</id> <logic> From f537cb1f448e0b51cd087f3c2448aee7802bc325 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Mon, 11 May 2026 14:40:45 +0200 Subject: [PATCH 08/49] [ETASK-23] Dynamic view configuration - make single task view as untabbed too --- .../engine/menu/domain/MenuItemView.java | 30 +++++++++++-------- ...kViewBody.java => SingleTaskViewBody.java} | 10 ++++--- .../SingleTaskViewConstants.java | 6 ++++ .../TabbedSingleTaskViewConstants.java | 5 ---- .../engine-processes/menu/menu_item.xml | 2 +- ...xml => single_task_view_configuration.xml} | 4 +-- .../menu/tabbed_ticket_view_configuration.xml | 2 +- .../resources/dashboard_management_test.xml | 2 +- 8 files changed, 34 insertions(+), 27 deletions(-) rename src/main/java/com/netgrif/application/engine/menu/domain/configurations/{TabbedSingleTaskViewBody.java => SingleTaskViewBody.java} (61%) create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewConstants.java delete mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java rename src/main/resources/petriNets/engine-processes/menu/{tabbed_single_task_view_configuration.xml => single_task_view_configuration.xml} (99%) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 6a1ba78b206..ddffbb2dcb2 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -14,16 +14,18 @@ * */ @Getter public enum MenuItemView { - TABBED_CASE_VIEW(new I18nString("Tabbed case view", Map.of("sk", "Zobrazenie prípadov v taboch", - "de", "Fallansicht mit Registerkarten")), "tabbed_case_view", List.of("tabbed_task_view"), - true, true), - TABBED_TASK_VIEW(new I18nString("Tabbed task view", Map.of("sk", "Zobrazenie úloh v taboch", - "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true, true), - TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of("sk", "Tiketové zobrazenie v taboch", - "de", "Ticketansicht mit Registerkarten")), "tabbed_ticket_view", - List.of("tabbed_single_task_view"), true, true), - TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of("sk", "Zobrazenie jednej úlohy v taboch", - "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true, true); + TABBED_CASE_VIEW(new I18nString("Tabbed case view", + Map.of("sk", "Zobrazenie prípadov v taboch", "de", "Fallansicht mit Registerkarten")), + "tabbed_case_view", List.of("tabbed_task_view"), true, false, true), + TABBED_TASK_VIEW(new I18nString("Tabbed task view", + Map.of("sk", "Zobrazenie úloh v taboch", "de", "Aufgabenansicht mit Registerkarten")), + "tabbed_task_view", List.of(), true, false, true), + TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", + Map.of("sk", "Tiketové zobrazenie v taboch", "de", "Ticketansicht mit Registerkarten")), + "tabbed_ticket_view", List.of("tabbed_single_task_view"), true, false, true), + SINGLE_TASK_VIEW(new I18nString("Single task view", + Map.of("sk", "Zobrazenie jednej úlohy", "de", "Einzelaufgabenansicht")), + "single_task_view", List.of(), true, true, true); private final I18nString name; private final String identifier; @@ -32,17 +34,19 @@ public enum MenuItemView { * */ private final List<String> allowedAssociatedViews; private final boolean isTabbed; + private final boolean isUntabbed; /** * if false, the view cannot be used as first configuration of the menu_item, but can be used as secondary * (associated to another view) * */ private final boolean isPrimary; - MenuItemView(I18nString name, String identifier, List<String> allowedAssociatedViews, boolean isTabbed, boolean isPrimary) { + MenuItemView(I18nString name, String identifier, List<String> allowedAssociatedViews, boolean isTabbed, boolean isUntabbed, boolean isPrimary) { this.name = name; this.identifier = identifier; this.allowedAssociatedViews = allowedAssociatedViews; this.isTabbed = isTabbed; + this.isUntabbed = isUntabbed; this.isPrimary = isPrimary; } @@ -68,7 +72,7 @@ public static MenuItemView fromIdentifier(String identifier) { * */ public static List<MenuItemView> findAllByIsTabbedAndIsPrimary(boolean isTabbed, boolean isPrimary) { return Arrays.stream(MenuItemView.values()) - .filter(view -> view.isTabbed == isTabbed && view.isPrimary == isPrimary) + .filter(view -> (view.isTabbed == isTabbed || view.isUntabbed != isTabbed) && view.isPrimary == isPrimary) .collect(Collectors.toList()); } @@ -83,7 +87,7 @@ public static List<MenuItemView> findAllByIsTabbedAndIsPrimary(boolean isTabbed, public static List<MenuItemView> findAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier) { MenuItemView parentView = fromIdentifier(parentIdentifier); return Arrays.stream(MenuItemView.values()) - .filter(view -> view.isTabbed == isTabbed + .filter(view -> (view.isTabbed == isTabbed || view.isUntabbed != isTabbed) && parentView.getAllowedAssociatedViews().contains(view.identifier)) .collect(Collectors.toList()); } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewBody.java similarity index 61% rename from src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java rename to src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewBody.java index fa58689c976..06633508bed 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewBody.java @@ -10,8 +10,9 @@ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) -public class TabbedSingleTaskViewBody extends ViewBody { - private String transitionId; +public class SingleTaskViewBody extends ViewBody { + private boolean showPageHeader = true; + private boolean showPageFooter = false; @Override public ViewBody getAssociatedViewBody() { @@ -20,12 +21,13 @@ public ViewBody getAssociatedViewBody() { @Override public MenuItemView getViewType() { - return MenuItemView.TABBED_SINGLE_TASK_VIEW; + return MenuItemView.SINGLE_TASK_VIEW; } @Override protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { - outcome.putDataSetEntry(TabbedSingleTaskViewConstants.FIELD_TRANSITION_ID, FieldType.TEXT, this.transitionId); + outcome.putDataSetEntry(SingleTaskViewConstants.FIELD_SHOW_PAGE_HEADER, FieldType.BOOLEAN, this.showPageHeader); + outcome.putDataSetEntry(SingleTaskViewConstants.FIELD_SHOW_PAGE_FOOTER, FieldType.BOOLEAN, this.showPageFooter); return outcome; } } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewConstants.java new file mode 100644 index 00000000000..2782e50e0dc --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewConstants.java @@ -0,0 +1,6 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +public class SingleTaskViewConstants extends ViewConstants { + public static final String FIELD_SHOW_PAGE_HEADER = "show_page_header"; + public static final String FIELD_SHOW_PAGE_FOOTER = "show_page_footer"; +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java deleted file mode 100644 index c0d6949b328..00000000000 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.netgrif.application.engine.menu.domain.configurations; - -public class TabbedSingleTaskViewConstants extends ViewConstants { - public static final String FIELD_TRANSITION_ID = "transition_id"; -} diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 5c1d8f85c3a..e29a594cd6c 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -509,7 +509,7 @@ <allowedNet>tabbed_case_view_configuration</allowedNet> <allowedNet>tabbed_task_view_configuration</allowedNet> <allowedNet>tabbed_ticket_view_configuration</allowedNet> - <allowedNet>tabbed_single_task_view_configuration</allowedNet> + <allowedNet>single_task_view_configuration</allowedNet> </allowedNets> </data> <data type="taskRef"> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml similarity index 99% rename from src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml rename to src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml index 6a346161c40..c951d9b2a10 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml @@ -1,7 +1,7 @@ <document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://petriflow.com/petriflow.schema.xsd"> - <id>tabbed_single_task_view_configuration</id> + <id>single_task_view_configuration</id> <initials>TST</initials> - <title>Tabbed single task view configuration + Single task view configuration check_box_outline_blank true false diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml index 32fc9e33a2b..07b4c103e61 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -106,7 +106,7 @@ view_configuration_id <allowedNets> - <allowedNet>tabbed_single_task_view_configuration</allowedNet> + <allowedNet>single_task_view_configuration</allowedNet> </allowedNets> </data> <data type="taskRef"> diff --git a/src/test/resources/dashboard_management_test.xml b/src/test/resources/dashboard_management_test.xml index d3f1ae15906..199b8a68b98 100644 --- a/src/test/resources/dashboard_management_test.xml +++ b/src/test/resources/dashboard_management_test.xml @@ -27,7 +27,7 @@ filterBody.getTitle(), filterBody.getIcon() ) - def singleTaskViewBody = new com.netgrif.application.engine.menu.domain.configurations.TabbedSingleTaskViewBody() + def singleTaskViewBody = new com.netgrif.application.engine.menu.domain.configurations.SingleTaskViewBody() def ticketViewBody = new com.netgrif.application.engine.menu.domain.configurations.TabbedTicketViewBody() ticketViewBody.setChainedView(singleTaskViewBody) menuItemBody.setAutoSelect(true) From 4126f045a9a6b123d1caf8a4e3045694bb6144c7 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Tue, 12 May 2026 09:23:52 +0200 Subject: [PATCH 09/49] [ETASK-23] Dynamic view configuration - make task view as untabbed too --- .../logic/action/ActionDelegate.groovy | 6 ++--- .../engine/menu/domain/MenuItemView.java | 10 ++++----- ...bedTaskViewBody.java => TaskViewBody.java} | 22 +++++++++---------- ...wConstants.java => TaskViewConstants.java} | 4 ++-- .../engine-processes/menu/menu_item.xml | 2 +- .../menu/tabbed_case_view_configuration.xml | 2 +- ...ration.xml => task_view_configuration.xml} | 4 ++-- .../engine/action/MenuItemApiTest.groovy | 18 +++++++-------- .../engine/menu/MenuItemServiceTest.java | 4 ++-- 9 files changed, 36 insertions(+), 36 deletions(-) rename src/main/java/com/netgrif/application/engine/menu/domain/configurations/{TabbedTaskViewBody.java => TaskViewBody.java} (63%) rename src/main/java/com/netgrif/application/engine/menu/domain/configurations/{TabbedTaskViewConstants.java => TaskViewConstants.java} (85%) rename src/main/resources/petriNets/engine-processes/menu/{tabbed_task_view_configuration.xml => task_view_configuration.xml} (99%) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 9b8b0c338bd..d19a3ddbe83 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -27,7 +27,7 @@ import com.netgrif.application.engine.menu.domain.FilterBody import com.netgrif.application.engine.menu.domain.MenuItemBody import com.netgrif.application.engine.menu.domain.MenuItemConstants import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody -import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody import com.netgrif.application.engine.menu.domain.configurations.ViewBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody @@ -2118,13 +2118,13 @@ class ActionDelegate { caseView.setDefaultHeaders(caseDefaultHeaders) caseView.setRequireTitleInCreation(true) - ViewBody taskView = new TabbedTaskViewBody() + ViewBody taskView = new TaskViewBody() taskView.setDefaultHeaders(taskDefaultHeaders) caseView.setChainedView(taskView) return caseView } else if (filterBody.getType() == "Task") { - ViewBody taskView = new TabbedTaskViewBody() + ViewBody taskView = new TaskViewBody() taskView.setFilterBody(filterBody) taskView.setDefaultHeaders(taskDefaultHeaders) return taskView diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index ddffbb2dcb2..8ecbab35c10 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -16,13 +16,13 @@ public enum MenuItemView { TABBED_CASE_VIEW(new I18nString("Tabbed case view", Map.of("sk", "Zobrazenie prípadov v taboch", "de", "Fallansicht mit Registerkarten")), - "tabbed_case_view", List.of("tabbed_task_view"), true, false, true), - TABBED_TASK_VIEW(new I18nString("Tabbed task view", - Map.of("sk", "Zobrazenie úloh v taboch", "de", "Aufgabenansicht mit Registerkarten")), - "tabbed_task_view", List.of(), true, false, true), + "tabbed_case_view", List.of("task_view"), true, false, true), + TASK_VIEW(new I18nString("Task view", + Map.of("sk", "Zobrazenie úloh", "de", "Aufgabenansicht")), + "task_view", List.of(), true, true, true), TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of("sk", "Tiketové zobrazenie v taboch", "de", "Ticketansicht mit Registerkarten")), - "tabbed_ticket_view", List.of("tabbed_single_task_view"), true, false, true), + "tabbed_ticket_view", List.of("single_task_view"), true, false, true), SINGLE_TASK_VIEW(new I18nString("Single task view", Map.of("sk", "Zobrazenie jednej úlohy", "de", "Einzelaufgabenansicht")), "single_task_view", List.of(), true, true, true); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java similarity index 63% rename from src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java rename to src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java index 0fabe6940fe..9e572485b3b 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java @@ -14,7 +14,7 @@ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) -public class TabbedTaskViewBody extends ViewBody { +public class TaskViewBody extends ViewBody { private Case filter; private boolean mergeFilters = true; private String viewSearchType = "fulltext_advanced"; @@ -33,29 +33,29 @@ public ViewBody getAssociatedViewBody() { @Override public MenuItemView getViewType() { - return MenuItemView.TABBED_TASK_VIEW; + return MenuItemView.TASK_VIEW; } @Override protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_MERGE_FILTERS, FieldType.BOOLEAN, + outcome.putDataSetEntry(TaskViewConstants.FIELD_MERGE_FILTERS, FieldType.BOOLEAN, this.mergeFilters); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, + outcome.putDataSetEntry(TaskViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, this.viewSearchType); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_HEADERS_MODE, FieldType.MULTICHOICE_MAP, + outcome.putDataSetEntry(TaskViewConstants.FIELD_HEADERS_MODE, FieldType.MULTICHOICE_MAP, this.headersMode == null ? new ArrayList<>() : this.headersMode); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, + outcome.putDataSetEntry(TaskViewConstants.FIELD_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, this.headersDefaultMode); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, + outcome.putDataSetEntry(TaskViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, this.isHeaderModeChangeable); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, + outcome.putDataSetEntry(TaskViewConstants.FIELD_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, this.allowHeaderTableMode); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_USE_DEFAULT_HEADERS, FieldType.BOOLEAN, + outcome.putDataSetEntry(TaskViewConstants.FIELD_USE_DEFAULT_HEADERS, FieldType.BOOLEAN, this.useDefaultHeaders); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT, + outcome.putDataSetEntry(TaskViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT, this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, + outcome.putDataSetEntry(TaskViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, this.showMoreMenu); return outcome; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java similarity index 85% rename from src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java rename to src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java index f895558fa77..afc745c463b 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java @@ -1,9 +1,9 @@ package com.netgrif.application.engine.menu.domain.configurations; /** - * Here are declared constants of process tabbed_task_view_configuration.xml. + * Here are declared constants of process task_view_configuration.xml. */ -public class TabbedTaskViewConstants extends ViewConstants { +public class TaskViewConstants extends ViewConstants { public static final String FIELD_MERGE_FILTERS = "merge_filters"; public static final String FIELD_VIEW_SEARCH_TYPE = "view_search_type"; public static final String FIELD_DEFAULT_HEADERS = "default_headers"; diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index e29a594cd6c..2699eff5789 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -507,7 +507,7 @@ <title/> <allowedNets> <allowedNet>tabbed_case_view_configuration</allowedNet> - <allowedNet>tabbed_task_view_configuration</allowedNet> + <allowedNet>task_view_configuration</allowedNet> <allowedNet>tabbed_ticket_view_configuration</allowedNet> <allowedNet>single_task_view_configuration</allowedNet> </allowedNets> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 14626ff6bab..f8d9736f507 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -383,7 +383,7 @@ <id>view_configuration_id</id> <title/> <allowedNets> - <allowedNet>tabbed_task_view_configuration</allowedNet> + <allowedNet>task_view_configuration</allowedNet> </allowedNets> </data> <data type="taskRef"> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml similarity index 99% rename from src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml rename to src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml index e89d84d1448..e09b34d8674 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml @@ -1,7 +1,7 @@ <document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://petriflow.com/petriflow.schema.xsd"> - <id>tabbed_task_view_configuration</id> + <id>task_view_configuration</id> <initials>TTV</initials> - <title>Tabbed task view configuration + Task view configuration check_box_outline_blank true false diff --git a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy index e3ae062e5e0..89d93af0054 100644 --- a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy @@ -7,7 +7,7 @@ import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchReques import com.netgrif.application.engine.menu.domain.MenuItemConstants import com.netgrif.application.engine.menu.domain.MenuItemView import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewConstants -import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewConstants +import com.netgrif.application.engine.menu.domain.configurations.TaskViewConstants import com.netgrif.application.engine.menu.utils.MenuItemUtils import com.netgrif.application.engine.orgstructure.groups.interfaces.INextGroupService import com.netgrif.application.engine.petrinet.domain.I18nString @@ -104,14 +104,14 @@ class MenuItemApiTest { assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TABBED_TASK_VIEW.identifier + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null Case tabbedTaskView = workflowService.findOne(tabbedTaskViewId) - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_FILTER_CASE].value == [] - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_FILTER_CASE].value == [] + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" Case testFolder = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue.keyword:\"/netgrif/test\"", PageRequest.of(0, 1))[0] Case netgrifFolder = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue.keyword:\"/netgrif\"", PageRequest.of(0, 1))[0] @@ -170,14 +170,14 @@ class MenuItemApiTest { assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TABBED_TASK_VIEW.identifier + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null && tabbedTaskViewId.equals(tabbedTaskViewIdBeforeChange) Case tabbedTaskView = workflowService.findOne(tabbedTaskViewId) - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_FILTER_CASE].value == [] - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_FILTER_CASE].value == [] + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" } @Test diff --git a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java index 0f711caf38c..dffd8056049 100644 --- a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java +++ b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java @@ -4,7 +4,7 @@ import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody; -import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody; +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; @@ -146,7 +146,7 @@ private Case createDefaultMenuItem(String identifier, I18nString name) throws Tr TabbedCaseViewBody caseView = new TabbedCaseViewBody(); caseView.setFilterBody(filterBody); caseView.setRequireTitleInCreation(false); - caseView.setChainedView(new TabbedTaskViewBody()); + caseView.setChainedView(new TaskViewBody()); MenuItemBody menuItemBody = new MenuItemBody(); menuItemBody.setUri("/"); From 64b92f03b8a8199148a1ade60cd3dedfb1b7823a Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 12 May 2026 11:09:18 +0200 Subject: [PATCH 10/49] [ETASK-23] Dynamic view configuration - make case view as untabbed too --- .../logic/action/ActionDelegate.groovy | 4 +- .../engine/menu/domain/MenuItemView.java | 6 +- ...bedCaseViewBody.java => CaseViewBody.java} | 30 +++++----- ...wConstants.java => CaseViewConstants.java} | 4 +- .../engine/menu/service/MenuItemService.java | 27 +++++---- ...ration.xml => case_view_configuration.xml} | 60 +++++++++++++++---- .../engine-processes/menu/menu_item.xml | 7 ++- .../engine/action/MenuItemApiTest.groovy | 30 +++++----- .../engine/menu/MenuItemServiceTest.java | 4 +- 9 files changed, 110 insertions(+), 62 deletions(-) rename src/main/java/com/netgrif/application/engine/menu/domain/configurations/{TabbedCaseViewBody.java => CaseViewBody.java} (60%) rename src/main/java/com/netgrif/application/engine/menu/domain/configurations/{TabbedCaseViewConstants.java => CaseViewConstants.java} (90%) rename src/main/resources/petriNets/engine-processes/menu/{tabbed_case_view_configuration.xml => case_view_configuration.xml} (96%) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index d19a3ddbe83..e1bc462a28c 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -26,7 +26,7 @@ import com.netgrif.application.engine.mail.interfaces.IMailService import com.netgrif.application.engine.menu.domain.FilterBody import com.netgrif.application.engine.menu.domain.MenuItemBody import com.netgrif.application.engine.menu.domain.MenuItemConstants -import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody +import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody import com.netgrif.application.engine.menu.domain.configurations.ViewBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody @@ -2113,7 +2113,7 @@ class ActionDelegate { protected ViewBody createLegacyMenuItemViews(FilterBody filterBody, List caseDefaultHeaders = null, List taskDefaultHeaders = null) { if (filterBody.getType() == "Case") { - ViewBody caseView = new TabbedCaseViewBody() + ViewBody caseView = new CaseViewBody() caseView.setFilterBody(filterBody) caseView.setDefaultHeaders(caseDefaultHeaders) caseView.setRequireTitleInCreation(true) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 8ecbab35c10..5061dc31a6f 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -14,9 +14,9 @@ * */ @Getter public enum MenuItemView { - TABBED_CASE_VIEW(new I18nString("Tabbed case view", - Map.of("sk", "Zobrazenie prípadov v taboch", "de", "Fallansicht mit Registerkarten")), - "tabbed_case_view", List.of("task_view"), true, false, true), + CASE_VIEW(new I18nString("Case view", + Map.of("sk", "Zobrazenie prípadov", "de", "Fallansicht")), + "case_view", List.of("task_view"), true, true, true), TASK_VIEW(new I18nString("Task view", Map.of("sk", "Zobrazenie úloh", "de", "Aufgabenansicht")), "task_view", List.of(), true, true, true), diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java similarity index 60% rename from src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java rename to src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java index 661ff73eaa5..baf76eff3ba 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java @@ -14,7 +14,7 @@ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) -public class TabbedCaseViewBody extends ViewBody { +public class CaseViewBody extends ViewBody { private String viewSearchType = "fulltext_advanced"; private I18nString createCaseButtonTitle; private String createCaseButtonIcon = "add"; @@ -38,39 +38,39 @@ public ViewBody getAssociatedViewBody() { @Override public MenuItemView getViewType() { - return MenuItemView.TABBED_CASE_VIEW; + return MenuItemView.CASE_VIEW; } @Override protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, + outcome.putDataSetEntry(CaseViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, this.viewSearchType); if (this.createCaseButtonTitle != null) { - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.I18N, + outcome.putDataSetEntry(CaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.I18N, this.createCaseButtonTitle); } - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_ICON, FieldType.TEXT, + outcome.putDataSetEntry(CaseViewConstants.FIELD_CREATE_CASE_BUTTON_ICON, FieldType.TEXT, this.createCaseButtonIcon); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_REQUIRE_TITLE_IN_CREATION, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_REQUIRE_TITLE_IN_CREATION, FieldType.BOOLEAN, this.requireTitleInCreation); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_SHOW_CREATE_CASE_BUTTON, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_SHOW_CREATE_CASE_BUTTON, FieldType.BOOLEAN, this.showCreateCaseButton); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_BANNED_NETS_IN_CREATION, FieldType.TEXT, + outcome.putDataSetEntry(CaseViewConstants.FIELD_BANNED_NETS_IN_CREATION, FieldType.TEXT, this.bannedNetsInCreation); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, this.showMoreMenu); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, this.allowHeaderTableMode); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_HEADERS_MODE, FieldType.MULTICHOICE_MAP, + outcome.putDataSetEntry(CaseViewConstants.FIELD_HEADERS_MODE, FieldType.MULTICHOICE_MAP, this.headersMode == null ? new ArrayList<>() : this.headersMode); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, + outcome.putDataSetEntry(CaseViewConstants.FIELD_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, this.headersDefaultMode); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT, + outcome.putDataSetEntry(CaseViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT, this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, this.isHeaderModeChangeable); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_USE_CASE_DEFAULT_HEADERS, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_USE_CASE_DEFAULT_HEADERS, FieldType.BOOLEAN, this.useDefaultHeaders); return outcome; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java similarity index 90% rename from src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java rename to src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java index b5c8ef2361b..b26ce065485 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java @@ -1,9 +1,9 @@ package com.netgrif.application.engine.menu.domain.configurations; /** - * Here are declared constants of process tabbed_case_view_configuration.xml. + * Here are declared constants of process case_view_configuration.xml. */ -public class TabbedCaseViewConstants extends ViewConstants { +public class CaseViewConstants extends ViewConstants { public static final String FIELD_NEW_FILTER_ID = "new_filter_id"; public static final String FIELD_DEFAULT_HEADERS = "default_headers"; public static final String FIELD_REQUIRE_TITLE_IN_CREATION = "require_title_in_creation"; diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index 708c05eb4a8..27a5f80c6c7 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -124,7 +124,7 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce Case viewCase = null; if (body.hasView()) { - viewCase = createView(body.getView()); + viewCase = createView(body.getView(), body.isUseTabbedView()); } ToDataSetOutcome dataSetOutcome = body.toDataSet(parentItemCase.getStringId(), nodePath, viewCase); menuItemCase = setDataWithExecute(menuItemCase, MenuItemConstants.TRANS_INIT_ID, dataSetOutcome.getDataSet()); @@ -150,7 +150,7 @@ public Case updateMenuItem(Case itemCase, MenuItemBody body) throws TransitionNo } Case viewCase = findView(itemCase); - viewCase = handleView(viewCase, body.getView()); + viewCase = handleView(viewCase, body.getView(), body.isUseTabbedView()); ToDataSetOutcome dataSetOutcome = body.toDataSet(viewCase); itemCase = setData(itemCase, MenuItemConstants.TRANS_SYNC_ID, dataSetOutcome.getDataSet()); log.debug("Updated menu item case [{}] with identifier [{}].", itemCase.getStringId(), body.getIdentifier()); @@ -475,30 +475,30 @@ protected Case findCaseInCaseRef(Case useCase, String caseRefId) { } } - protected Case handleView(Case existingViewCase, ViewBody body) throws TransitionNotExecutableException { + protected Case handleView(Case existingViewCase, ViewBody body, boolean isTabbed) throws TransitionNotExecutableException { if (mustUpdateView(existingViewCase, body)) { - return updateView(existingViewCase, body); + return updateView(existingViewCase, body, isTabbed); } else if (mustCreateView(existingViewCase, body)) { - return createView(body); + return createView(body, isTabbed); } else if (mustRemoveView(existingViewCase, body)) { removeView(existingViewCase); return null; } else if (mustRemoveAndCreateView(existingViewCase, body)) { removeView(existingViewCase); - return createView(body); + return createView(body, isTabbed); } else { return null; } } - protected Case createView(ViewBody body) throws TransitionNotExecutableException { + protected Case createView(ViewBody body, boolean isTabbed) throws TransitionNotExecutableException { IUser loggedUser = userService.getLoggedOrSystem(); Case viewCase = createCase(body.getViewProcessIdentifier(), body.getViewProcessIdentifier(), - loggedUser.transformToLoggedUser()); + loggedUser.transformToLoggedUser(), isTabbed); Case associatedViewCase = null; if (body.hasAssociatedView()) { - associatedViewCase = createView(body.getAssociatedViewBody()); + associatedViewCase = createView(body.getAssociatedViewBody(), isTabbed); } Case filterCase = null; if (body.getFilterBody() != null) { @@ -516,12 +516,12 @@ protected Case createView(ViewBody body) throws TransitionNotExecutableException return viewCase; } - protected Case updateView(Case viewCase, ViewBody body) throws TransitionNotExecutableException { + protected Case updateView(Case viewCase, ViewBody body, boolean isTabbed) throws TransitionNotExecutableException { Case filterCase = findFilter(viewCase); filterCase = handleFilter(filterCase, body.getFilterBody()); Case associatedViewCase = findView(viewCase); - associatedViewCase = handleView(associatedViewCase, body.getAssociatedViewBody()); + associatedViewCase = handleView(associatedViewCase, body.getAssociatedViewBody(), isTabbed); ToDataSetOutcome outcome = body.toDataSet(associatedViewCase, filterCase); viewCase = setData(viewCase, ViewConstants.TRANS_SYNC_ID, outcome.getDataSet()); @@ -699,6 +699,11 @@ protected Case createCase(String identifier, String title, LoggedUser loggedUser return workflowService.createCaseByIdentifier(identifier, title, "", loggedUser).getCase(); } + protected Case createCase(String identifier, String title, LoggedUser loggedUser, boolean isTabbed) { + return workflowService.createCaseByIdentifier(identifier, title, "", loggedUser, + Map.of("is_tabbed", String.valueOf(isTabbed))).getCase(); + } + protected Case setData(Case useCase, String transId, Map> dataSet) { String taskId = MenuItemUtils.findTaskIdInCase(useCase, transId); return setData(taskId, dataSet); diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml similarity index 96% rename from src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml rename to src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml index f8d9736f507..a088abb4cff 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml @@ -1,7 +1,7 @@ - tabbed_case_view_configuration - TCV - Tabbed case view configuration + case_view_configuration + CVC + Case view configuration check_box_outline_blank true false @@ -29,10 +29,19 @@ + + view_create + + + is_tabbed_var_arc: f.is_tabbed_var_arc; + change is_tabbed_var_arc value { Boolean.valueOf(params["is_tabbed"]) ? 1 : 0 } + + + view_delete - + removeViewCase() @@ -406,14 +415,22 @@ advanced_content_form: f.advanced_content_form; - change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "header_settings"}.task, - useCase.tasks.find { taskPair -> taskPair.transition == "create_case_btn_settings"}.task, - useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task, - useCase.tasks.find { taskPair -> taskPair.transition == "next_view_settings"}.task] } + String nextViewTaskId = useCase.tasks.find { taskPair -> taskPair.transition == "next_view_settings"}?.task + def taskIds = [useCase.tasks.find { taskPair -> taskPair.transition == "header_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "create_case_btn_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task] + if (nextViewTaskId != null) { + taskIds.add(nextViewTaskId) + } + change advanced_content_form value { taskIds } + + is_tabbed_var_arc + Is tabbed + @@ -696,7 +713,7 @@ next_view_settings - 560 + 336 48 queue_play_next @@ -927,7 +944,7 @@ header_settings - 336 + 560 48 view_column @@ -1240,6 +1257,14 @@ 1 false + + is_tabbed + 240 + 48 + + 0 + false + a1 read @@ -1289,4 +1314,19 @@ create_case_btn_settings 1 + + a8 + regular + initialize + is_tabbed + 1 + is_tabbed_var_arc + + + a9 + read + is_tabbed + next_view_settings + 1 + \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 2699eff5789..140c69c6379 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -506,7 +506,7 @@ view_configuration_id <allowedNets> - <allowedNet>tabbed_case_view_configuration</allowedNet> + <allowedNet>case_view_configuration</allowedNet> <allowedNet>task_view_configuration</allowedNet> <allowedNet>tabbed_ticket_view_configuration</allowedNet> <allowedNet>single_task_view_configuration</allowedNet> @@ -1615,6 +1615,7 @@ <id>0</id> <actions phase="post"> <action> + use_tabbed_view: f.use_tabbed_view, view_configuration_type: f.view_configuration_type, view_configuration_form: f.view_configuration_form, view_configuration_all_data_form: f.view_configuration_all_data_form, @@ -1631,7 +1632,9 @@ return } - def configurationCase = createCase(view_configuration_type.value + "_configuration") + def configurationCase = createCase(view_configuration_type.value + "_configuration", null, "", + userService.loggedOrSystem, org.springframework.context.i18n.LocaleContextHolder.getLocale(), + ["is_tabbed":String.valueOf(use_tabbed_view.value)]) def initTask = assignTask("initialize", configurationCase) finishTask(initTask) configurationCase = workflowService.findOne(configurationCase.stringId) diff --git a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy index 89d93af0054..3c591ddd80d 100644 --- a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy @@ -6,7 +6,7 @@ import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseSer import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest import com.netgrif.application.engine.menu.domain.MenuItemConstants import com.netgrif.application.engine.menu.domain.MenuItemView -import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewConstants +import com.netgrif.application.engine.menu.domain.configurations.CaseViewConstants import com.netgrif.application.engine.menu.domain.configurations.TaskViewConstants import com.netgrif.application.engine.menu.utils.MenuItemUtils import com.netgrif.application.engine.orgstructure.groups.interfaces.INextGroupService @@ -91,7 +91,7 @@ class MenuItemApiTest { assert item.dataSet[MenuItemConstants.FIELD_BANNED_ROLES].options.containsKey("role_2:filter_api_test") assert item.dataSet[MenuItemConstants.FIELD_ALLOWED_ROLES].options.containsKey("role_1:filter_api_test") assert item.dataSet[MenuItemConstants.FIELD_USE_TABBED_VIEW].value == true - assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.TABBED_CASE_VIEW.identifier + assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.CASE_VIEW.identifier assert filter.dataSet["filter"].filterMetadata["filterType"] == "Case" assert filter.dataSet["filter"].allowedNets == ["filter", "menu_item"] @@ -101,12 +101,12 @@ class MenuItemApiTest { String tabbedCaseViewId = MenuItemUtils.getCaseIdFromCaseRef(item, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedCaseViewId != null Case tabbedCaseView = workflowService.findOne(tabbedCaseViewId) - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier - String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, CaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null Case tabbedTaskView = workflowService.findOne(tabbedTaskViewId) assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false @@ -139,7 +139,7 @@ class MenuItemApiTest { Case item = getMenuItem(caze) String tabbedCaseViewIdBeforeChange = MenuItemUtils.getCaseIdFromCaseRef(item, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) Case tabbedCaseViewBeforeChange = workflowService.findOne(tabbedCaseViewIdBeforeChange) - String tabbedTaskViewIdBeforeChange = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseViewBeforeChange, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + String tabbedTaskViewIdBeforeChange = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseViewBeforeChange, CaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) def newUri = uriService.getOrCreate("/netgrif/test_new", UriContentType.DEFAULT) caze = setData(caze, [ @@ -157,7 +157,7 @@ class MenuItemApiTest { assert item.dataSet[MenuItemConstants.FIELD_MENU_NAME].value.toString() == "CHANGED FILTER" assert item.dataSet[MenuItemConstants.FIELD_ALLOWED_ROLES].options.entrySet()[0].key.contains("role_2") assert item.dataSet[MenuItemConstants.FIELD_USE_TABBED_VIEW].value == true - assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.TABBED_CASE_VIEW.identifier + assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.CASE_VIEW.identifier assert item.uriNodeId == newUri.stringId assert filter.dataSet["filter"].allowedNets == ["filter"] @@ -167,12 +167,12 @@ class MenuItemApiTest { String tabbedCaseViewId = MenuItemUtils.getCaseIdFromCaseRef(item, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedCaseViewId != null && tabbedCaseViewId.equals(tabbedCaseViewIdBeforeChange) Case tabbedCaseView = workflowService.findOne(tabbedCaseViewId) - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier - String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, CaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null && tabbedTaskViewId.equals(tabbedTaskViewIdBeforeChange) Case tabbedTaskView = workflowService.findOne(tabbedTaskViewId) assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false @@ -354,7 +354,7 @@ class MenuItemApiTest { String tabbedCaseViewId = MenuItemUtils.getCaseIdFromCaseRef(leafItemCase, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedCaseViewId != null Case tabbedCaseView = workflowService.findOne(tabbedCaseViewId) - String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, CaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null workflowService.deleteCase(testFolder) diff --git a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java index dffd8056049..c1df0ebdf78 100644 --- a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java +++ b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java @@ -3,7 +3,7 @@ import com.netgrif.application.engine.TestHelper; import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; -import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody; +import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.petrinet.domain.I18nString; @@ -143,7 +143,7 @@ private Case createDefaultMenuItem(String identifier, I18nString name) throws Tr filterBody.setIcon("home"); filterBody.setVisibility("private"); - TabbedCaseViewBody caseView = new TabbedCaseViewBody(); + CaseViewBody caseView = new CaseViewBody(); caseView.setFilterBody(filterBody); caseView.setRequireTitleInCreation(false); caseView.setChainedView(new TaskViewBody()); From fe4f7437f4be84ff9b5151ffa85ccaddf7e81c83 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Tue, 12 May 2026 15:50:10 +0200 Subject: [PATCH 11/49] [ETASK-23] Dynamic view configuration - fixes --- .../menu/domain/configurations/CaseViewBody.java | 2 +- .../domain/configurations/CaseViewConstants.java | 2 +- .../menu/case_view_configuration.xml | 12 +++++------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java index baf76eff3ba..3bb18185d18 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java @@ -70,7 +70,7 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null); outcome.putDataSetEntry(CaseViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, this.isHeaderModeChangeable); - outcome.putDataSetEntry(CaseViewConstants.FIELD_USE_CASE_DEFAULT_HEADERS, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_USE_DEFAULT_HEADERS, FieldType.BOOLEAN, this.useDefaultHeaders); return outcome; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java index b26ce065485..ecadc80da20 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java @@ -17,5 +17,5 @@ public class CaseViewConstants extends ViewConstants { public static final String FIELD_HEADERS_MODE = "headers_mode"; public static final String FIELD_HEADERS_DEFAULT_MODE = "headers_default_mode"; public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "is_header_mode_changeable"; - public static final String FIELD_USE_CASE_DEFAULT_HEADERS = "use_case_default_headers"; + public static final String FIELD_USE_DEFAULT_HEADERS = "use_default_headers"; } diff --git a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml index a088abb4cff..273ea0822ae 100644 --- a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml @@ -357,8 +357,8 @@ <init>true</init> </data> <data type="boolean" immediate="true"> - <id>use_case_default_headers</id> - <title name="use_case_default_headers">Use custom default headers? + use_default_headers + Use custom default headers? true @@ -462,7 +462,6 @@ Súčasný filter Zobrazenie prípadov Všeobecné - Použiť predvolené hlavičky Vybrať zobrazenie Nastavenie @@ -495,7 +494,6 @@ "Erweiterte Optionen" Taste bei einzelnen Fällen anzeigen Menüeintrageinstellungen Fallansicht - Benutzerdefinierte Standardheader verwenden? Wählen Sie einen Ansichtstyp Einstellungen @@ -994,12 +992,12 @@ - use_case_default_headers + use_default_headers editable trans: t.this, - use: f.use_case_default_headers, + use: f.use_default_headers, headers: f.default_headers; make headers,editable on trans when { use.value } @@ -1216,7 +1214,7 @@ - use_case_default_headers + use_default_headers visible From dd2ec2ed86a3defef9e10459f408bbd186aa550f Mon Sep 17 00:00:00 2001 From: chvostek Date: Wed, 13 May 2026 09:44:44 +0200 Subject: [PATCH 12/49] [ETASK-23] Dynamic view configuration - fix field ids --- .../configurations/CaseViewConstants.java | 16 +-- .../configurations/TaskViewConstants.java | 16 +-- .../menu/case_view_configuration.xml | 114 +++++++++--------- .../menu/task_view_configuration.xml | 114 +++++++++--------- 4 files changed, 130 insertions(+), 130 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java index ecadc80da20..99ec5c2f2fa 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java @@ -5,17 +5,17 @@ */ public class CaseViewConstants extends ViewConstants { public static final String FIELD_NEW_FILTER_ID = "new_filter_id"; - public static final String FIELD_DEFAULT_HEADERS = "default_headers"; + public static final String FIELD_DEFAULT_HEADERS = "case_default_headers"; public static final String FIELD_REQUIRE_TITLE_IN_CREATION = "require_title_in_creation"; - public static final String FIELD_VIEW_SEARCH_TYPE = "view_search_type"; + public static final String FIELD_VIEW_SEARCH_TYPE = "case_view_search_type"; public static final String FIELD_CREATE_CASE_BUTTON_TITLE = "create_case_button_title"; public static final String FIELD_CREATE_CASE_BUTTON_ICON = "create_case_button_icon"; public static final String FIELD_BANNED_NETS_IN_CREATION = "banned_nets_in_creation"; public static final String FIELD_SHOW_CREATE_CASE_BUTTON = "show_create_case_button"; - public static final String FIELD_SHOW_MORE_MENU = "show_more_menu"; - public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "allow_header_table_mode"; - public static final String FIELD_HEADERS_MODE = "headers_mode"; - public static final String FIELD_HEADERS_DEFAULT_MODE = "headers_default_mode"; - public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "is_header_mode_changeable"; - public static final String FIELD_USE_DEFAULT_HEADERS = "use_default_headers"; + public static final String FIELD_SHOW_MORE_MENU = "case_show_more_menu"; + public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "case_allow_header_table_mode"; + public static final String FIELD_HEADERS_MODE = "case_headers_mode"; + public static final String FIELD_HEADERS_DEFAULT_MODE = "case_headers_default_mode"; + public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "case_is_header_mode_changeable"; + public static final String FIELD_USE_DEFAULT_HEADERS = "use_case_default_headers"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java index afc745c463b..32e9b716f67 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java @@ -5,12 +5,12 @@ */ public class TaskViewConstants extends ViewConstants { public static final String FIELD_MERGE_FILTERS = "merge_filters"; - public static final String FIELD_VIEW_SEARCH_TYPE = "view_search_type"; - public static final String FIELD_DEFAULT_HEADERS = "default_headers"; - public static final String FIELD_HEADERS_MODE = "headers_mode"; - public static final String FIELD_HEADERS_DEFAULT_MODE = "headers_default_mode"; - public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "is_header_mode_changeable"; - public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "allow_header_table_mode"; - public static final String FIELD_USE_DEFAULT_HEADERS = "use_default_headers"; - public static final String FIELD_SHOW_MORE_MENU = "show_more_menu"; + public static final String FIELD_VIEW_SEARCH_TYPE = "task_view_search_type"; + public static final String FIELD_DEFAULT_HEADERS = "task_default_headers"; + public static final String FIELD_HEADERS_MODE = "task_headers_mode"; + public static final String FIELD_HEADERS_DEFAULT_MODE = "task_headers_default_mode"; + public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "task_is_header_mode_changeable"; + public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "task_allow_header_table_mode"; + public static final String FIELD_USE_DEFAULT_HEADERS = "use_task_default_headers"; + public static final String FIELD_SHOW_MORE_MENU = "task_show_more_menu"; } diff --git a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml index 273ea0822ae..a017c3fce7e 100644 --- a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml @@ -227,8 +227,8 @@ - view_search_type - Search type for case view + case_view_search_type + Search type for case view @@ -299,18 +299,18 @@ - show_more_menu - Show more menu for case item? + case_show_more_menu + Show more menu for case item? false - allow_header_table_mode - Allow table mode for headers? + case_allow_header_table_mode + Allow table mode for headers? true - headers_mode - Header mode + case_headers_mode + Header mode @@ -318,23 +318,23 @@ sort,edit,search - headersMode: f.headers_mode, - defaultMode: f.headers_default_mode, + headersMode: f.case_headers_mode, + defaultMode: f.case_headers_default_mode, holder: f.headers_options_holder; updateOptionsBasedOnValue(defaultMode, headersMode, holder) - headersMode: f.headers_mode, - defaultMode: f.headers_default_mode, + headersMode: f.case_headers_mode, + defaultMode: f.case_headers_default_mode, holder: f.headers_options_holder; updateOptionsBasedOnValue(defaultMode, headersMode, holder) - headers_default_mode - Default header mode + case_headers_default_mode + Default header mode @@ -352,18 +352,18 @@ - is_header_mode_changeable - Can header mode be changed? + case_is_header_mode_changeable + Can header mode be changed? true - use_default_headers - Use custom default headers? + use_case_default_headers + Use custom default headers? true - default_headers - Set default headers + case_default_headers + Set default headers Example: "meta-title,meta-visualId" defaultHeaders: f.this; @@ -437,11 +437,11 @@ Názov tlačidla "Nová inštancia" Identifikátor ikony tlačidla "Nová inštancia" Náhľad ikony - Predvolené hlavičky + Predvolené hlavičky Napríklad: "meta-title,meta-visualId" Zvoľte nový filter Aktualizovať zobrazenie s vybraným filtrom - Typ vyhľadávania prípadov + Typ vyhľadávania prípadov Skryté Fulltext Fulltext a rozšírené @@ -449,15 +449,15 @@ Zakázané siete pri vytváraní Uveďte identifikátory procesov oddelené čiarkou. Napríklad: mynet1,mynet2 Zobraziť tlačidlo na vytvorenie prípadu? - Zobrazovať menu pre prípadovú položku? + Zobrazovať menu pre prípadovú položku? Zoraďovanie Vyhľadávanie Upravovanie - Mód hlavičiek - Predvolený mód hlavičiek - Môže byť mód hlavičiek zmenený? - Povoliť tabuľkový mód pre hlavičky? - Použiť vlastné predvolené hlavičky? + Mód hlavičiek + Predvolený mód hlavičiek + Môže byť mód hlavičiek zmenený? + Povoliť tabuľkový mód pre hlavičky? + Použiť vlastné predvolené hlavičky? Nastavenie položky Súčasný filter Zobrazenie prípadov @@ -469,7 +469,7 @@ Schaltflächentitel "Neuer Fall" Ikone ID Ikonevorschau - Anzuzeigende Attributmenge auswählen + Anzuzeigende Attributmenge auswählen Neue Filter auswählen Beispiel: "meta-title,meta-visualId" Versteckt @@ -477,21 +477,21 @@ Sortieren Suchen Bearbeiten - Kopfzeilenmodus - Standardkopfzeilenmodus - Erlaube Änderung des Kopfzeilenmodus? - Erlaube Tabellenmodus? - Eigene Kopfzeilen verwenden? + Kopfzeilenmodus + Standardkopfzeilenmodus + Erlaube Änderung des Kopfzeilenmodus? + Erlaube Tabellenmodus? + Eigene Kopfzeilen verwenden? Aktueller Filter Allgemein Aktualisiere die Ansicht mit dem ausgewählten Filter - Suchmodus im Fallansicht + Suchmodus im Fallansicht Einfacher und erweiterter Suchmodus Erforde den Titel beim erzeugen von Fällen? Ausgeschlossene Prozesse Trenne die Prozessidentifikatoren mit einer Komma. z.B.: netz1,netz2 Schaltfläche „Fall erstellen“ anzeigen? - "Erweiterte Optionen" Taste bei einzelnen Fällen anzeigen + "Erweiterte Optionen" Taste bei einzelnen Fällen anzeigen Menüeintrageinstellungen Fallansicht Wählen Sie einen Ansichtstyp @@ -546,7 +546,7 @@ 4 grid - view_search_type + case_view_search_type editable required @@ -561,7 +561,7 @@ - show_more_menu + case_show_more_menu editable @@ -964,15 +964,15 @@ 3 grid - is_header_mode_changeable + case_is_header_mode_changeable editable required trans: t.this, - isChangeable: f.is_header_mode_changeable, - mode: f.headers_mode, - defaultMode: f.headers_default_mode; + isChangeable: f.case_is_header_mode_changeable, + mode: f.case_headers_mode, + defaultMode: f.case_headers_default_mode; make [mode, defaultMode], editable on trans when { isChangeable.value } make [mode, defaultMode], required on trans when { isChangeable.value } @@ -992,13 +992,13 @@ - use_default_headers + use_case_default_headers editable trans: t.this, - use: f.use_default_headers, - headers: f.default_headers; + use: f.use_case_default_headers, + headers: f.case_default_headers; make headers,editable on trans when { use.value } make headers,visible on trans when { !use.value } @@ -1015,7 +1015,7 @@ - default_headers + case_default_headers editable @@ -1030,7 +1030,7 @@ - allow_header_table_mode + case_allow_header_table_mode editable required @@ -1046,7 +1046,7 @@ - headers_mode + case_headers_mode editable required @@ -1062,7 +1062,7 @@ - headers_default_mode + case_headers_default_mode editable required @@ -1142,7 +1142,7 @@ - view_search_type + case_view_search_type visible @@ -1154,7 +1154,7 @@ - show_more_menu + case_show_more_menu visible @@ -1190,37 +1190,37 @@ - is_header_mode_changeable + case_is_header_mode_changeable visible - allow_header_table_mode + case_allow_header_table_mode visible - headers_mode + case_headers_mode visible - headers_default_mode + case_headers_default_mode visible - use_default_headers + use_case_default_headers visible - default_headers + case_default_headers visible diff --git a/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml index e09b34d8674..c9f25059274 100644 --- a/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml @@ -223,8 +223,8 @@ - view_search_type - Search type for task view + task_view_search_type + Search type for task view @@ -233,31 +233,31 @@ fulltext_advanced - headers_mode - Header mode + task_headers_mode + Header mode sort,edit - headersMode: f.headers_mode, - defaultMode: f.headers_default_mode, + headersMode: f.task_headers_mode, + defaultMode: f.task_headers_default_mode, holder: f.headers_options_holder; updateOptionsBasedOnValue(defaultMode, headersMode, holder) - headersMode: f.headers_mode, - defaultMode: f.headers_default_mode, + headersMode: f.task_headers_mode, + defaultMode: f.task_headers_default_mode, holder: f.headers_options_holder; updateOptionsBasedOnValue(defaultMode, headersMode, holder) - headers_default_mode - Default header mode + task_headers_default_mode + Default header mode @@ -273,23 +273,23 @@ - is_header_mode_changeable - Can header mode be changed? + task_is_header_mode_changeable + Can header mode be changed? true - allow_header_table_mode - Allow table mode for headers? + task_allow_header_table_mode + Allow table mode for headers? true - use_default_headers - Use custom default headers? + use_task_default_headers + Use custom default headers? true - default_headers - Set default headers + task_default_headers + Set default headers Example: "meta-title,meta-user" defaultHeaders: f.this; @@ -301,8 +301,8 @@ - show_more_menu - Show more menu for task item? + task_show_more_menu + Show more menu for task item? true @@ -335,15 +335,15 @@ Vyhľadávanie Upravovanie Zjednotiť filter so základným filtrom? - Typ vyhľadávania úloh - Mód hlavičiek - Predvolený mód hlavičiek - Môže byť mód hlavičiek zmenený? - Povoliť tabuľkový mód pre hlavičky? - Použiť vlastné predvolené hlavičky? - Predvolené hlavičky + Typ vyhľadávania úloh + Mód hlavičiek + Predvolený mód hlavičiek + Môže byť mód hlavičiek zmenený? + Povoliť tabuľkový mód pre hlavičky? + Použiť vlastné predvolené hlavičky? + Predvolené hlavičky Napríklad: "meta-title,meta-user" - Zobrazovať menu pre úlohovú položku? + Zobrazovať menu pre úlohovú položku? Nastavenie položky Súčasný filter Všeobecné @@ -356,20 +356,20 @@ Sortieren Suchen Bearbeiten - Kopfzeilenmodus - Standardkopfzeilenmodus - Erlaube Änderung des Kopfzeilenmodus? - Erlaube Tabellenmodus? - Eigene Kopfzeilen verwenden? - Anzuzeigende Attributmenge auswählen + Kopfzeilenmodus + Standardkopfzeilenmodus + Erlaube Änderung des Kopfzeilenmodus? + Erlaube Tabellenmodus? + Eigene Kopfzeilen verwenden? + Anzuzeigende Attributmenge auswählen Beispiel: "meta-title,meta-user" Aktueller Filter Allgemein Aktualisiere die Ansicht mit dem ausgewählten Filter Einfacher und erweiterter Suchmodus Mit dem Basisfilter kombinieren? - Suchmodus im Aufgabenansicht - "Erweiterte Optionen" Taste bei einzelnen Aufgaben anzeigen + Suchmodus im Aufgabenansicht + "Erweiterte Optionen" Taste bei einzelnen Aufgaben anzeigen Menüeintrageinstellungen Filter entfernen @@ -422,7 +422,7 @@ 4 grid - view_search_type + task_view_search_type editable required @@ -437,7 +437,7 @@ - show_more_menu + task_show_more_menu editable @@ -642,14 +642,14 @@ 3 grid - is_header_mode_changeable + task_is_header_mode_changeable editable trans: t.this, - isChangeable: f.is_header_mode_changeable, - mode: f.headers_mode, - defaultMode: f.headers_default_mode; + isChangeable: f.task_is_header_mode_changeable, + mode: f.task_headers_mode, + defaultMode: f.task_headers_default_mode; make [mode, defaultMode], editable on trans when { isChangeable.value } make [mode, defaultMode], required on trans when { isChangeable.value } @@ -669,13 +669,13 @@ - use_default_headers + use_task_default_headers editable trans: t.this, - use: f.use_default_headers, - headers: f.default_headers; + use: f.use_task_default_headers, + headers: f.task_default_headers; make headers,editable on trans when { use.value } make headers,visible on trans when { !use.value } @@ -692,7 +692,7 @@ - default_headers + task_default_headers editable @@ -707,7 +707,7 @@ - allow_header_table_mode + task_allow_header_table_mode editable @@ -721,7 +721,7 @@ - headers_mode + task_headers_mode editable required @@ -736,7 +736,7 @@ - headers_default_mode + task_headers_default_mode editable required @@ -827,49 +827,49 @@ - view_search_type + task_view_search_type visible - show_more_menu + task_show_more_menu visible - is_header_mode_changeable + task_is_header_mode_changeable visible - allow_header_table_mode + task_allow_header_table_mode visible - headers_mode + task_headers_mode visible - headers_default_mode + task_headers_default_mode visible - use_default_headers + use_task_default_headers visible - default_headers + task_default_headers visible From 08ae9b02f4aa00ff30c195b09b77adbdf8e260d5 Mon Sep 17 00:00:00 2001 From: chvostek Date: Thu, 14 May 2026 08:36:29 +0200 Subject: [PATCH 13/49] [ETASK-23] Dynamic view configuration - introduce empty content configuration - fix null node --- .../engine/menu/domain/MenuItemBody.java | 4 +- .../domain/configurations/CaseViewBody.java | 8 ++ .../configurations/CaseViewConstants.java | 2 + .../domain/configurations/TaskViewBody.java | 9 ++ .../configurations/TaskViewConstants.java | 2 + .../menu/case_view_configuration.xml | 107 +++++++++++++++++- .../engine-processes/menu/menu_item.xml | 47 ++++---- .../menu/task_view_configuration.xml | 105 ++++++++++++++++- 8 files changed, 250 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java index 5af588b2fa3..15f73c3e8c0 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -160,7 +160,9 @@ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCas outcome.putDataSetEntry(MenuItemConstants.FIELD_MENU_NAME, FieldType.I18N, this.menuName); outcome.putDataSetEntry(MenuItemConstants.FIELD_MENU_ICON, FieldType.TEXT, this.menuIcon); outcome.putDataSetEntry(MenuItemConstants.FIELD_USE_TABBED_VIEW, FieldType.BOOLEAN, this.useTabbedView); - outcome.putDataSetEntry(MenuItemConstants.FIELD_TAB_NAME, FieldType.I18N, this.tabName); + if (this.tabName != null) { + outcome.putDataSetEntry(MenuItemConstants.FIELD_TAB_NAME, FieldType.I18N, this.tabName); + } outcome.putDataSetEntry(MenuItemConstants.FIELD_TAB_ICON, FieldType.TEXT, this.tabIcon); if (this.identifier != null) { outcome.putDataSetEntry(MenuItemConstants.FIELD_IDENTIFIER, FieldType.TEXT, this.getIdentifier()); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java index 3bb18185d18..7be6230497a 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java @@ -28,6 +28,8 @@ public class CaseViewBody extends ViewBody { private List defaultHeaders; private boolean isHeaderModeChangeable = true; private boolean useDefaultHeaders = true; + private I18nString emptyContentText; + private String emptyContentIcon; private ViewBody chainedView; @@ -72,6 +74,12 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { this.isHeaderModeChangeable); outcome.putDataSetEntry(CaseViewConstants.FIELD_USE_DEFAULT_HEADERS, FieldType.BOOLEAN, this.useDefaultHeaders); + if (this.emptyContentText != null) { + outcome.putDataSetEntry(CaseViewConstants.FIELD_EMPTY_CONTENT_TEXT, FieldType.I18N, + this.emptyContentText); + } + outcome.putDataSetEntry(CaseViewConstants.FIELD_EMPTY_CONTENT_ICON, FieldType.TEXT, + this.emptyContentIcon); return outcome; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java index 99ec5c2f2fa..721cce1b851 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java @@ -18,4 +18,6 @@ public class CaseViewConstants extends ViewConstants { public static final String FIELD_HEADERS_DEFAULT_MODE = "case_headers_default_mode"; public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "case_is_header_mode_changeable"; public static final String FIELD_USE_DEFAULT_HEADERS = "use_case_default_headers"; + public static final String FIELD_EMPTY_CONTENT_TEXT = "case_empty_content_text"; + public static final String FIELD_EMPTY_CONTENT_ICON = "case_empty_content_icon"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java index 9e572485b3b..7ad90df3469 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java @@ -2,6 +2,7 @@ import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.workflow.domain.Case; import lombok.Data; @@ -25,6 +26,8 @@ public class TaskViewBody extends ViewBody { private boolean useDefaultHeaders = true; private List defaultHeaders; private boolean showMoreMenu = true; + private I18nString emptyContentText; + private String emptyContentIcon; @Override public ViewBody getAssociatedViewBody() { @@ -57,6 +60,12 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null); outcome.putDataSetEntry(TaskViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, this.showMoreMenu); + if (this.emptyContentText != null) { + outcome.putDataSetEntry(TaskViewConstants.FIELD_EMPTY_CONTENT_TEXT, FieldType.I18N, + this.emptyContentText); + } + outcome.putDataSetEntry(TaskViewConstants.FIELD_EMPTY_CONTENT_ICON, FieldType.TEXT, + this.emptyContentIcon); return outcome; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java index 32e9b716f67..771f27726ce 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java @@ -13,4 +13,6 @@ public class TaskViewConstants extends ViewConstants { public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "task_allow_header_table_mode"; public static final String FIELD_USE_DEFAULT_HEADERS = "use_task_default_headers"; public static final String FIELD_SHOW_MORE_MENU = "task_show_more_menu"; + public static final String FIELD_EMPTY_CONTENT_TEXT = "task_empty_content_text"; + public static final String FIELD_EMPTY_CONTENT_ICON = "task_empty_content_icon"; } diff --git a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml index a017c3fce7e..f8a30347617 100644 --- a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml @@ -431,6 +431,41 @@ is_tabbed_var_arc Is tabbed + + case_empty_content_text + Empty content text + If empty, default text will be used + + + case_empty_content_icon + Empty content icon + If empty, default icon will be used + + 0 + + + icon: f.this, + iconPreview: f.case_empty_content_icon_preview; + + if (icon.value == "") { + change iconPreview value {"""]]>} + return + } + + change iconPreview value { + """]]> + icon.value + """]]> + } + + + + + + case_empty_content_icon_preview + Empty content icon preview + + htmltextarea + + @@ -543,7 +578,7 @@ view_dataGroup - 4 + 3 grid case_view_search_type @@ -555,7 +590,7 @@ 0 0 1 - 2 + 1 outline @@ -566,14 +601,56 @@ editable - 2 + 1 0 1 - 2 + 1 + + outline + + + + case_empty_content_text + + editable + + + 0 + 1 + 1 + 1 + + outline + + + + case_empty_content_icon + + editable + + + 1 + 1 + 1 + 1 outline + + case_empty_content_icon_preview + + visible + + + 2 + 1 + 1 + 1 + + standard + + advanced_content_form @@ -581,9 +658,9 @@ 0 - 1 + 2 1 - 4 + 3 outline @@ -1159,6 +1236,24 @@ visible + + case_empty_content_text + + visible + + + + case_empty_content_icon + + visible + + + + case_empty_content_icon_preview + + visible + + create_case_button_title diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 140c69c6379..8b35a669b23 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -486,6 +486,9 @@ tab_name Name of the item Will be shown in tab + + new com.netgrif.application.engine.petrinet.domain.I18nString("View", [sk: "Zobrazenie", de: "Die Ansicht"]) + view_configuration_type @@ -1444,7 +1447,7 @@ configuration_view - 4 + 3 grid use_custom_view @@ -1497,7 +1500,7 @@ 1 0 1 - 3 + 2 outline @@ -1531,27 +1534,13 @@ - - tab_name - - editable - - - 2 - 0 - 1 - 1 - - outline - - use_tab_icon editable - 3 + 2 0 1 1 @@ -1571,7 +1560,7 @@ - tab_icon + tab_name editable @@ -1584,16 +1573,30 @@ outline + + tab_icon + + editable + + + 1 + 1 + 1 + 1 + + outline + + tab_icon_preview visible - 1 + 2 1 1 - 2 + 1 standard @@ -1607,7 +1610,7 @@ 0 2 1 - 4 + 3 outline @@ -1654,7 +1657,7 @@ 0 3 1 - 4 + 3 outline diff --git a/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml index c9f25059274..b8298bfa552 100644 --- a/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml @@ -323,6 +323,41 @@ + + task_empty_content_text + Empty content text + If empty, default text will be used + + + task_empty_content_icon + Empty content icon + If empty, default icon will be used + + 0 + + + icon: f.this, + iconPreview: f.task_empty_content_icon_preview; + + if (icon.value == "") { + change iconPreview value {"""]]>} + return + } + + change iconPreview value { + """]]> + icon.value + """]]> + } + + + + + + task_empty_content_icon_preview + Empty content icon preview + + htmltextarea + + @@ -419,7 +454,7 @@ view_dataGroup - 4 + 3 grid task_view_search_type @@ -431,7 +466,7 @@ 0 0 1 - 3 + 1 outline @@ -442,7 +477,7 @@ editable - 3 + 1 0 1 1 @@ -451,7 +486,7 @@ - advanced_content_form + task_empty_content_text editable @@ -459,7 +494,49 @@ 0 1 1 - 4 + 1 + + outline + + + + task_empty_content_icon + + editable + + + 1 + 1 + 1 + 1 + + outline + + + + task_empty_content_icon_preview + + visible + + + 2 + 1 + 1 + 1 + + standard + + + + advanced_content_form + + editable + + + 0 + 2 + 1 + 3 outline @@ -838,6 +915,24 @@ visible + + task_empty_content_text + + visible + + + + task_empty_content_icon + + visible + + + + task_empty_content_icon_preview + + visible + + task_is_header_mode_changeable From 8e6f3db68fd7007d1ccbcdd19d9766b4b68a674d Mon Sep 17 00:00:00 2001 From: chvostek Date: Thu, 14 May 2026 14:58:50 +0200 Subject: [PATCH 14/49] [ETASK-23] Dynamic view configuration - fix test - update doc --- .../engine/menu/service/MenuItemService.java | 10 ++++------ .../application/engine/menu/MenuItemServiceTest.java | 7 +++---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index 27a5f80c6c7..4fc58a05749 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -402,13 +402,11 @@ public Case removeChildItemFromParent(String folderId, Case childItem) { } /** - * Retrieves menu item data along with its associated view configuration data. The method collects immediate - * data from the menu item case and recursively traverses through all associated view configuration cases, - * aggregating their immediate data into a single map. - * todo 23 doc + * Retrieves menu item data groups for the specified case and locale. + * * @param caseId identifier of the menu item case - * @return map where keys are process identifiers (with "_configuration" suffix removed for view cases) and - * values are lists of immediate data fields from the corresponding cases + * @param locale locale to use for retrieving localized data + * @return list of data groups from the menu item case */ @Override public List getMenuItemData(String caseId, Locale locale) { diff --git a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java index c1df0ebdf78..a6c4d428352 100644 --- a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java +++ b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java @@ -6,11 +6,11 @@ import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; +import com.netgrif.application.engine.petrinet.domain.DataGroup; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.startup.SuperCreator; import com.netgrif.application.engine.workflow.domain.Case; -import com.netgrif.application.engine.workflow.domain.eventoutcomes.dataoutcomes.GetDataGroupsEventOutcome; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -113,15 +113,14 @@ public void removeChildItemFromParentTest() { @Test public void getMenuItemDataTest() throws TransitionNotExecutableException { - // todo 23 fix test assertThrows(IllegalArgumentException.class, () -> menuItemService.getMenuItemData("wrongCaseId", Locale.getDefault())); Case menuItemCase = createDefaultMenuItem("my_menu_item", new I18nString("This is name", Map.of("sk", "Toto je nazov"))); login(); - GetDataGroupsEventOutcome result = menuItemService.getMenuItemData(menuItemCase.getStringId(), Locale.getDefault()); - assertTrue(result != null); + List result = menuItemService.getMenuItemData(menuItemCase.getStringId(), Locale.getDefault()); + assertTrue(result != null && !result.isEmpty()); } @Test From bfc90b15cd4824306aa1eaa9b765b7d3fa6441a6 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 15 May 2026 14:33:05 +0200 Subject: [PATCH 15/49] [ETASK-23] Dynamic view configuration - begin updating filter --- .../petriNets/engine-processes/filter.xml | 14 ++-- .../menu/case_view_configuration.xml | 81 +++++++++---------- 2 files changed, 44 insertions(+), 51 deletions(-) diff --git a/src/main/resources/petriNets/engine-processes/filter.xml b/src/main/resources/petriNets/engine-processes/filter.xml index 0be99e1940f..3e721c26fc5 100644 --- a/src/main/resources/petriNets/engine-processes/filter.xml +++ b/src/main/resources/petriNets/engine-processes/filter.xml @@ -121,27 +121,29 @@ i18n_filter_name - + </data> <data type="text"> <id>filter_case_id</id> - <title> + </data> <data type="text"> <id>menu_identifier</id> - <title> + </data> <data type="button"> <id>and_me</id> + <title/> <placeholder name="and">AND</placeholder> </data> <data type="button"> <id>and_view</id> + <title/> <placeholder name="and">AND</placeholder> </data> <data type="taskRef"> <id>taskref_and_parent</id> - <title> + </data> <data type="text"> <id>trimmed_origin_view_id</id> @@ -172,7 +174,7 @@ </data> <data type="enumeration_map"> <id>missing_nets_translation</id> - <title> + <options> <option key="sk">Zoznam chýbajúcich procesov pre aktuálny filter:</option> <option key="en">List of missing processes for current filter:</option> @@ -181,7 +183,7 @@ </data> <data type="taskRef"> <id>my_full_filter</id> - <title> + </data> <data type="multichoice_map"> <id>allowed_nets</id> diff --git a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml index f8a30347617..b93951a6913 100644 --- a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml @@ -224,6 +224,39 @@ <allowedNet>filter</allowedNet> </allowedNets> </data> + <data type="enumeration_map"> + <id>available_filter_cases</id> + <title/> + <component> + <name>caseref</name> + <property key="headers">meta-title,meta-author,filter-visibility</property> + <property key="allowedNets">filter</property> + <property key="createCase">true</property> + <property key="search">true</property> + <property key="filter">true</property> + <property key="filterQuery">{ "query": "processIdentifier:filter AND (author:<<me>> OR dataSet.visibility.keyValue:public) AND dataSet.filter_type.keyValue:Case" }</property> + </component> + <event type="set"> + <id>0</id> + <actions phase="post"> + <action> + available_filter_cases: f.this, + current_filter_preview: f.current_filter_preview; + + if (available_filter_cases.value == null || available_filter_cases.value == "") { + change current_filter_preview value { [] } + return + } + change current_filter_preview value { + return [findTask({it.caseId.eq(available_filter_cases.value).and(it.transitionId.eq("view_filter"))}).stringId] + } + </action> + </actions> + </event> + <allowedNets> + <allowedNet>filter</allowedNet> + </allowedNets> + </data> <!-- VIEW CONFIGURATION DATA --> <data type="enumeration_map" immediate="true"> @@ -699,7 +732,7 @@ <cols>4</cols> <layout>grid</layout> <dataRef> - <id>filter_autocomplete_selection</id> + <id>available_filter_cases</id> <logic> <behavior>editable</behavior> </logic> @@ -707,48 +740,6 @@ <x>0</x> <y>0</y> <rows>1</rows> - <cols>3</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>update_filter</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>3</x> - <y>0</y> - <rows>1</rows> - <cols>1</cols> - <template>material</template> - <appearance>standard</appearance> - </layout> - </dataRef> - <dataRef> - <id>selected_filter_preview</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>0</x> - <y>1</y> - <rows>1</rows> - <cols>4</cols> - <template>material</template> - <appearance>standard</appearance> - </layout> - </dataRef> - <dataRef> - <id>filter_header</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>0</x> - <y>2</y> - <rows>1</rows> <cols>4</cols> <template>material</template> <appearance>outline</appearance> @@ -761,7 +752,7 @@ </logic> <layout> <x>0</x> - <y>3</y> + <y>1</y> <rows>1</rows> <cols>4</cols> <template>material</template> @@ -1189,7 +1180,7 @@ <alignment>start</alignment> <stretch>true</stretch> <dataRef> - <id>filter_autocomplete_selection</id> + <id>available_filter_cases</id> <logic> <behavior>visible</behavior> </logic> From dfea0b87e42feb030c2e8badec1db1c2dbb52a0d Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Fri, 15 May 2026 15:58:10 +0200 Subject: [PATCH 16/49] [ETASK-23] Dynamic view configuration - introduce menu item configuration templates --- .../engine/menu/domain/templates/README.md | 48 +++++++++++++++++ .../templates/SimpleCaseViewTemplate.java | 47 +++++++++++++++++ .../templates/SimpleTaskViewTemplate.java | 47 +++++++++++++++++ .../templates/SingleTaskViewTemplate.java | 48 +++++++++++++++++ .../templates/TabbedCaseViewTemplate.java | 52 +++++++++++++++++++ .../templates/TabbedTaskViewTemplate.java | 47 +++++++++++++++++ .../templates/TabbedTicketViewTemplate.java | 47 +++++++++++++++++ .../menu/domain/templates/Template.java | 10 ++++ .../engine/menu/service/MenuItemService.java | 6 +++ .../menu/service/MenuItemTemplateHolder.java | 52 +++++++++++++++++++ .../service/interfaces/IMenuItemService.java | 1 + .../engine-processes/menu/menu_item.xml | 30 +++++++++++ 12 files changed, 435 insertions(+) create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/README.md create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/README.md b/src/main/java/com/netgrif/application/engine/menu/domain/templates/README.md new file mode 100644 index 00000000000..7dd8af166fa --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/README.md @@ -0,0 +1,48 @@ +# Menu Item Template Creation Guide + +## Overview + +Templates provide predefined configurations for menu items that can be reused throughout the application. Each template +encapsulates a complete menu item structure with its associated views and filters. + +## Template Structure + +A template must implement the `Template` interface with three core components: + +- **Identifier**: A unique string constant that identifies the template +- **Name**: An internationalized string (I18nString) providing the display name in multiple languages +- **Template Body**: A MenuItemBody instance containing the complete menu item configuration + +## Creation Steps + +1. **Create Template Class** + - Implement the `Template` interface + - Define a unique public static IDENTIFIER constant + - Create a private static I18nString NAME with translations + - Declare a private static MenuItemBody TEMPLATE field + +2. **Build Template Configuration** + - Implement a private static `buildTemplate()` method + - Instantiate MenuItemBody and configure its properties + - Create and configure the appropriate view body (CaseViewBody, TaskViewBody, ...) + - Set up FilterBody for data filtering requirements + - Configure chained views if needed (e.g., TaskViewBody within CaseViewBody) + - Wire all components together through setter methods + +3. **Implement Interface Methods** + - Return IDENTIFIER from `getIdentifier()` + - Return NAME from `getName()` + - Return TEMPLATE from `getTemplate()` + +4. **Register Template** + - Add the template to the `MenuItemTemplateHolder.templates` map + - Use the template's IDENTIFIER as the key + - Instantiate the template class as the value + +## Best Practices + +- Keep identifiers lowercase with underscores for consistency +- Provide translations for all supported languages in the NAME field +- Build template configuration statically to ensure immutability +- Ensure all required view and filter configurations are properly initialized +- Use descriptive identifiers that reflect the template's purpose diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java new file mode 100644 index 00000000000..1fc40d4e94f --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java @@ -0,0 +1,47 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class SimpleCaseViewTemplate implements Template { + + public static final String IDENTIFIER = "simple_case_view"; + + private static final I18nString NAME = new I18nString("Simple case view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + CaseViewBody caseViewBody = new CaseViewBody(); + // todo 23 task view body data + + FilterBody filterBody = new FilterBody(); + // todo 23 filter body data + + caseViewBody.setFilterBody(filterBody); + menuItemBody.setView(caseViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java new file mode 100644 index 00000000000..98df69c9bfb --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java @@ -0,0 +1,47 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class SimpleTaskViewTemplate implements Template { + + public static final String IDENTIFIER = "simple_task_view"; + + private static final I18nString NAME = new I18nString("Simple task view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + TaskViewBody taskViewBody = new TaskViewBody(); + // todo 23 task view body data + + FilterBody filterBody = new FilterBody(); + // todo 23 filter body data + + taskViewBody.setFilterBody(filterBody); + menuItemBody.setView(taskViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java new file mode 100644 index 00000000000..893b505b740 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java @@ -0,0 +1,48 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.SingleTaskViewBody; +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class SingleTaskViewTemplate implements Template { + + public static final String IDENTIFIER = "single_task_view"; + + private static final I18nString NAME = new I18nString("Single task view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + SingleTaskViewBody singleTaskViewBody = new SingleTaskViewBody(); + // todo 23 task view body data + + FilterBody filterBody = new FilterBody(); + // todo 23 filter body data + + singleTaskViewBody.setFilterBody(filterBody); + menuItemBody.setView(singleTaskViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java new file mode 100644 index 00000000000..88656826c20 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java @@ -0,0 +1,52 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class TabbedCaseViewTemplate implements Template { + + public static final String IDENTIFIER = "tabbed_case_view"; + + private static final I18nString NAME = new I18nString("Tabbed case view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + CaseViewBody caseViewBody = new CaseViewBody(); + // todo 23 case view body data + + FilterBody filterBody = new FilterBody(); + // todo 23 filter body data + + TaskViewBody taskViewBody = new TaskViewBody(); + // todo 23 task view body data + + caseViewBody.setFilterBody(filterBody); + caseViewBody.setChainedView(taskViewBody); + menuItemBody.setView(caseViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java new file mode 100644 index 00000000000..38be03aaae9 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java @@ -0,0 +1,47 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class TabbedTaskViewTemplate implements Template { + + public static final String IDENTIFIER = "tabbed_task_view"; + + private static final I18nString NAME = new I18nString("Tabbed task view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + TaskViewBody taskViewBody = new TaskViewBody(); + // todo 23 task view body data + + FilterBody filterBody = new FilterBody(); + // todo 23 filter body data + + taskViewBody.setFilterBody(filterBody); + menuItemBody.setView(taskViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java new file mode 100644 index 00000000000..3b1eb03e775 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java @@ -0,0 +1,47 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.SingleTaskViewBody; +import com.netgrif.application.engine.menu.domain.configurations.TabbedTicketViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class TabbedTicketViewTemplate implements Template { + + public static final String IDENTIFIER = "tabbed_ticket_view"; + + private static final I18nString NAME = new I18nString("Tabbed ticket view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + TabbedTicketViewBody tabbedTicketViewBody = new TabbedTicketViewBody(); + // todo 23 case view body data + + SingleTaskViewBody singleTaskViewBody = new SingleTaskViewBody(); + // todo 23 task view body data + + tabbedTicketViewBody.setChainedView(singleTaskViewBody); + menuItemBody.setView(tabbedTicketViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java new file mode 100644 index 00000000000..ecbc61f3ae3 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java @@ -0,0 +1,10 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +public interface Template { + String getIdentifier(); + I18nString getName(); + MenuItemBody getTemplate(); +} diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index 4fc58a05749..acf42be17d8 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -415,6 +415,12 @@ public List<DataGroup> getMenuItemData(String caseId, Locale locale) { return dataService.getDataGroups(taskId, locale).getData(); } + // todo 23 doc + @Override + public void handleConfigurationTemplate(Case menuItemCase) { + // todo 23 + } + protected Case findCase(String processIdentifier, String query) { CaseSearchRequest request = CaseSearchRequest.builder() .process(Collections.singletonList(new CaseSearchRequest.PetriNet(processIdentifier))) diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java new file mode 100644 index 00000000000..10070526211 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java @@ -0,0 +1,52 @@ +package com.netgrif.application.engine.menu.service; + +import com.netgrif.application.engine.menu.domain.templates.*; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Holder class that manages predefined menu item templates. + * <p> + * This class provides a central registry for menu item templates that can be used + * throughout the application. Templates are identified by unique string identifiers + * and can be retrieved or transformed into user-selectable options. + * </p> + */ +public class MenuItemTemplateHolder { + /** + * Map of available menu item templates indexed by their unique identifiers. + * <p> + * Each entry maps a template identifier to its corresponding Template instance. + * This map is immutable and initialized with predefined templates. + * The key is the template identifier (String), and the value is the Template instance. + * </p> + */ + public static Map<String, Template> templates = Map.of( + TabbedCaseViewTemplate.IDENTIFIER, new TabbedCaseViewTemplate(), + TabbedTaskViewTemplate.IDENTIFIER, new TabbedTaskViewTemplate(), + SimpleCaseViewTemplate.IDENTIFIER, new SimpleCaseViewTemplate(), + SimpleTaskViewTemplate.IDENTIFIER, new SimpleTaskViewTemplate(), + SingleTaskViewTemplate.IDENTIFIER, new SingleTaskViewTemplate(), + TabbedTicketViewTemplate.IDENTIFIER, new TabbedTicketViewTemplate() + ); + + /** + * Transforms the available templates into a map of selectable options. + * <p> + * This method converts the templates map into a format suitable for displaying + * as user-selectable options, where the key is the template identifier and the + * value is the internationalized name of the template. + * </p> + * + * @return a map where keys are template identifiers and values are internationalized template names + */ + public static Map<String, I18nString> transformToOptions() { + return templates.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue().getName() + )); + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java index 90cd699331b..5f0ef140ddf 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java @@ -31,6 +31,7 @@ public interface IMenuItemService { Case duplicateItem(Case originItem, I18nString newTitle, String newIdentifier) throws TransitionNotExecutableException; Case removeChildItemFromParent(String folderId, Case childItem); List<DataGroup> getMenuItemData(String caseId, Locale locale); + void handleConfigurationTemplate(Case menuItemCase); /** * Gets all tabbed or non-tabbed views diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 8b35a669b23..01ebb9eae98 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -630,6 +630,13 @@ </actions> </event> </data> + <data type="enumeration_map"> + <id>configuration_templates</id> + <title name="todo 23">Pick a configuration template + + com.netgrif.application.engine.menu.service.MenuItemTemplateHolder.transformToOptions() + + @@ -812,6 +819,20 @@ outline + + configuration_templates + + editable + + + 0 + 2 + 1 + 4 + + outline + + finish @@ -827,6 +848,15 @@ changeMenuItem useCase uri { newUri } change name value { new com.netgrif.application.engine.petrinet.domain.I18nString(identifier.value) } + + configuration_templates: f.configuration_templates; + + if (configuration_templates.value == null || configuration_templates.value == "") { + return + } + + menuItemService.handleConfigurationTemplate(configuration_templates.value) + Create From 0d40b4b5689bbe3452d8a0be5191c2ec8c667b76 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 18 May 2026 11:33:44 +0200 Subject: [PATCH 17/49] [ETASK-23] Dynamic view configuration - finish implementation of template configurations --- .../domain/ConfigurationTemplateOutcome.java | 20 +++++++++ .../engine/menu/domain/MenuItemBody.java | 32 ++++++++++++-- .../engine/menu/domain/MenuItemConstants.java | 1 + .../templates/SimpleCaseViewTemplate.java | 11 +---- .../templates/SimpleTaskViewTemplate.java | 11 +---- .../templates/SingleTaskViewTemplate.java | 12 +---- .../templates/TabbedCaseViewTemplate.java | 17 ++----- .../templates/TabbedTaskViewTemplate.java | 12 ++--- .../templates/TabbedTicketViewTemplate.java | 7 +-- .../menu/domain/templates/Template.java | 25 +++++++++++ .../engine/menu/service/MenuItemService.java | 44 ++++++++++++++++--- .../menu/service/MenuItemTemplateHolder.java | 18 +++++++- .../service/interfaces/IMenuItemService.java | 3 +- .../engine-processes/menu/menu_item.xml | 9 ++-- 14 files changed, 148 insertions(+), 74 deletions(-) create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java b/src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java new file mode 100644 index 00000000000..e31d6bbdbea --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java @@ -0,0 +1,20 @@ +package com.netgrif.application.engine.menu.domain; + +import java.util.HashMap; +import java.util.Map; + +public class ConfigurationTemplateOutcome { + // todo 23 doc + public final Map mapping; + + public ConfigurationTemplateOutcome() { + this.mapping = new HashMap<>(); + } + + public ConfigurationTemplateOutcome(ToDataSetOutcome toDataSetOutcome) { + this(); + toDataSetOutcome.getDataSet() + .forEach((fieldId, fieldMap) -> this.mapping.put(fieldId, fieldMap.get("value"))); + } + +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java index 15f73c3e8c0..bb19500b411 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -2,6 +2,7 @@ import com.netgrif.application.engine.menu.domain.configurations.ViewBody; import com.netgrif.application.engine.menu.domain.configurations.ViewConstants; +import com.netgrif.application.engine.menu.domain.templates.Template; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; @@ -31,7 +32,7 @@ public class MenuItemBody { private String customViewSelector; private boolean isAutoSelect = false; - private boolean useTabbedView; + private boolean useTabbedView = true; private String tabIcon; private boolean useTabIcon = true; private I18nString tabName; @@ -108,8 +109,14 @@ public void setTabName(String name) { } public void setView(ViewBody viewBody) { - this.view = viewBody; - this.useTabbedView = viewBody == null || viewBody.getViewType().isTabbed(); + if (viewBody != null) { + this.view = viewBody; + MenuItemView viewType = viewBody.getViewType(); + if (viewType.isTabbed() != viewType.isUntabbed()) { + // if isTabbed == isUntabbed we cannot determine the result value + this.useTabbedView = viewType.isTabbed(); + } + } } /** @@ -176,6 +183,24 @@ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCas outcome.putDataSetEntryOptions(MenuItemConstants.FIELD_ALLOWED_ROLES, FieldType.MULTICHOICE_MAP, this.allowedRoles); outcome.putDataSetEntryOptions(MenuItemConstants.FIELD_BANNED_ROLES, FieldType.MULTICHOICE_MAP, this.bannedRoles); + outcome = toDataSetWithView(viewCase, outcome); + + return outcome; + } + + /** + * Transforms minimal attributes into dataSet for view configuration by template. + * + * @param viewCase case instance of view + * @return {@link ToDataSetOutcome} object with dataSet containing only view-related fields + */ + public ToDataSetOutcome toDataSetByConfigTemplate(Case viewCase) { + ToDataSetOutcome outcome = new ToDataSetOutcome(); + outcome.putDataSetEntry(MenuItemConstants.FIELD_USE_TABBED_VIEW, FieldType.BOOLEAN, this.useTabbedView); + return toDataSetWithView(viewCase, outcome); + } + + protected ToDataSetOutcome toDataSetWithView(Case viewCase, ToDataSetOutcome outcome) { if (viewCase != null) { outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE, FieldType.ENUMERATION_MAP, this.view.getViewType().getIdentifier()); @@ -186,7 +211,6 @@ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCas String allDataTaskId = MenuItemUtils.findTaskIdInCase(viewCase, ViewConstants.TRANS_ALL_MENU_DATA_ID); outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM, FieldType.TASK_REF, List.of(allDataTaskId)); } - return outcome; } } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java index 320c0f08ebf..a8405551449 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java @@ -29,6 +29,7 @@ public class MenuItemConstants { public static final String FIELD_VIEW_CONFIGURATION_ID = "view_configuration_id"; public static final String FIELD_VIEW_CONFIGURATION_FORM = "view_configuration_form"; public static final String FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM = "view_configuration_all_data_form"; + public static final String FIELD_CONFIGURATION_TEMPLATES = "configuration_templates"; public static final String TRANS_SETTINGS_ID = "item_settings"; public static final String TRANS_INIT_ID = "system_initialize"; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java index 1fc40d4e94f..a23b243f7eb 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java @@ -1,6 +1,5 @@ package com.netgrif.application.engine.menu.domain.templates; -import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; import com.netgrif.application.engine.petrinet.domain.I18nString; @@ -18,16 +17,10 @@ public class SimpleCaseViewTemplate implements Template { private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(false); CaseViewBody caseViewBody = new CaseViewBody(); - // todo 23 task view body data - - FilterBody filterBody = new FilterBody(); - // todo 23 filter body data - - caseViewBody.setFilterBody(filterBody); + caseViewBody.setFilterBody(Template.defaultCaseFilterBody(NAME)); menuItemBody.setView(caseViewBody); return menuItemBody; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java index 98df69c9bfb..aa4815c2f6e 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java @@ -1,6 +1,5 @@ package com.netgrif.application.engine.menu.domain.templates; -import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.petrinet.domain.I18nString; @@ -18,16 +17,10 @@ public class SimpleTaskViewTemplate implements Template { private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(false); TaskViewBody taskViewBody = new TaskViewBody(); - // todo 23 task view body data - - FilterBody filterBody = new FilterBody(); - // todo 23 filter body data - - taskViewBody.setFilterBody(filterBody); + taskViewBody.setFilterBody(Template.defaultTaskFilterBody(NAME)); menuItemBody.setView(taskViewBody); return menuItemBody; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java index 893b505b740..98ea5f60190 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java @@ -1,9 +1,7 @@ package com.netgrif.application.engine.menu.domain.templates; -import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.SingleTaskViewBody; -import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.petrinet.domain.I18nString; import java.util.Map; @@ -19,16 +17,10 @@ public class SingleTaskViewTemplate implements Template { private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(false); SingleTaskViewBody singleTaskViewBody = new SingleTaskViewBody(); - // todo 23 task view body data - - FilterBody filterBody = new FilterBody(); - // todo 23 filter body data - - singleTaskViewBody.setFilterBody(filterBody); + singleTaskViewBody.setFilterBody(Template.defaultTaskFilterBody(NAME)); menuItemBody.setView(singleTaskViewBody); return menuItemBody; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java index 88656826c20..609384ad046 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java @@ -1,6 +1,5 @@ package com.netgrif.application.engine.menu.domain.templates; -import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; @@ -13,26 +12,18 @@ public class TabbedCaseViewTemplate implements Template { public static final String IDENTIFIER = "tabbed_case_view"; private static final I18nString NAME = new I18nString("Tabbed case view", - Map.of("sk", "", "de", "")); // todo 23 translate + Map.of("sk", "Zobrazenie prípadov v záložkách", "de", "Registerkartenansicht für Fälle")); private static final MenuItemBody TEMPLATE = buildTemplate(); private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(true); CaseViewBody caseViewBody = new CaseViewBody(); - // todo 23 case view body data - FilterBody filterBody = new FilterBody(); - // todo 23 filter body data - - TaskViewBody taskViewBody = new TaskViewBody(); - // todo 23 task view body data - - caseViewBody.setFilterBody(filterBody); - caseViewBody.setChainedView(taskViewBody); + caseViewBody.setFilterBody(Template.defaultCaseFilterBody(NAME)); + caseViewBody.setChainedView(new TaskViewBody()); menuItemBody.setView(caseViewBody); return menuItemBody; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java index 38be03aaae9..47877bf8c1c 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java @@ -1,6 +1,5 @@ package com.netgrif.application.engine.menu.domain.templates; -import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.petrinet.domain.I18nString; @@ -12,22 +11,17 @@ public class TabbedTaskViewTemplate implements Template { public static final String IDENTIFIER = "tabbed_task_view"; private static final I18nString NAME = new I18nString("Tabbed task view", - Map.of("sk", "", "de", "")); // todo 23 translate + Map.of("sk", "Zobrazenie úloh v záložkách", "de", "Aufgabenansicht in Registerkarten")); private static final MenuItemBody TEMPLATE = buildTemplate(); private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(true); TaskViewBody taskViewBody = new TaskViewBody(); - // todo 23 task view body data - FilterBody filterBody = new FilterBody(); - // todo 23 filter body data - - taskViewBody.setFilterBody(filterBody); + taskViewBody.setFilterBody(Template.defaultTaskFilterBody(NAME)); menuItemBody.setView(taskViewBody); return menuItemBody; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java index 3b1eb03e775..347b0d96cdb 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java @@ -18,15 +18,12 @@ public class TabbedTicketViewTemplate implements Template { private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(true); TabbedTicketViewBody tabbedTicketViewBody = new TabbedTicketViewBody(); - // todo 23 case view body data SingleTaskViewBody singleTaskViewBody = new SingleTaskViewBody(); - // todo 23 task view body data - + singleTaskViewBody.setFilterBody(Template.defaultTaskFilterBody(NAME)); tabbedTicketViewBody.setChainedView(singleTaskViewBody); menuItemBody.setView(tabbedTicketViewBody); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java index ecbc61f3ae3..c6c2a462842 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java @@ -1,10 +1,35 @@ package com.netgrif.application.engine.menu.domain.templates; +import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.petrinet.domain.I18nString; +import java.util.List; + public interface Template { String getIdentifier(); I18nString getName(); MenuItemBody getTemplate(); + + static FilterBody defaultTaskFilterBody(I18nString name) { + FilterBody filterBody = new FilterBody(); + filterBody.setIcon("filter"); + filterBody.setType("Task"); + filterBody.setVisibility("private"); + filterBody.setTitle(name); + filterBody.setQuery("*"); + filterBody.setAllowedNets(List.of()); + return filterBody; + } + + static FilterBody defaultCaseFilterBody(I18nString name) { + FilterBody filterBody = new FilterBody(); + filterBody.setIcon("filter"); + filterBody.setType("Case"); + filterBody.setVisibility("private"); + filterBody.setTitle(name); + filterBody.setQuery("*"); + filterBody.setAllowedNets(List.of()); + return filterBody; + } } diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index acf42be17d8..f7626296cfd 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -5,12 +5,10 @@ import com.netgrif.application.engine.auth.service.interfaces.IUserService; import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService; import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest; -import com.netgrif.application.engine.menu.domain.FilterBody; -import com.netgrif.application.engine.menu.domain.MenuItemBody; -import com.netgrif.application.engine.menu.domain.MenuItemConstants; -import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.menu.domain.*; import com.netgrif.application.engine.menu.domain.configurations.ViewBody; import com.netgrif.application.engine.menu.domain.configurations.ViewConstants; +import com.netgrif.application.engine.menu.domain.templates.Template; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.DataGroup; @@ -415,10 +413,42 @@ public List getMenuItemData(String caseId, Locale locale) { return dataService.getDataGroups(taskId, locale).getData(); } - // todo 23 doc + /** + * Handles the application of a configuration template to a menu item case. + *

+ * This method retrieves the selected configuration template from the menu item case, + * loads the corresponding template definition, and applies it by creating or updating + * the associated view configuration. If no template is selected, the method returns + * without making any changes. + *

+ * + * @param menuItemCase the menu item case to which the configuration template should be applied + * @return a ConfigurationTemplateOutcome containing the dataSet outcome from applying the template, + * or an empty outcome if no template was selected + * @throws TransitionNotExecutableException if the workflow transition required for applying the template configuration cannot be executed + * @throws IllegalArgumentException if the selected template identifier does not correspond to any registered template + */ @Override - public void handleConfigurationTemplate(Case menuItemCase) { - // todo 23 + public ConfigurationTemplateOutcome handleConfigurationTemplate(Case menuItemCase) throws TransitionNotExecutableException { + String selectedTemplate = (String) menuItemCase.getFieldValue(MenuItemConstants.FIELD_CONFIGURATION_TEMPLATES); + if (selectedTemplate == null || selectedTemplate.isEmpty()) { + return new ConfigurationTemplateOutcome(); + } + + String menuItemIdentifier = (String) menuItemCase.getFieldValue(MenuItemConstants.FIELD_IDENTIFIER); + log.debug("Handling configuration template selection for menu item: [{}, {}] and configuration template: {}", + menuItemCase.getStringId(), menuItemIdentifier, selectedTemplate); + Optional