Frontend CHANGE move yang changes from session to configuration service
diff --git a/frontend/projects/netconf-lib/src/lib/classes/Modification.ts b/frontend/projects/netconf-lib/src/lib/classes/Modification.ts
new file mode 100644
index 0000000..d7954f1
--- /dev/null
+++ b/frontend/projects/netconf-lib/src/lib/classes/Modification.ts
@@ -0,0 +1,14 @@
+/**

+ * Author: Jakub Man <xmanja00@stud.fit.vutbr.cz>

+ * A single modification

+ */

+import {ModificationType} from './ModificationType';

+

+export class Modification {

+  [path: string]: {

+    type: ModificationType;

+    original: any; // Original value before modification

+    value: any; // A new value

+    data: object; // The node data

+  }

+}

diff --git a/frontend/projects/netconf-lib/src/lib/classes/ModificationType.ts b/frontend/projects/netconf-lib/src/lib/classes/ModificationType.ts
index ee3ef16..84d1289 100644
--- a/frontend/projects/netconf-lib/src/lib/classes/ModificationType.ts
+++ b/frontend/projects/netconf-lib/src/lib/classes/ModificationType.ts
@@ -4,14 +4,14 @@
  */
 export const enum ModificationType {
   /** Creating a new node */
-  Create = "create",
+  Create = 'create',
   /** Changing value of a current leaf node */
-  Change = "change",
+  Change = 'change',
   /** Delete a current node */
-  Delete = "delete",
+  Delete = 'delete',
   /** Replacing a current node */
-  Replace = "replace",
+  Replace = 'replace',
   /** Reordering user-ordered lists or leaf-lists */
-  Reorder = "reorder"
+  Reorder = 'reorder'
 }
 
diff --git a/frontend/projects/netconf-lib/src/lib/classes/session.ts b/frontend/projects/netconf-lib/src/lib/classes/session.ts
index 9600224..09e490b 100644
--- a/frontend/projects/netconf-lib/src/lib/classes/session.ts
+++ b/frontend/projects/netconf-lib/src/lib/classes/session.ts
@@ -8,5 +8,5 @@
   public key: string;

   public device: Device;

   public data?: object[];

-  public modifications: object;

+  public modifications?: object;

 }

diff --git a/frontend/projects/netconf-lib/src/lib/services/configuration.service.ts b/frontend/projects/netconf-lib/src/lib/services/configuration.service.ts
index 29b33f3..dbb2578 100644
--- a/frontend/projects/netconf-lib/src/lib/services/configuration.service.ts
+++ b/frontend/projects/netconf-lib/src/lib/services/configuration.service.ts
@@ -2,23 +2,57 @@
  * Author: Jakub Man <xmanja00@stud.fit.vutbr.cz>

  * Service for configuration changes

  */

-import {Injectable} from '@angular/core';

+import {EventEmitter, Injectable} from '@angular/core';

 import {Session} from '../classes/session';

 import {HttpClient} from '@angular/common/http';

 import {Observable} from 'rxjs';

+import {Modification} from '../classes/Modification';

+import {ModificationType} from '../classes/ModificationType';

+

