From a36bec20c7fa4ed7e7d0ec8a451bb982ec293cde Mon Sep 17 00:00:00 2001
From: Sebastian-Ion TINCU <Sebastian-Ion.TINCU@ext.ec.europa.eu>
Date: Mon, 24 Jun 2024 07:13:02 +0200
Subject: [PATCH] EDELIVERY-11590 SMP UI Improvements Breadcrumbs

Refactor existing code.
---
 .../breadcrumb-item.component.html            | 15 +++-
 .../breadcrumb-item.component.ts              | 18 ++---
 .../sidenav/navigation-model.service.ts       | 79 ++++++++++++++-----
 3 files changed, 81 insertions(+), 31 deletions(-)

diff --git a/smp-angular/src/app/window/breadcrumb/breadcrumb-item/breadcrumb-item.component.html b/smp-angular/src/app/window/breadcrumb/breadcrumb-item/breadcrumb-item.component.html
index 060f05687..4906e5502 100644
--- a/smp-angular/src/app/window/breadcrumb/breadcrumb-item/breadcrumb-item.component.html
+++ b/smp-angular/src/app/window/breadcrumb/breadcrumb-item/breadcrumb-item.component.html
@@ -1,10 +1,17 @@
-<a
-  class="smp-breadcrumb-item"
+<a *ngIf="clickable" class="smp-breadcrumb-item"
    (click)="triggerClickEvent()" [matTooltip]="description">
+  <ng-container [ngTemplateOutlet]="content"></ng-container>
+</a>
+
+<div *ngIf="!clickable" class="smp-breadcrumb-item non-clickable">
+  <ng-container [ngTemplateOutlet]="content"></ng-container>
+</div>
+
+<ng-template #content>
   <div class="smp-breadcrumb-arrow top" [ngClass]="{'smp-breadcrumb-item-selected': value.selected}"></div>
   <div class="smp-breadcrumb-arrow bottom" [ngClass]="{'smp-breadcrumb-item-selected': value.selected}"></div>
-  <div class="smp-breadcrumb-content" >
+  <div class="smp-breadcrumb-content">
     <mat-icon *ngIf="icon" style="vertical-align: middle;">{{icon}}</mat-icon>
     <span>{{name}}</span>
   </div>