+interface Modifications {

+  [key: string]: Modification;

+}

 

 @Injectable({

   providedIn: 'root'

 })

 export class ConfigurationService {

 

+

+

+  modifications: Modifications = {};

+

+  public modificationsChanged = new EventEmitter<string>();

+

+

   constructor(public http: HttpClient) {

   }

 

-  public commitChanges(session: Session): Observable<any> {

-    console.log('Modifications: ');

-    console.log(session.modifications);

-    return this.http.post<any>('/netconf/session/commit', {'key': session.key, 'modifications': session.modifications});

+  public commitChanges(sessionKey: string): Observable<any> {

+    return this.http.post<any>('/netconf/session/commit', {'key': sessionKey, 'modifications': this.modifications[sessionKey]});

+  }

+

+  createChangeModification(sessionKey: string, path: string, node: object, newValue: any) {

+    // tslint:disable-next-line:triple-equals

+    if (node['value'] == newValue) {

+      console.log('Value did not change');

+      return;

+    }

+    if (!this.modifications[sessionKey]) {

+      this.modifications[sessionKey] = {};

+    }

+    this.modifications[sessionKey][path] = {

+        'type': ModificationType.Change,

+        'original': node['value'],

+        'value': newValue,

+        'data': node

+    };

+    console.log(this.modifications);

+    this.modificationsChanged.emit(sessionKey);

+  }

+

+  discardModifications(sessionKey: string) {

+    this.modifications[sessionKey] = {};

   }

 

 }

diff --git a/frontend/projects/netconf-lib/src/lib/services/session.service.ts b/frontend/projects/netconf-lib/src/lib/services/session.service.ts
index d39377c..1ec826f 100644
--- a/frontend/projects/netconf-lib/src/lib/services/session.service.ts
+++ b/frontend/projects/netconf-lib/src/lib/services/session.service.ts
@@ -35,7 +35,6 @@
   private _sessions: Session[] = [];

 

   public sessionsChanged: EventEmitter<Session[]> = new EventEmitter<Session[]>();

-  public modificationAdded: EventEmitter<Session> = new EventEmitter<Session>();

 

   addSession(key: string, device: Device) {

     if (!this.doesSessionExists(key)) {

@@ -57,7 +56,7 @@
     return this.http.delete('/netconf/session/' + key)

       .pipe(

         tap(

-          next => {

+          _ => {

             this._sessions.splice(idx, 1);

             this.sessionsChanged.emit(this.sessions);

           }

@@ -65,11 +64,17 @@
       );

   }

 

-  loadOpenSessions(): Observable<Session[]> {

-    return this.http.get<Session[]>('/netconf/sessions');

+  loadOpenSessions(forceReload = false): Observable<Session[]> {

+    if (!forceReload && this.sessions.length !== 0) {

+      return of(this.sessions);

+    }

+    return this.http.get<Session[]>('/netconf/sessions').pipe(

+      tap(data => this.sessions = data)

+    );

   }

 

   destroyAllSessions() {

+    this.sessions = [];

     return this.http.delete('/netconf/sessions');

   }

 

@@ -101,11 +106,7 @@
    * For more information see https://netopeer.liberouter.org/doc/libyang/devel/howtoxpath.html

    */

   public getCompatibleDeviceSessions(path: any): Observable<Session[]> {

-    if (this.sessions.length === 0) {

-      return this.loadOpenSessions();

-    } else {

-      return of(this.sessions);

-    }

+    return this.loadOpenSessions(); // TODO: Path filtering

 

   }

 

@@ -134,35 +135,4 @@
 

   }

 

-  createChangeModification(sessionKey: string, path: string, node: object, newValue: string) {

-    if (node['value'] == newValue) {

-      // No change

-      console.log('Value did not change');

-      return;

-    }

-    const idx = this.findSessionIndex(sessionKey);

-    if (idx < 0) {

-      console.warn('Session "' + sessionKey + '" not found');

-      return;

-    }

-    if (!this.sessions[idx].modifications) {

-      this.sessions[idx].modifications = {};

-    }

-    this.sessions[idx].modifications[path] = {

-      'type': ModificationType.Change,

-      'original': node['value'],

-      'value': newValue,

-      'data': node

-    };

-    this.modificationAdded.emit(this.sessions[idx]);

-  }

-

-  discardModifications(sessionKey: string) {

-    const idx = this.findSessionIndex(sessionKey);

-    if (idx > 0) {

-      this.sessions[idx].modifications = {};

-    }

-

-  }

-

 }

diff --git a/frontend/projects/tools/src/yang-configure/components/confirm-commit.component.ts b/frontend/projects/tools/src/yang-configure/components/confirm-commit.component.ts
index 1e5596c..8cdc38e 100644
--- a/frontend/projects/tools/src/yang-configure/components/confirm-commit.component.ts
+++ b/frontend/projects/tools/src/yang-configure/components/confirm-commit.component.ts
@@ -4,7 +4,7 @@
  */
 
 import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
-import {ConfigurationService, SessionService} from 'netconf-lib';
+import {ConfigurationService, Session} from 'netconf-lib';
 import {NodeControlService} from '../services/node-control.service';
 
 @Component({
@@ -14,25 +14,25 @@
 })
 export class ConfirmCommitComponent implements OnInit {
 
-  @Input() session;
+  @Input() session: Session;
   @Output() shouldClose: EventEmitter<boolean> = new EventEmitter<boolean>();
 
   error = '';
 
   constructor(public configurationService: ConfigurationService,
-              public sessionService: SessionService,
               public nodeControlService: NodeControlService) { }
 
   ngOnInit() {
   }
 
   commitChanges() {
-    this.configurationService.commitChanges(this.session).subscribe(
+    this.configurationService.commitChanges(this.session.key).subscribe(
       success => {
         if (!success.success) {
           this.error = success.message;
         } else {
           this.error = '';
+          this.nodeControlService.confirmNewValue();
           this.shouldClose.emit(true);
         }
       },
@@ -44,7 +44,7 @@
 
   discardChanges() {
     this.nodeControlService.restoreOriginalValuesOnAll();
-    this.sessionService.discardModifications(this.session.key);
+    this.configurationService.discardModifications(this.session.key);
     this.shouldClose.emit(true);
   }
 
diff --git a/frontend/projects/tools/src/yang-configure/components/yang-configure.component.ts b/frontend/projects/tools/src/yang-configure/components/yang-configure.component.ts
index 9fda740..8bd08de 100644
--- a/frontend/projects/tools/src/yang-configure/components/yang-configure.component.ts
+++ b/frontend/projects/tools/src/yang-configure/components/yang-configure.component.ts
@@ -19,6 +19,7 @@
   constructor(

     public sessionService: SessionService,

     public nodeControlService: NodeControlService,

+    public configurationService: ConfigurationService,

     private deviceService: DeviceService,

     public schemaService: SchemasService

   ) {

@@ -38,13 +39,12 @@
     this.sessionService.loadOpenSessions().subscribe(

       sessions => {

         console.log(sessions);

-        this.sessionService.sessions = sessions;

+        // this.sessionService.sessions = sessions;

       }

     );

-    this.sessionService.modificationAdded.subscribe(

-      session => {

-        if (session.key === this.selectedSession.key) {

-          this.selectedSession.modifications = session.modifications; // Update selected session value

+    this.configurationService.modificationsChanged.subscribe(

+      sessionKey => {

+        if (sessionKey === this.selectedSession.key) {

           this.commitChangesShown = true;

         }

       }

diff --git a/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.html b/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.html
index 57a2789..4b601c0 100644
--- a/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.html
+++ b/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.html
@@ -17,29 +17,16 @@
 
   <!-- Node has a value -->
   <span *ngIf="node['value']">:</span>
-  <span class="value" *ngIf="node['value'] && !editing">{{node['value']}}</span>
-  <ng-container *ngIf="node['info']['type'] == 4 || (node['new'] && node['info']['type'] == 8)">
-
-    <ng-container
-      *ngIf="node['info']['config'] &&
-            !node['deleted'] &&
-            (!node['info']['key'] || node['new'])
-            && node['info']['datatypebase'] != 'empty'">
-      <input [class.hidden]="!editing" type="text" autofocus class="editing-input" [(ngModel)]="editingValue">
-      <span class="old-value" *ngIf="node['value'] !== originalValue">{{originalValue}}</span>
-      <i class="fa fa-pencil action-icon" aria-hidden="true" [class.hidden]="editing"
-         (click)="toggleEdit()"
-         title="Edit value"></i>
-
-
-      <i class="fa fa-times action-icon text-danger" aria-hidden="true" [class.hidden]="!editing"
-         (click)="toggleEdit()"></i>
-      <i class="fa fa-check action-icon text-success" aria-hidden="true" [class.hidden]="!editing"
-         (click)="confirmEdit()"></i>
-      <span title="Current value" class="disabled" [class.hidden]="!editing || editingValue == originalValue"></span>
-
-    </ng-container>
-  </ng-container>
+  <nct-yang-node-editable-value
+            *ngIf="node['value']"
+             [editable]="(node['info']['type'] == 4 || (node['new'] && node['info']['type'] == 8)) &&
+                        (node['info']['config'] &&
+                        !node['deleted']) &&
+                        (!node['info']['key'] || node['new'])"
+            [value]="node['value']"
+            (changeSaved)="confirmEdit($event)"
+            (changeDiscarded)="discardChanges($event)"
+  ></nct-yang-node-editable-value>
 
   <!-- Menu -->
   <nct-yang-node-menu *ngIf="node['info']['config']"
@@ -47,6 +34,8 @@
                       [nodeType]="node['info']['type']"
                       (addChildClicked)="addChildNode()"></nct-yang-node-menu>
 
+  <span class="type" *ngIf="node['info']['datatype']">{{node['info']['datatype']}}</span>
+
   <!-- Node description -->
   <nct-yang-node-description [description]="node['info']['dsc']"></nct-yang-node-description>
 
diff --git a/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.scss b/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.scss
index ae8f922..f450f2e 100644
--- a/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.scss
+++ b/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.scss
@@ -10,12 +10,6 @@
   display: none;
 }
 
-.value {
-  font-family: "JetBrains Mono", "Source Code Pro", Consolas, monospace;
-  margin-left: 10px;
-  /* cursor: pointer; */
-}
-
 .disabled {
   color: transparentize($colorText, 0.3);
 }
@@ -41,13 +35,6 @@
   border-left: 1px solid transparentize($colorText, 0.5);
 }
 
-.old-value {
-  text-decoration: line-through;
-  font-weight: lighter;
-  color: $colorError;
-  margin-left: 7px;
-}
-
 .modified {
   color: $colorSuccess;
 }
@@ -56,8 +43,10 @@
   color: $colorError;
 }
 
-.editing-input {
-  border: none;
-  border-bottom: 1px solid $colorText;
+.type {
+  color: transparentize($colorText, 0.2);
   font-family: "JetBrains Mono", "Source Code Pro", Consolas, monospace;
+  margin-left: 15px;
 }
+
+
diff --git a/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.ts b/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.ts
index 66ba899..74dc7ac 100644
--- a/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.ts
+++ b/frontend/projects/tools/src/yang-configure/components/yang-schema-node.component.ts
@@ -40,22 +40,18 @@
   @Input() showChildren = false;
   @Input() activeSession;
   showAllChildrenOnOpen = false;
-  editing = false;
-  originalValue;
-  editingValue = '';
   newNode: { key: string, module: string, path: string };
 
   ngOnInit() {
     this.showAllChildrenOnOpen = this.showChildren;
     this.nodeControlService.performNodeAction.subscribe(
       action => {
-        this.performGlobalAction(action);
+        if (action === 'close') {
+          this.showChildren = false;
+          this.showAllChildrenOnOpen = false;
+        }
       }
     );
-    if (this.node['value']) {
-      this.originalValue = this.node['value'];
-      this.editingValue = this.node['value'];
-    }
   }
 
   toggleChildren() {
@@ -68,32 +64,13 @@
     this.showChildren = !this.showChildren;
   }
 
-  toggleEdit() {
-    this.editing = !this.editing;
+  confirmEdit(value) {
+    this.configurationService.createChangeModification(this.activeSession.key, this.node['info']['path'], this.node, value);
+    this.node['value'] = value;
   }
 
-  performGlobalAction(action: string) {
-    switch (action) {
-      case 'close':
-        this.showChildren = false;
-        this.showAllChildrenOnOpen = false;
-        break;
-      case 'discardChanges':
-        this.restoreOriginal();
-        break;
-    }
-  }
-
-  confirmEdit() {
-    this.editing = false;
-    this.sessionService.createChangeModification(this.activeSession.key, this.node['info']['path'], this.node, this.editingValue);
-    this.sessionService.modificationAdded.emit(this.activeSession);
-    this.node['value'] = this.editingValue;
-  }
-
-  restoreOriginal() {
-    this.editingValue = this.originalValue;
-    this.node['value'] = this.originalValue;
+  discardChanges(value) {
+    this.node['value'] = value;
   }
 
   addChildNode() {
diff --git a/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-new-node/yang-new-node.component.ts b/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-new-node/yang-new-node.component.ts
index 94b12bc..38cba63 100644
--- a/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-new-node/yang-new-node.component.ts
+++ b/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-new-node/yang-new-node.component.ts
@@ -8,7 +8,11 @@
   styleUrls: ['./yang-new-node.component.scss']
 })
 export class YangNewNodeComponent implements OnInit {
-
+  /*
+  * Move child generation to the session service
+  * Mark them with node['new']
+  * This component will only be the dropdown menu, node will be handleded by the schema-node component
+  */
   @Input() sessionKey: string;
   @Input() schema: string;
   @Input() path: string;
diff --git a/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-node-editable-value/yang-node-editable-value.component.html b/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-node-editable-value/yang-node-editable-value.component.html
new file mode 100644
index 0000000..5a2258d
--- /dev/null
+++ b/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-node-editable-value/yang-node-editable-value.component.html
@@ -0,0 +1,18 @@
+<span class="value" *ngIf="value && !editing">{{value}}</span>
+
+<ng-container *ngIf="editable">
+  <input [class.hidden]="!editing" type="text" autofocus class="editing-input" [(ngModel)]="editingValue">
+  <span class="old-value" *ngIf="value !== originalValue">{{originalValue}}</span>
+  <i class="fa fa-pencil action-icon" aria-hidden="true" [class.hidden]="editing"
+     (click)="editing = !editing"
+     title="Edit value"></i>
+
+
+  <i class="fa fa-times action-icon text-danger" aria-hidden="true" [class.hidden]="!editing"
+     (click)="editing = !editing"></i>
+  <i class="fa fa-check action-icon text-success" aria-hidden="true" [class.hidden]="!editing"
+     (click)="confirmEdit()"></i>
+  <span title="Current value" class="disabled" [class.hidden]="!editing || editingValue == originalValue"></span>
+
+</ng-container>
+<span class="type" *ngIf="datatype">{{datatype}}</span>
diff --git a/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-node-editable-value/yang-node-editable-value.component.scss b/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-node-editable-value/yang-node-editable-value.component.scss
new file mode 100644
index 0000000..bdab355
--- /dev/null
+++ b/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-node-editable-value/yang-node-editable-value.component.scss
@@ -0,0 +1,40 @@
+@import "../../../../../../shared-styles/colors";

+

+.editing-input {

+  border: none;

+  border-bottom: 1px solid $colorText;

+  font-family: "JetBrains Mono", "Source Code Pro", Consolas, monospace;

+}

+

+.disabled {

+  color: transparentize($colorText, 0.3);

+}

+

+.hidden {

+  display: none;

+}

+

+.value {

+  font-family: "JetBrains Mono", "Source Code Pro", Consolas, monospace;

+  margin-left: 10px;

+  /* cursor: pointer; */

+}

+

+.action-icon {

+  padding-left: 5px;

+  padding-right: 5px;

+  cursor: pointer;

+  &:first-child {

+    padding-left: 10px;

+  }

+  &:hover {

+    color: $colorMain;

+  }

+}

+

+.old-value {

+  text-decoration: line-through;

+  font-weight: lighter;

+  color: $colorError;

+  margin-left: 7px;

+}

diff --git a/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-node-editable-value/yang-node-editable-value.component.ts b/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-node-editable-value/yang-node-editable-value.component.ts
new file mode 100644
index 0000000..b402cc9
--- /dev/null
+++ b/frontend/projects/tools/src/yang-configure/components/yang-tree-components/yang-node-editable-value/yang-node-editable-value.component.ts
@@ -0,0 +1,47 @@
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {NodeControlService} from '../../../services/node-control.service';
+
+@Component({
+  selector: 'nct-yang-node-editable-value',
+  templateUrl: './yang-node-editable-value.component.html',
+  styleUrls: ['./yang-node-editable-value.component.scss']
+})
+export class YangNodeEditableValueComponent implements OnInit {
+
+  @Input() editable: boolean;
+  @Input() value: any;
+  @Input() datatype?: string;
+
+
+  @Output() changeSaved = new EventEmitter<any>();
+  @Output() changeDiscarded = new EventEmitter<any>();
+
+  editing = false;
+  originalValue: any;
+  editingValue: any;
+
+  constructor(public nodeControlService: NodeControlService) {
+  }
+
+  ngOnInit() {
+    this.originalValue = this.value;
+    this.editingValue = this.value;
+    this.nodeControlService.performNodeAction.subscribe(
+      action => {
+        if (action === 'discardChanges') {
+          this.value = this.originalValue;
+          this.editingValue = this.originalValue;
+          this.changeDiscarded.emit(this.originalValue);
+        } else if (action === 'confirmNewValue') {
+          this.originalValue = this.value;
+        }
+      }
+    );
+  }
+
+  confirmEdit() {
+    this.editing = false;
+    this.changeSaved.emit(this.editingValue);
+  }
+
+}
diff --git a/frontend/projects/tools/src/yang-configure/services/node-control.service.ts b/frontend/projects/tools/src/yang-configure/services/node-control.service.ts
index 9cd7f14..f669980 100644
--- a/frontend/projects/tools/src/yang-configure/services/node-control.service.ts
+++ b/frontend/projects/tools/src/yang-configure/services/node-control.service.ts
@@ -37,6 +37,7 @@
     this.performNodeAction.emit('discardChanges');
   }
 
-
-
+  confirmNewValue() {
+    this.performNodeAction.emit('confirmNewValue');
+  }
 }
diff --git a/frontend/projects/tools/src/yang-configure/yang-configure.module.ts b/frontend/projects/tools/src/yang-configure/yang-configure.module.ts
index 5e29048..2885912 100644
--- a/frontend/projects/tools/src/yang-configure/yang-configure.module.ts
+++ b/frontend/projects/tools/src/yang-configure/yang-configure.module.ts
@@ -15,7 +15,8 @@
 import { YangNewNodeComponent } from './components/yang-tree-components/yang-new-node/yang-new-node.component';
 import { YangNodeDescriptionComponent } from './components/yang-tree-components/yang-node-description/yang-node-description.component';
 import { YangSchemaLinkComponent } from './components/yang-tree-components/yang-schema-link/yang-schema-link.component';
-import { YangNodeMenuComponent } from './components/yang-tree-components/yang-node-menu/yang-node-menu.component';

+import { YangNodeMenuComponent } from './components/yang-tree-components/yang-node-menu/yang-node-menu.component';
+import { YangNodeEditableValueComponent } from './components/yang-tree-components/yang-node-editable-value/yang-node-editable-value.component';

 

 

 @NgModule({

@@ -26,7 +27,7 @@
     RouterModule,

     FormsModule

   ],

-  declarations: [YangConfigureComponent, YangSchemaNodeComponent, ConfirmCommitComponent, YangNewNodeComponent, YangNodeDescriptionComponent, YangSchemaLinkComponent, YangNodeMenuComponent],

+  declarations: [YangConfigureComponent, YangSchemaNodeComponent, ConfirmCommitComponent, YangNewNodeComponent, YangNodeDescriptionComponent, YangSchemaLinkComponent, YangNodeMenuComponent, YangNodeEditableValueComponent],

   entryComponents: [YangConfigureComponent]

 })

 export class YangConfigureModule {