-</a>
+</ng-template>
diff --git a/smp-angular/src/app/window/breadcrumb/breadcrumb-item/breadcrumb-item.component.ts b/smp-angular/src/app/window/breadcrumb/breadcrumb-item/breadcrumb-item.component.ts
index 19c7c9045..8780f83c0 100644
--- a/smp-angular/src/app/window/breadcrumb/breadcrumb-item/breadcrumb-item.component.ts
+++ b/smp-angular/src/app/window/breadcrumb/breadcrumb-item/breadcrumb-item.component.ts
@@ -3,7 +3,7 @@ import {NavigationNode} from "../../sidenav/navigation-model.service";
 
 
 /**
- * Top page navigation bar  Breadcrumb-  side navigation panel of the DomiSMP. The component shows all tools/pages according to user role and permissions
+ * Top page navigation bar  Breadcrumbs - side navigation panel of the DomiSMP. The component shows all tools/pages according to user role and permissions
  *
  * @author Joze Rihtarsic
  * @since 5.0
@@ -18,23 +18,23 @@ export class BreadcrumbItemComponent {
   @Output() onClickEvent: EventEmitter<NavigationNode> = new EventEmitter();
   @Input() value : NavigationNode;
 
-
-  constructor() {
-  }
-
-  get icon(){
+  get icon() {
     return this.value.icon;
   }
-  get name(){
+
+  get name() {
     return this.value.name;
   }
 
-  get description(){
+  get description() {
     return this.value.code;
   }
 
   triggerClickEvent() {
-    this.onClickEvent.emit(this.value);
+    this.clickable && this.onClickEvent.emit(this.value);
   }
 
+  get clickable(): boolean {
+    return this.value.clickable;
+  }
 }
diff --git a/smp-angular/src/app/window/sidenav/navigation-model.service.ts b/smp-angular/src/app/window/sidenav/navigation-model.service.ts
index 5a7f24406..bacda9300 100644
--- a/smp-angular/src/app/window/sidenav/navigation-model.service.ts
+++ b/smp-angular/src/app/window/sidenav/navigation-model.service.ts
@@ -32,7 +32,6 @@ let PUBLIC_NAVIGATION_TREE: NavigationNode = {
           icon: "find_in_page",
           tooltip: "Search registered resources",
           routerLink: "search-resources",
-
         }
       ]
     }
@@ -51,6 +50,7 @@ export interface NavigationNode {
   tooltip?: string;
   routerLink?: string;
   children?: NavigationNode[];
+  clickable?: boolean;
   selected?: boolean;
   transient?: boolean; // if true then node must be ignored
 }
@@ -107,8 +107,8 @@ export class NavigationService extends MatTreeNestedDataSource<NavigationNode> {
   }
 
   select(node: NavigationNode) {
-
     let targetNode = this.findLeaf(node);
+
     if (targetNode === this.selected) {
       console.log("Already selected skip");
       return
@@ -122,29 +122,53 @@ export class NavigationService extends MatTreeNestedDataSource<NavigationNode> {
       this.selected = targetNode
       this.selected.selected = true;
       this.selectedPath = this.findPathForNode(this.selected, this.rootNode);
+      this.markNodesAsClickable(this.selectedPath);
       this.selectedPathSubject.next(this.selectedPath);
-      let navigationPath: string[] = this.getNavigationPath(this.selectedPath);
-      // navigate to selected path
 
+      // navigate to selected path
+      let navigationPath: string[] = this.getNavigationPath(this.selectedPath);
       this.router.navigate(navigationPath);
     } else {
       this.selectedPathSubject.next(null);
     }
   }
 
+  private markNodesAsClickable(selectedPath: NavigationNode[]) {
+    if (selectedPath) {
+      // reset all  nodes (maybe some previously marked as non-clickable)
+      selectedPath.forEach(value => value.clickable = true);
+
+      if (selectedPath.length) {
+        let leafIndex = selectedPath.length - 1;
+
+        // mark the selected leaf as non-clickable
+        selectedPath[leafIndex].clickable = false;
+
+        // mark the parent of the first leaf in a menu as non-clickable
+        let parent = this.findParent(selectedPath[leafIndex]);
+        if (parent && parent.children && parent.children[0] == selectedPath[leafIndex]) {
+          parent.clickable = false;
+        }
+
+        // mark the root parent as non-clickable when selecting the very first leaf in a three level tree
+        let userRootLeaf = this.getDeepestLeaf(this.rootNode);
+        if (userRootLeaf == selectedPath[leafIndex] && selectedPath.length == 3) {
+          this.rootNode.clickable = false;
+        }
+      }
+    }
+  }
+
   selectPreviousNode() {
     this.select(this.previousSelected)
   }
 
-
   public reset() {
     this.rootNode = PUBLIC_NAVIGATION_TREE;
     this.data = this.rootNode.children;
     this.select(this.rootNode)
-
   }
 
-
   protected getNavigationPath(path: NavigationNode[]): string [] {
     return path.map(node => node.routerLink);
   }
@@ -159,12 +183,31 @@ export class NavigationService extends MatTreeNestedDataSource<NavigationNode> {
   }
 
   protected noTargetChildren(targetNode: NavigationNode): boolean {
-    if (!targetNode || !targetNode.children || targetNode.children.length == 0) {
-      return true;
+    return this.findSiblings(targetNode).length == 0;
+  }
+
+  protected findSiblings(node:NavigationNode): NavigationNode[] {
+    if (!node || !node.children || node.children.length == 0) {
+      return [];
+    }
+
+    return node.children.filter(node => !node.transient);
+  }
+
+  protected findParent(node: NavigationNode): NavigationNode {
+    let path = this.findPathForNode(node, this.rootNode);
+    if (path) {
+      let parentIndex = path.indexOf(node) - 1;
+      return path[parentIndex];
     }
+    return null;
+  }
 
-    let nonTransient = targetNode.children.filter(node => !node.transient);
-    return nonTransient.length == 0;
+  private getDeepestLeaf(currentNode: NavigationNode): NavigationNode {
+    if (this.noTargetChildren(currentNode)) {
+      return currentNode;
+    }
+    return this.getDeepestLeaf(currentNode.children[0]);
   }
 
   /**
@@ -235,10 +278,13 @@ export class NavigationService extends MatTreeNestedDataSource<NavigationNode> {
   }
 
   setNavigationTreeByPath(path: string[], userRootNode: NavigationNode) {
-    // find the node by the navigation
+    this.rootNode = userRootNode;
+    this.data = this.rootNode?.children;
+    this.selectStartNode(path, userRootNode);
+  }
 
+  private selectStartNode(path: string[], userRootNode: NavigationNode) {
     let startNode = userRootNode;
-
     for (let index in path) {
       let pathSegment = path[index];
       // the first node is empty - skip all empty nodes
@@ -249,18 +295,13 @@ export class NavigationService extends MatTreeNestedDataSource<NavigationNode> {
         }
       }
     }
-
-    this.rootNode = userRootNode;
-    this.data = this.rootNode?.children;
     this.select(startNode);
   }
 
-
   getSelectedPathObservable(): Observable<NavigationNode[]> {
     return this.selectedPathSubject.asObservable();
   }
 
-
   /** Add node as child of parent */
   public add(node: NavigationNode, parent: NavigationNode) {
     // add root node
@@ -362,9 +403,11 @@ export class NavigationService extends MatTreeNestedDataSource<NavigationNode> {
       icon: "login",
       name: "Login",
       routerLink: "login",
+      clickable: true,
       selected: true,
       tooltip: "",
       transient: true,
     }
   }
+
 }
-- 
GitLab