From 38b0782c887f046426c31766901906c614d73140 Mon Sep 17 00:00:00 2001
From: rsanchez <rsanchez@curisit.net>
Date: Tue, 14 Mar 2017 13:51:56 +0000
Subject: [PATCH] #3527 feature - Added pack form

---
 securis/src/main/webapp/src/app/license.list.component.ts          |  151 ++++++++++
 securis/src/main/webapp/src/app/pack.list.component.html           |   37 +
 securis/src/main/webapp/src/app/license.list.component.html        |   51 +++
 securis/src/main/webapp/src/app/common/session.ts                  |   30 ++
 securis/src/main/java/net/curisit/securis/DevFilter.java           |    2 
 securis/src/main/webapp/src/app/common/i18n.ts                     |   20 
 securis/src/main/webapp/src/app/common/error.checker.ts            |   55 +++
 securis/src/main/webapp/src/app/common/default.requests.options.ts |    4 
 securis/src/main/webapp/src/app/forms/pack.form.component.ts       |   69 ++++
 securis/src/main/webapp/src/app/app.module.ts                      |    6 
 securis/src/main/webapp/src/app/forms/license.form.component.ts    |   53 +++
 securis/src/main/webapp/src/app/forms/pack.form.html               |  229 ++++++++++----
 securis/src/main/webapp/src/app/pack.list.component.ts             |   24 +
 securis/src/main/webapp/assets/securis.css                         |   13 
 securis/src/main/webapp/src/app/forms/license.form.html            |   75 +++++
 securis/src/main/webapp/src/lang/messages_en.json                  |   22 +
 16 files changed, 739 insertions(+), 102 deletions(-)

diff --git a/securis/src/main/java/net/curisit/securis/DevFilter.java b/securis/src/main/java/net/curisit/securis/DevFilter.java
index 2a3c6c0..8b01f33 100644
--- a/securis/src/main/java/net/curisit/securis/DevFilter.java
+++ b/securis/src/main/java/net/curisit/securis/DevFilter.java
@@ -37,7 +37,7 @@
 		res.addHeader("Access-Control-Allow-Headers", "X-SECURIS-TOKEN, Content-Type");
 		res.addHeader("Access-Control-Expose-Headers", "X-SECURIS-ERROR-MSG, X-SECURIS-ERROR-CODE, Content-Type");
 
-		LOG.info("Added header to: " + res.getHeaderNames());
+		// LOG.info("Added header to: " + res.getHeaderNames());
 		if (!req.getMethod().equals("OPTIONS")) {
 			fc.doFilter(sreq, sres);
 		}
diff --git a/securis/src/main/webapp/assets/securis.css b/securis/src/main/webapp/assets/securis.css
index 993196c..64749e5 100644
--- a/securis/src/main/webapp/assets/securis.css
+++ b/securis/src/main/webapp/assets/securis.css
@@ -28,4 +28,17 @@
 .td-data-table-row > .td-data-table-cell:first-child,
 .td-data-table-row > .td-data-table-column:first-child {
 	max-width: 30px !important;
+}
+
+md-dialog-actions > .mat-button {
+	margin-left: 5px !important;
+	margin-right: 5px !important;
+}
+
+.label-value {
+	margin-top: 20px;
+}
+
+input:read-only {
+    color: rgba(0, 0, 0, 0.50) !important;
 }
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/app/app.module.ts b/securis/src/main/webapp/src/app/app.module.ts
index 04dca82..9b3ec9d 100644
--- a/securis/src/main/webapp/src/app/app.module.ts
+++ b/securis/src/main/webapp/src/app/app.module.ts
@@ -1,3 +1,4 @@
+import { ErrorCheckerComponent } from './common/error.checker';
 import { NgModule }      from '@angular/core';
 import { BrowserModule } from '@angular/platform-browser';
 import { CommonModule } from '@angular/common';
@@ -11,6 +12,7 @@
 import { ToastModule } from 'ng2-toastr/ng2-toastr';
 
 import { AppHomeComponent }  from './app.component';
+import { I18nDirective }  from './common/i18n';
 import { UserService }  from './user.service';
 import { PacksService }  from './resources/packs';
 import { LicenseTypesService }  from './resources/license_types';
@@ -25,6 +27,7 @@
 import { appRoutes, appRoutingProviders } from './app.routes';
 import { requestOptionsProvider, requestBackendProvider } from './common/default.requests.options';
 import { LocaleServiceModule } from './common/i18n';
+import { SeCurisSession } from './common/session';
 import { PackFormComponent } from "./forms/pack.form.component";
 
 @NgModule({
@@ -48,11 +51,14 @@
     PackListComponent,
     PackFormComponent,
     LoginFormComponent,
+    ErrorCheckerComponent,
+    I18nDirective,
     AppHomeComponent
   ],
   bootstrap: [ AppHomeComponent ],
   entryComponents: [ PackFormComponent ],
   providers: [
+    SeCurisSession,
     UserService,
     PacksService,
     LicensesService,
diff --git a/securis/src/main/webapp/src/app/common/default.requests.options.ts b/securis/src/main/webapp/src/app/common/default.requests.options.ts
index d02c654..5b3cba2 100644
--- a/securis/src/main/webapp/src/app/common/default.requests.options.ts
+++ b/securis/src/main/webapp/src/app/common/default.requests.options.ts
@@ -31,7 +31,7 @@
     
   }
 }
-
+ 
 @Injectable()
 export class ApiXHRBackend extends XHRBackend {
     
@@ -40,7 +40,7 @@
     }
 
     createConnection(request: Request): XHRConnection {
-        if (!request.url.endsWith('.js') && !request.url.endsWith('.html') && !request.url.endsWith('.svg')){
+        if (!request.url.endsWith('.js') && !request.url.endsWith('.json') && !request.url.endsWith('.html') && !request.url.endsWith('.svg')){
            request.url = 'http://localhost:8080/securis/' + request.url;     // prefix base url
         }
         return super.createConnection(request);
diff --git a/securis/src/main/webapp/src/app/common/error.checker.ts b/securis/src/main/webapp/src/app/common/error.checker.ts
new file mode 100644
index 0000000..b08055e
--- /dev/null
+++ b/securis/src/main/webapp/src/app/common/error.checker.ts
@@ -0,0 +1,55 @@
+import { Http } from '@angular/http';
+import { PacksService } from '../resources/packs';
+import { LocaleService } from '../common/i18n';
+import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
+import { IPageChangeEvent } from '@covalent/core';
+import { Component, Input, AfterViewInit } from '@angular/core';
+import { TdMediaService } from '@covalent/core';
+
+@Component({
+  selector: 'error-checker',
+  template: `
+    <div *ngIf="formField?.touched && formField.invalid" layout="column">
+      <span *ngFor="let err of getFieldErrors()" align="end">{{err}}</span>
+    </div>`
+})
+export class ErrorCheckerComponent {
+
+  @Input() formField: any;
+  @Input() fieldName: string;
+
+  constructor(private $L: LocaleService) {
+      console.log('contructor: ' + this.formField);
+  }
+  
+  getFieldErrors() : string[] {
+    if (this.formField.valid) {
+      return []
+    } else {
+      return (<string[]>Object.keys(this.formField.errors)).map((err:string) => this.getErrorMsg(err));
+    }
+  }
+
+  private updateFieldErrors() {
+  }
+
+  private getErrorMsg(err: string) : string{
+    switch(err) { 
+      case 'required': { 
+        return this.fieldName + ' '+ this.$L.get('is required');
+      } 
+      case 'number': { 
+        return this.fieldName + ' '+ this.$L.get('should be a number');
+      } 
+      default: { 
+        return this.fieldName + ' '+ this.$L.get('unknown error') + ' ' + err;
+      } 
+    } 
+  }
+
+  log(obj: any) {
+    console.log(obj)
+  }
+  
+}
+
diff --git a/securis/src/main/webapp/src/app/common/i18n.ts b/securis/src/main/webapp/src/app/common/i18n.ts
index b8132e1..58f1411 100644
--- a/securis/src/main/webapp/src/app/common/i18n.ts
+++ b/securis/src/main/webapp/src/app/common/i18n.ts
@@ -5,13 +5,14 @@
 // Use as reference: https://github.com/ngx-translate/core/tree/master/src
 @Injectable()
 export class LocaleService {
-    get URL_TPL() {return  'src/lang/messages-{0}.json'};
+    get URL_TPL() {return  'src/lang/messages_{0}.json'};
     
+	private _devLang : string = 'en';
 	private _currentLang : string = null;
     private _messages : any = null;
     private _elements = new Array<ElementRef>();
     private constructor(private http: Http, @Inject('INITIAL_LANG') initLang: string) {
-		this._currentLang = initLang || this.getBrowserLang();
+		this.lang = initLang || this.getBrowserLang();
     }
     
     get lang() : string {
@@ -21,7 +22,7 @@
     set lang(newLang: string) {
         this._currentLang = newLang;
         this.http.get(this._format(this.URL_TPL, newLang)).subscribe((data) => {
-            this._messages = data;
+            this._messages = data.json();
             this.reloadTexts();
         }); 
     }
@@ -48,7 +49,7 @@
     /**
      * It works similar to MessageFormat in Java
      */
-    _format(str: string, ...params: Array<string>) {
+    _format(str: string, ...params: string[]) {
         
         return str.replace(/\{(\d+)\}/g, function(match, index) {
             return params[index];
@@ -78,7 +79,7 @@
      * $L.get('hello'); // This returns "hola"
      * $L.get('Hello {0}!!', 'John'); // This returns: "Hola John!!" if language is spanish
      */
-    get(msg: string) : string {
+    get(msg: string, ...params: string[] ) : string {
         if (msg == null) {
             return '';
         }
@@ -90,16 +91,15 @@
                 trans_msg = this._messages[msg];
             } else if (this._messages[msg.toLowerCase()]) {
                 trans_msg = this._messages[msg.toLowerCase()];
-            } else {
-                this._currentLang !== 'es' && console.error("Missing i18 key: " + msg);
+            } else { 
+                (this._currentLang !== this._devLang) && console.error("Missing i18 key: " + msg);
                 trans_msg = msg;
             }
         }
         // Enviar evento cuando el idioma cambia al $rootScope
         
         if (arguments.length === 1) return trans_msg;
-        var params = Array.prototype.slice.call(arguments, 1);
-        return this._format.apply(trans_msg, params);
+        return this._format(trans_msg, ...params);
     }
     
 }
@@ -108,6 +108,7 @@
 @Directive({ selector: '[i18n]' })
 export class I18nDirective {
     constructor(private el: ElementRef, private renderer: Renderer, private $L: LocaleService) {
+        console.log(el);
     }
 
 	ngAfterViewChecked() {
@@ -124,7 +125,6 @@
 })
 export class LocaleServiceModule {
     static withConfig(initLang?: string): ModuleWithProviders {
-        console.log('Init lang with ' + initLang);
         return {
             ngModule: LocaleServiceModule,
             providers: [
diff --git a/securis/src/main/webapp/src/app/common/session.ts b/securis/src/main/webapp/src/app/common/session.ts
new file mode 100644
index 0000000..e6e9402
--- /dev/null
+++ b/securis/src/main/webapp/src/app/common/session.ts
@@ -0,0 +1,30 @@
+import { LocalStorageService } from 'angular-2-local-storage';
+import { Injectable } from '@angular/core';
+
+
+@Injectable()
+export class SeCurisSession {
+
+  sessionData: any = {}
+
+  constructor(private store: LocalStorageService) {
+    
+  }
+
+  public get(key: string): any {
+    return this.sessionData[key];
+  }
+
+  public exists(key: string): boolean {
+    return this.sessionData[key] != undefined;
+  }
+
+  public set(key: string, value: any): void {
+    this.sessionData[key] = value;
+  }
+
+  public remove(key: string) : void {
+      delete this.sessionData[key];
+  }
+} 
+
diff --git a/securis/src/main/webapp/src/app/forms/license.form.component.ts b/securis/src/main/webapp/src/app/forms/license.form.component.ts
new file mode 100644
index 0000000..ba627af
--- /dev/null
+++ b/securis/src/main/webapp/src/app/forms/license.form.component.ts
@@ -0,0 +1,53 @@
+import { Http } from '@angular/http';
+import { LicensesService } from '../resources/licenses';
+import { LocaleService } from '../common/i18n';
+import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
+import { IPageChangeEvent } from '@covalent/core';
+import { Component, AfterViewInit } from '@angular/core';
+import { TdMediaService } from '@covalent/core';
+import { IComboOption } from './base';
+
+@Component({
+  selector: 'license-form',
+  templateUrl: 'src/app/forms/license.form.html'
+})
+export class LicenseFormComponent implements AfterViewInit {
+
+  form_title: string = 'Title';
+  form_subtitle: string = '';
+  data: any = {};
+  isNew : boolean = true;
+
+  constructor(private http: Http,
+              private packs: LicensesService,
+              private $L: LocaleService) {
+      
+  }
+
+  private loadCombos(): void {
+    /*
+      this.http.get('organization')
+        .map(response => response.json().map((org : any) => <IComboOption>{id: org.id, label: `(${org.code}) ${org.name}`}))
+        .subscribe(
+          data => this.organizations = (<IComboOption[]>data).sort((e1, e2) => e1.label.localeCompare(e2.label)),
+          err => console.error('Error loading orgs')
+        );
+        */
+  }
+
+  log(obj: any) {
+    console.log(obj)
+  }
+  
+  ngOnInit(): void {
+    this.loadCombos();
+    this.data = {};
+    this.form_title = this.$L.get('License data');
+    this.form_subtitle = this.$L.get(this.isNew ? 'Create a new license': 'Modify the license data') ;
+  }
+
+
+  ngAfterViewInit(): void {
+  }
+}
+
diff --git a/securis/src/main/webapp/src/app/forms/license.form.html b/securis/src/main/webapp/src/app/forms/license.form.html
new file mode 100644
index 0000000..df65d4d
--- /dev/null
+++ b/securis/src/main/webapp/src/app/forms/license.form.html
@@ -0,0 +1,75 @@
+<form #packForm="ngForm" class="inset" (keyup.enter)="save()">
+	<md-card>
+		<md-card-title>
+			{{form_title}}
+		</md-card-title>
+		<md-card-subtitle>
+			{{form_subtitle}}
+		</md-card-subtitle>
+<md-divider></md-divider>
+		<md-card-content>
+	<div layout="column" layout-align="start center" layout-fill>
+		<div layout="row" layout-fill layout-padding>
+			<div layout="column" layout-fill  flex="50">
+			<md-input-container>
+				<input mdInput maxLength="50" type="text" [(ngModel)]="data.code" name="code" required />
+				<md-placeholder>
+          			<span i18n>Code</span>
+        		</md-placeholder>
+			</md-input-container>
+			{{log(packForm.controls)}}
+			<div *ngIf="packForm.controls.code?.touched && packForm.controls.code.invalid" layout="column">
+				<span *ngIf="packForm.controls.code.errors.required" align="end">Code is required</span>
+				<span align="end">Code is ok</span>
+			</div>
+			</div>
+	<!-- TODO: <div class="alert inline-alert alert-warning" ng-show="packForm.code.$invalid">
+		<span class="glyphicon glyphicon-warning-sign"></span> 
+		<span ng-show="packForm.code.$error.maxlength" ng-bind="maxLengthErrorMsg('Code', maxlength.code)" 
+		class="ng-binding ng-hide">Code length is too long (max: 50).</span> 
+		<span ng-show="packForm.code.$error.required" ng-bind="mandatoryFieldErrorMsg('Code')" class="ng-binding">'Code' is required.</span>
+	</div> -->
+			<md-input-container flex="50">
+				<input mdInput type="number" type="text" [(ngModel)]="data.num_licenses" name="num_licenses" required />
+				<md-placeholder>
+          			<span i18n>Num. licenses</span>
+        		</md-placeholder>
+			</md-input-container>
+		</div>
+		<div layout="row" layout-align="start center" layout-fill layout-padding>
+			<md-input-container flex>
+				<input mdInput type="date" type="text" [(ngModel)]="data.init_valid_date" name="init_valid_date" required />
+				<md-placeholder>
+          			<span i18n>Initial date</span>
+        		</md-placeholder>
+				<md-hint align="end">YYYY-MM-DD</md-hint>
+			</md-input-container>
+			<md-input-container flex>
+				<input mdInput type="date" type="text" [(ngModel)]="data.end_valid_date" name="end_valid_date" required />
+				<md-placeholder>
+          			<span i18n>End date</span>
+        		</md-placeholder>
+				<md-hint align="end">YYYY-MM-DD</md-hint>
+			</md-input-container>
+		</div>
+		<div layout="row" layout-fill layout-padding>
+			<md-select flex placeholder="Organization" [(ngModel)]="data.organization_id" name="organization_id">
+				<md-option *ngFor="let org of organizations" [value]="org.id">
+					{{org.label}}
+				</md-option>
+			</md-select>
+			<md-select flex placeholder="License type" [(ngModel)]="data.license_type_id" name="license_type_id">
+				<md-option *ngFor="let lt of licensetypes" [value]="lt.id">
+					{{lt.label}}
+				</md-option>
+			</md-select>
+		</div>
+	</div>
+</md-card-content>
+<md-divider></md-divider>
+<md-card-actions>
+	<button [disabled]="!packForm.form.valid" md-raised-button color="primary" (click)="save()">Save</button>
+	<button md-button md-dialog-close>Cancel</button>
+</md-card-actions>
+</md-card>
+</form>	
diff --git a/securis/src/main/webapp/src/app/forms/pack.form.component.ts b/securis/src/main/webapp/src/app/forms/pack.form.component.ts
index 852dba3..53858be 100644
--- a/securis/src/main/webapp/src/app/forms/pack.form.component.ts
+++ b/securis/src/main/webapp/src/app/forms/pack.form.component.ts
@@ -1,11 +1,16 @@
 import { Http } from '@angular/http';
+import { ToastsManager } from 'ng2-toastr/ng2-toastr';
+
 import { PacksService } from '../resources/packs';
+import { LicenseTypesService } from '../resources/license_types';
 import { LocaleService } from '../common/i18n';
 import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
 import { IPageChangeEvent } from '@covalent/core';
 import { Component, AfterViewInit } from '@angular/core';
 import { TdMediaService } from '@covalent/core';
 import { IComboOption } from './base';
+
+
 
 @Component({
   selector: 'pack-form',
@@ -19,11 +24,35 @@
   licensetypes : IComboOption[];
   data: any = {};
   isNew : boolean = true;
-
+  fields: any = {
+    'code': '',
+    'num_licenses': '',
+    'init_valid_date': '',
+    'end_valid_date': '',
+    'license_preactivation': '',
+    'license_type_id': '',
+    'organization_id': '',
+    'licensetype_code': '',
+    'organization_name': '',
+    'preactivation_valid_period': '',
+    'renew_valid_period': '',
+    'application_name': '',
+    'status': '',
+    'metadata': '',
+    'comments': '',
+    'key': '',
+    'value': '',
+  }
   constructor(private http: Http,
+              private toaster: ToastsManager,
+              private licenseTypes: LicenseTypesService,
               private packs: PacksService,
               private $L: LocaleService) {
-      
+      Object.keys(this.fields).forEach(k => this.fields[k] = $L.get(`field.${k}`));
+  }
+
+  public getFieldName(fieldId: string) : string {
+    return this.fields[fieldId];
   }
 
   private loadCombos(): void {
@@ -33,23 +62,47 @@
           data => this.organizations = (<IComboOption[]>data).sort((e1, e2) => e1.label.localeCompare(e2.label)),
           err => console.error('Error loading orgs')
         );
-      this.http.get('licensetype')
-        .map(response => response.json().map((lt : any) => <IComboOption>{id: lt.id, label: `(${lt.code}) ${lt.name}`}))
+      this.licenseTypes.get()
+        .map(list => list.map((lt : any) => <IComboOption>{id: lt.id, label: `(${lt.code}) ${lt.name}`}))
         .subscribe(
           data => this.licensetypes = (<IComboOption[]>data).sort((e1, e2) => e1.label.localeCompare(e2.label)),
           err => console.error('Error loading license types')
         );
   }
 
-  log(obj: any) {
-    console.log(obj)
+  save() {
+      var command = this.isNew ? this.packs.create(this.data) : this.packs.modify(this.data.id, this.data);
+      command.subscribe(
+          data => this.toaster.success(this.$L.get('Pack saved sucessfully')),
+          err => {
+            console.error(err);
+            this.toaster.success(this.$L.get('Error saving pack'));
+          }
+        );
   }
+  
+  changeLicType(event) {
+    this.licenseTypes.get(this.data.license_type_id)
+        .map(lt_data => lt_data.metadata)
+        .subscribe(
+          metadata => this.data.metadata = metadata,
+          err => {
+            console.error('Error loading license type metadata');
+            console.error(err);
+          }
+        );
+  }
+
+  changeOrg(event) {
+    console.log(event);
+    console.log(this.data.organization_id);
+  } 
   
   ngOnInit(): void {
     this.loadCombos();
-    this.data = {};
+    // this.data = {};
     this.form_title = this.$L.get('Pack data');
-    this.form_subtitle = this.$L.get('Create a new licenses pack');
+    this.form_subtitle = this.$L.get(this.isNew ? 'Create a new licenses pack': 'Modify the licenses pack data') ;
   }
 
 
diff --git a/securis/src/main/webapp/src/app/forms/pack.form.html b/securis/src/main/webapp/src/app/forms/pack.form.html
index df65d4d..82d8815 100644
--- a/securis/src/main/webapp/src/app/forms/pack.form.html
+++ b/securis/src/main/webapp/src/app/forms/pack.form.html
@@ -1,75 +1,174 @@
-<form #packForm="ngForm" class="inset" (keyup.enter)="save()">
-	<md-card>
-		<md-card-title>
-			{{form_title}}
-		</md-card-title>
-		<md-card-subtitle>
-			{{form_subtitle}}
-		</md-card-subtitle>
+<md-toolbar>
+	<span md-dialog-title>{{form_title}}</span>
+	<span flex></span>
+	<button md-icon-button (click)="save()"><md-icon>save</md-icon></button>
+	<button md-icon-button md-dialog-close><md-icon>close</md-icon></button>
+</md-toolbar>
+<p class="md-subhead">{{form_subtitle}}</p>
 <md-divider></md-divider>
-		<md-card-content>
-	<div layout="column" layout-align="start center" layout-fill>
+<form #packForm="ngForm" class="inset" >
+<div layout="column" >
+<md-dialog-content>
+	<div layout="column" layout-align="start center">
 		<div layout="row" layout-fill layout-padding>
-			<div layout="column" layout-fill  flex="50">
-			<md-input-container>
-				<input mdInput maxLength="50" type="text" [(ngModel)]="data.code" name="code" required />
-				<md-placeholder>
-          			<span i18n>Code</span>
-        		</md-placeholder>
-			</md-input-container>
-			{{log(packForm.controls)}}
-			<div *ngIf="packForm.controls.code?.touched && packForm.controls.code.invalid" layout="column">
-				<span *ngIf="packForm.controls.code.errors.required" align="end">Code is required</span>
-				<span align="end">Code is ok</span>
+			<div layout="column" layout-fill  flex="15" *ngIf="!isNew">
+				<md-input-container>
+					<input mdInput maxLength="50" type="text" [(ngModel)]="data.id" name="id" required [readonly]="!isNew" />
+					<md-placeholder>
+						<span i18n="field.id"></span>
+					</md-placeholder>
+				</md-input-container>
 			</div>
+			<div layout="column" layout-fill  flex>
+				<md-input-container>
+					<input mdInput maxLength="50" type="text" [(ngModel)]="data.code" name="code" required [readonly]="!isNew" />
+					<md-placeholder>
+						<span i18n="field.code"></span>
+					</md-placeholder>
+				</md-input-container>
+				<error-checker [fieldName]="getFieldName('code')" [formField]="packForm.controls.code"></error-checker>
 			</div>
-	<!-- TODO: <div class="alert inline-alert alert-warning" ng-show="packForm.code.$invalid">
-		<span class="glyphicon glyphicon-warning-sign"></span> 
-		<span ng-show="packForm.code.$error.maxlength" ng-bind="maxLengthErrorMsg('Code', maxlength.code)" 
-		class="ng-binding ng-hide">Code length is too long (max: 50).</span> 
-		<span ng-show="packForm.code.$error.required" ng-bind="mandatoryFieldErrorMsg('Code')" class="ng-binding">'Code' is required.</span>
-	</div> -->
-			<md-input-container flex="50">
-				<input mdInput type="number" type="text" [(ngModel)]="data.num_licenses" name="num_licenses" required />
-				<md-placeholder>
-          			<span i18n>Num. licenses</span>
-        		</md-placeholder>
-			</md-input-container>
-		</div>
-		<div layout="row" layout-align="start center" layout-fill layout-padding>
-			<md-input-container flex>
-				<input mdInput type="date" type="text" [(ngModel)]="data.init_valid_date" name="init_valid_date" required />
-				<md-placeholder>
-          			<span i18n>Initial date</span>
-        		</md-placeholder>
-				<md-hint align="end">YYYY-MM-DD</md-hint>
-			</md-input-container>
-			<md-input-container flex>
-				<input mdInput type="date" type="text" [(ngModel)]="data.end_valid_date" name="end_valid_date" required />
-				<md-placeholder>
-          			<span i18n>End date</span>
-        		</md-placeholder>
-				<md-hint align="end">YYYY-MM-DD</md-hint>
-			</md-input-container>
+			<div layout="column" layout-fill flex>
+				<md-input-container>
+					<input mdInput type="number"  [(ngModel)]="data.num_licenses" name="num_licenses" required />
+					<md-placeholder>
+						<span i18n="field.num_licenses"></span>
+					</md-placeholder>
+				</md-input-container>
+				<error-checker [fieldName]="getFieldName('num_licenses')" [formField]="packForm.controls.num_licenses"></error-checker>
+			</div>
 		</div>
 		<div layout="row" layout-fill layout-padding>
-			<md-select flex placeholder="Organization" [(ngModel)]="data.organization_id" name="organization_id">
-				<md-option *ngFor="let org of organizations" [value]="org.id">
-					{{org.label}}
-				</md-option>
-			</md-select>
-			<md-select flex placeholder="License type" [(ngModel)]="data.license_type_id" name="license_type_id">
-				<md-option *ngFor="let lt of licensetypes" [value]="lt.id">
-					{{lt.label}}
-				</md-option>
-			</md-select>
+			<div layout="column" layout-fill flex>
+				<md-input-container flex>
+					<input mdInput type="date" [(ngModel)]="data.init_valid_date" name="init_valid_date" required />
+					<md-placeholder>
+						<span i18n="field.end_valid_date"></span>
+					</md-placeholder>
+				</md-input-container>
+				<error-checker [fieldName]="getFieldName('init_valid_date')" [formField]="packForm.controls.init_valid_date"></error-checker>
+			</div>
+			<div layout="column" layout-fill flex>
+				<md-input-container flex>
+					<input mdInput type="date" [(ngModel)]="data.end_valid_date" name="end_valid_date" required />
+					<md-placeholder>
+						<span i18n="field.end_valid_date"></span>
+					</md-placeholder>
+				</md-input-container>
+				<error-checker [fieldName]="getFieldName('end_valid_date')" [formField]="packForm.controls.end_valid_date"></error-checker>
+			</div>
+		</div>
+		<div layout="row" layout-fill layout-padding *ngIf="isNew">
+			<div layout="column" layout-fill flex>
+				<md-select [placeholder]="getFieldName('organization_id')" flex [(ngModel)]="data.organization_id" name="organization_id" (change)="changeOrg($event)">
+					<md-option *ngFor="let org of organizations" [value]="org.id">
+						{{org.label}}
+					</md-option>
+				</md-select>
+				<error-checker [fieldName]="getFieldName('organization_id')" [formField]="packForm.controls.organization_id"></error-checker>
+			</div>
+			<div layout="column" layout-fill flex>
+				<md-select flex [placeholder]="getFieldName('license_type_id')" [(ngModel)]="data.license_type_id" name="license_type_id" (change)="changeLicType($event)">
+					<md-option *ngFor="let lt of licensetypes" [value]="lt.id">
+						{{lt.label}}
+					</md-option>
+				</md-select>
+				<error-checker [fieldName]="getFieldName('license_type_id')" [formField]="packForm.controls.license_type_id"></error-checker>
+			</div>
+		</div>
+		<div layout="row" layout-fill layout-padding *ngIf="!isNew">
+			<div layout="column" layout-fill flex>
+				<div layout="column" class="mat-input-container" flex>
+					<label class="mat-input-placeholder mat-float">
+						<span class="placeholder" i18n="field.organization_id"></span>
+					</label>
+					<div class="label-value mat-input-element">{{data.organization_name}}</div>
+				</div>
+			</div>
+			<div layout="column" layout-fill flex>
+				<div layout="column" class="mat-input-container" flex>
+					<label class="mat-input-placeholder mat-float">
+						<span class="placeholder" i18n="field.license_type_id"></span>
+					</label>
+					<div class="label-value mat-input-element">{{data.licensetype_code}}</div>
+				</div>
+			</div>
+		</div>
+		<div layout="row" layout-fill layout-padding >
+			<div layout="column" layout-fill flex>
+				<md-input-container flex>
+					<input mdInput type="number"  [(ngModel)]="data.preactivation_valid_period" name="preactivation_valid_period" required />
+					<md-placeholder>
+						<span i18n="field.preactivation_valid_period"></span>
+					</md-placeholder>
+					<md-hint align="end">days</md-hint>
+				</md-input-container>
+				<error-checker [fieldName]="getFieldName('preactivation_valid_period')" [formField]="packForm.controls.preactivation_valid_period"></error-checker>
+			</div>
+			<div layout="column" layout-fill flex>
+				<md-input-container flex>
+					<input mdInput type="number"[(ngModel)]="data.renew_valid_period" name="renew_valid_period" required />
+					<md-placeholder>
+						<span i18n="field.renew_valid_period"></span>
+					</md-placeholder>
+					<md-hint align="end">days</md-hint>
+				</md-input-container>
+				<error-checker [fieldName]="getFieldName('renew_valid_period')" [formField]="packForm.controls.renew_valid_period"></error-checker>
+			</div>
+		</div>
+		<div layout="row" layout-fill layout-padding>
+			<div layout="column" layout-fill flex>
+				<md-input-container flex>
+					<textarea mdInput type="text" type="text" [(ngModel)]="data.comments" name="comments" maxlength="1024" ></textarea>
+					<md-placeholder>
+						<span i18n="field.comments"></span>
+					</md-placeholder>
+					<md-hint align="end">(max 1024)</md-hint>
+				</md-input-container>
+			</div>
+		</div>
+		<div layout="row" layout-fill layout-padding *ngIf="!isNew">
+			<div layout="column" layout-fill flex>
+				<div layout="column" class="mat-input-container" flex>
+					<label class="mat-input-placeholder mat-float">
+						<span class="placeholder" i18n="field.created_by_name"></span>
+					</label>
+					<div class="label-value mat-input-element">{{data.created_by_name}}</div>
+				</div>
+			</div>
+			<div layout="column" layout-fill flex>
+				<div layout="column" class="mat-input-container" flex>
+					<label class="mat-input-placeholder mat-float">
+						<span class="placeholder" i18n="field.creation_timestamp"></span>
+					</label>
+					<div class="label-value mat-input-element">{{data.creation_timestamp | date: 'medium'}}</div>
+				</div>
+			</div>
+		</div>
+		<div layout="column" layout-fill>
+			<span class="md-title" i18n>License metadata</span>
+			<div layout="row" layout-fill layout-padding *ngFor="let pair of data.metadata">
+				<md-input-container flex="40">
+					<input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.key" readonly />
+					<md-placeholder>
+						<span i18n="field.key"></span>
+					</md-placeholder>
+				</md-input-container>
+				<md-input-container flex>
+					<input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.value" [readonly]="pair.readonly" [required]="pair.required" />
+					<md-placeholder>
+						<span i18n="field.value"></span>
+					</md-placeholder>
+				</md-input-container>
+			</div>
 		</div>
 	</div>
-</md-card-content>
+</md-dialog-content>
+<div flex></div>
 <md-divider></md-divider>
-<md-card-actions>
-	<button [disabled]="!packForm.form.valid" md-raised-button color="primary" (click)="save()">Save</button>
-	<button md-button md-dialog-close>Cancel</button>
-</md-card-actions>
-</md-card>
+<md-dialog-actions layout="row" layout-align="end center">
+		<button [disabled]="!packForm.form.valid" md-raised-button color="primary" (click)="save()">Save</button>
+		<button md-button md-dialog-close>Cancel</button>
+</md-dialog-actions>
+</div>
 </form>	
diff --git a/securis/src/main/webapp/src/app/license.list.component.html b/securis/src/main/webapp/src/app/license.list.component.html
new file mode 100644
index 0000000..25969b2
--- /dev/null
+++ b/securis/src/main/webapp/src/app/license.list.component.html
@@ -0,0 +1,51 @@
+<md-toolbar role="toolbar" class="mat-secondary">
+  <span class="push-left-sm">
+      <span class="md-title" i18n>License Packs</span>
+  </span>
+  <span class="push-left-sm" *ngIf="filteredItems < data.length">
+      <span class="md-body-1">{{filteredItems}} of {{data.length}} packs filtered</span>
+  </span>
+  <td-search-box #searchBox class="push-right-sm" placeholder="Search here" (searchDebounce)="search($event)" flex>
+  </td-search-box>
+  <button md-mini-fab color="accent" (click)="createPack()">
+      <md-icon>add</md-icon>
+    </button>
+</md-toolbar>
+<md-divider></md-divider>
+<div layout="row" layout-align="center center">
+  <div flex="80" layout="column" layout-align="end center" >
+<td-data-table [data]="filteredData" [columns]="columns" style="width: 100%">
+  <template tdDataTableTemplate="used_licenses" let-row="row">
+    <div layout="row">
+        <td-notification-count color="secondary" [notifications]="row['num_licenses']">
+        </td-notification-count>
+        <td-notification-count color="primary" [notifications]="row['num_available']">
+        </td-notification-count>
+    </div>
+  </template>
+  <template tdDataTableTemplate="code" let-row="row" let-value="value">
+    <div layout="row" layout-align="start center">
+      <span style="white-space: nowrap">{{value}}</span>
+    </div>
+  </template>
+  <template tdDataTableTemplate="menu" let-row="row" let-index="index">
+    <div layout="row" layout-align="end center">
+      <button md-icon-button (click)="editPack(row)" color="primary"><md-icon>edit</md-icon></button>
+      <button md-icon-button [mdMenuTriggerFor]="packMenu" aria-label="Pack menu">
+        <md-icon>more_vert</md-icon>
+      </button>
+       
+      <md-menu #packMenu="mdMenu">
+          <button md-menu-item *ngFor="let action of pack_menu_options" (click)="packAction(action.command, row)" [disabled]="!isActionAvailable(row)">
+            <md-icon *ngIf="!!action.icon">{{ action.icon }}</md-icon> {{ action.name }}
+          </button>
+      </md-menu>
+      <button md-icon-button (click)="showLicenses(row)" color="accent"><md-icon>arrow_forward</md-icon></button>
+    </div>
+  </template>
+</td-data-table>
+<td-paging-bar #pagingBar [pageSizes]="[10, 20, 40]" [total]="filteredTotal" (change)="page($event)" align="end">
+  <span td-paging-bar-label hide-xs>Rows per page:</span> {{pagingBar.range}} <span hide-xs>of {{pagingBar.total}}</span>
+</td-paging-bar>
+</div>
+</div>
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/app/license.list.component.ts b/securis/src/main/webapp/src/app/license.list.component.ts
new file mode 100644
index 0000000..a14377e
--- /dev/null
+++ b/securis/src/main/webapp/src/app/license.list.component.ts
@@ -0,0 +1,151 @@
+import { Router } from '@angular/router';
+import { MdDialog } from '@angular/material';
+import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
+import { IPageChangeEvent } from '@covalent/core';
+import { Component, AfterViewInit } from '@angular/core';
+import { TdMediaService } from '@covalent/core';
+import { LicensesService } from './resources/licenses';
+import { LicenseFormComponent } from './forms/license.form.component';
+
+var lic_example = { activation_code: '19fa8d30-29cb-4b59-81b5-3837af8204b6',
+  code: 'CISA02-494-1',
+  code_suffix: 1,
+  created_by_id: '_client',
+  creation_timestamp: 1447848747000,
+  email: 'ccalvo@curisit.net',
+  expiration_date: 1450440747000,
+  full_name: 'César SA',
+  id: 110,
+  licenseData: '{"appCode":"CISA","appName":"CurisIntegrity SA","licenseCode":"CISA02-494-1","activationCode":"19fa8d30-29cb-4b59-81b5-3837af8204b6","expirationDate":1450440746790,"arch":"x86_64","osName":"Mac OS X","macAddresses":["60-03-08-95-AE-D0","B6-2B-33-E9-64-2D"],"crcLogo":"10f6379e0e1c00ebc403160307e3c5d0aba0727c9cae0bf1ac7cd19d84fdc80f","metadata":{"maxWellLifeLines":"50","simulationModes":"A1,A2,N1,QL"},"signature":"Tejun4bNbknxOyEmPaO/fGfGhv4URhVON/7bESxbODFWMJYKQqOPHrDiSUMlf6RbfWSVg2Dry8bY1WX881QGjTkBaHeDJKCy1EaJBwJ2nv9TYSMOiRj0eqMNYWE9/oLpvufHylAkPUpZwXVkSzTxmN+RvWa2Xt4Fu7xN+4PDHV4t7PSq7QwsFlD9ArgYC6Vx+zuL9WZANBtJ2gU/gKOE0CU0KjsB49RGQSFS/G27+H/YuDkCiQq7PC7VdVwYONQ2HO91fyPvInIrzDC5+sWHcUAqSCop//8klMy03hWl6VvAlaSP7kNM3KadyqXIJ3tx4Jwm1W+gBb3tngHzVCpYmw=="}',
+  modification_timestamp: 1447848747000,
+  pack_code: 'CISA02',
+  pack_id: 18,
+  request_data: '{"appCode":"CISA","activationCode":"19fa8d30-29cb-4b59-81b5-3837af8204b6","arch":"x86_64","osName":"Mac OS X","macAddresses":["60-03-08-95-AE-D0","B6-2B-33-E9-64-2D"],"crcLogo":"10f6379e0e1c00ebc403160307e3c5d0aba0727c9cae0bf1ac7cd19d84fdc80f"}',
+  status: 'AC' }
+
+@Component({
+  selector: 'license-list',
+  templateUrl: 'src/app/license.list.component.html'
+})
+export class LicenseListComponent implements AfterViewInit {
+  data: any[] = [];
+  columns: ITdDataTableColumn[] = [
+    { name: 'code', label: 'Code', tooltip: 'License pack code' },
+    { name: 'application_name', label: 'App name' },
+    { name: 'licensetype_code', label: 'License type' },
+    { name: 'organization_name', label: 'Organization' },
+    { name: 'used_licenses', label: 'Lics', tooltip: 'Initial/Available pack licenses' },
+    { name: 'menu', label: '' }
+  ];
+
+  filteredData: any[] = this.data;
+  filteredTotal: number = this.data.length;
+
+  searchTerm: string = '';
+  fromRow: number = 1;
+  currentPage: number = 1;
+  pageSize: number = 10;
+  sortBy: string = 'application_name';
+  sortOrder: TdDataTableSortingOrder = TdDataTableSortingOrder.Descending;
+  filteredItems = this.data.length;
+  license_menu_options : any[] = [{
+    icon: 'edit',
+    command: 'edit',
+    name: 'Edit'
+  },{
+    icon: 'cancel',
+    command: 'cancel',
+    name: 'Cancel'
+  }]
+  
+  licenseAction(action: any) {
+    console.log(action.command);
+  }
+
+  isActionAvailable(pack : any) : boolean {
+    return true;
+  }
+
+  constructor(private _dataTableService: TdDataTableService,
+    private media: TdMediaService,
+    private router: Router,
+    private dialog: MdDialog,
+    private licenseForm: LicenseFormComponent,
+    private licenses: LicensesService) {
+      this.licenses.get().subscribe(
+        list => {
+          this.data = list;
+          this.filter();
+        },
+        err => console.error(err)
+      );
+  }
+
+  ngOnInit(): void {
+    this.filter();
+  }
+
+  createPack() : void {
+    var ref = this.dialog.open(LicenseFormComponent, {
+      height: '50%', // can be px or %
+      width: '40%', // can be px or %
+    });
+    ref.componentInstance.isNew = true;
+    ref.afterClosed().subscribe(result => {
+      console.log(result);
+      this.filter();
+    });
+  }
+
+  editLicense(lic: any) : void {
+    var ref = this.dialog.open(LicenseFormComponent, {
+      height: '50%', // can be px or %
+      width: '40%', // can be px or %
+    });
+    ref.componentInstance.isNew = false;
+    ref.componentInstance.data = lic;
+    ref.afterClosed().subscribe(result => {
+      console.log(result);
+      this.filter();
+    });
+  }
+
+
+  sort(sortEvent: ITdDataTableSortChangeEvent): void {
+    this.sortBy = sortEvent.name;
+    this.sortOrder = sortEvent.order;
+    this.filter();
+  }
+
+  search(searchTerm: string): void {
+    this.searchTerm = searchTerm;
+    this.filter();
+  }
+
+  page(pagingEvent: IPageChangeEvent): void {
+    this.fromRow = pagingEvent.fromRow;
+    this.currentPage = pagingEvent.page;
+    this.pageSize = pagingEvent.pageSize;
+    this.filter();
+  }
+
+  loadData() {
+
+  }
+
+  filter(): void {
+    let newData: any[] = this.data;
+    newData = this._dataTableService.filterData(newData, this.searchTerm, true);
+    this.filteredTotal = newData.length;
+    this.filteredItems = newData.length;
+    newData = this._dataTableService.sortData(newData, this.sortBy, this.sortOrder);
+    newData = this._dataTableService.pageData(newData, this.fromRow, this.currentPage * this.pageSize);
+    this.filteredData = newData;
+  }
+
+  ngAfterViewInit(): void {
+    this.media.broadcast();
+
+  }
+}
+
diff --git a/securis/src/main/webapp/src/app/pack.list.component.html b/securis/src/main/webapp/src/app/pack.list.component.html
index 73a3bfe..25969b2 100644
--- a/securis/src/main/webapp/src/app/pack.list.component.html
+++ b/securis/src/main/webapp/src/app/pack.list.component.html
@@ -12,27 +12,40 @@
     </button>
 </md-toolbar>
 <md-divider></md-divider>
-<td-data-table [data]="filteredData" [columns]="columns">
-  
+<div layout="row" layout-align="center center">
+  <div flex="80" layout="column" layout-align="end center" >
+<td-data-table [data]="filteredData" [columns]="columns" style="width: 100%">
   <template tdDataTableTemplate="used_licenses" let-row="row">
     <div layout="row">
         <td-notification-count color="secondary" [notifications]="row['num_licenses']">
         </td-notification-count>
-        <td-notification-count color="green" [notifications]="row['num_available']">
+        <td-notification-count color="primary" [notifications]="row['num_available']">
         </td-notification-count>
     </div>
   </template>
-  <template tdDataTableTemplate="lics" let-row="row">
-      <button md-icon-button color="primary"><md-icon>arrow_right</md-icon></button>
+  <template tdDataTableTemplate="code" let-row="row" let-value="value">
+    <div layout="row" layout-align="start center">
+      <span style="white-space: nowrap">{{value}}</span>
+    </div>
   </template>
-  <template tdDataTableTemplate="menu" let-row="row">
-    <div layout="row">
-      <button md-icon-button color="primary"><md-icon>arrow_right</md-icon></button>
-      <button md-icon-button><md-icon>edit</md-icon></button>
-      <button md-icon-button><md-icon>more_vert</md-icon></button>
+  <template tdDataTableTemplate="menu" let-row="row" let-index="index">
+    <div layout="row" layout-align="end center">
+      <button md-icon-button (click)="editPack(row)" color="primary"><md-icon>edit</md-icon></button>
+      <button md-icon-button [mdMenuTriggerFor]="packMenu" aria-label="Pack menu">
+        <md-icon>more_vert</md-icon>
+      </button>
+       
+      <md-menu #packMenu="mdMenu">
+          <button md-menu-item *ngFor="let action of pack_menu_options" (click)="packAction(action.command, row)" [disabled]="!isActionAvailable(row)">
+            <md-icon *ngIf="!!action.icon">{{ action.icon }}</md-icon> {{ action.name }}
+          </button>
+      </md-menu>
+      <button md-icon-button (click)="showLicenses(row)" color="accent"><md-icon>arrow_forward</md-icon></button>
     </div>
   </template>
 </td-data-table>
-<td-paging-bar #pagingBar [pageSizes]="[10, 20, 40]" [total]="filteredTotal" (change)="page($event)">
+<td-paging-bar #pagingBar [pageSizes]="[10, 20, 40]" [total]="filteredTotal" (change)="page($event)" align="end">
   <span td-paging-bar-label hide-xs>Rows per page:</span> {{pagingBar.range}} <span hide-xs>of {{pagingBar.total}}</span>
-</td-paging-bar>
\ No newline at end of file
+</td-paging-bar>
+</div>
+</div>
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/app/pack.list.component.ts b/securis/src/main/webapp/src/app/pack.list.component.ts
index f931ef8..0af18bb 100644
--- a/securis/src/main/webapp/src/app/pack.list.component.ts
+++ b/securis/src/main/webapp/src/app/pack.list.component.ts
@@ -1,3 +1,4 @@
+import { Router } from '@angular/router';
 import { MdDialog } from '@angular/material';
 import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
 import { IPageChangeEvent } from '@covalent/core';
@@ -42,7 +43,6 @@
 export class PackListComponent implements AfterViewInit {
   data: any[] = [];
   columns: ITdDataTableColumn[] = [
-    { name: 'lics', label: '' },
     { name: 'code', label: 'Code', tooltip: 'License pack code' },
     { name: 'application_name', label: 'App name' },
     { name: 'licensetype_code', label: 'License type' },
@@ -61,9 +61,25 @@
   sortBy: string = 'application_name';
   sortOrder: TdDataTableSortingOrder = TdDataTableSortingOrder.Descending;
   filteredItems = this.data.length;
+  pack_menu_options : any[] = [{
+    command: 'edit',
+    name: 'Edit'
+  },{
+    command: 'cancel',
+    name: 'Cancel'
+  }]
+  
+  packAction(action: any) {
+    console.log(action.command);
+  }
+
+  isActionAvailable(pack : any) : boolean {
+    return true;
+  }
 
   constructor(private _dataTableService: TdDataTableService,
     private media: TdMediaService,
+    private router: Router,
     private dialog: MdDialog,
     private packForm: PackFormComponent,
     private packs: PacksService) {
@@ -92,9 +108,13 @@
     });
   }
 
+  showLicenses(pack: any) : void {
+    this.router.navigateByUrl('/licenses');
+  }
+
   editPack(pack: any) : void {
     var ref = this.dialog.open(PackFormComponent, {
-      height: '50%', // can be px or %
+      height: '70%', // can be px or %
       width: '40%', // can be px or %
     });
     ref.componentInstance.isNew = false;
diff --git a/securis/src/main/webapp/src/lang/messages_en.json b/securis/src/main/webapp/src/lang/messages_en.json
index 413715e..6b2ae29 100644
--- a/securis/src/main/webapp/src/lang/messages_en.json
+++ b/securis/src/main/webapp/src/lang/messages_en.json
@@ -1,4 +1,22 @@
 {
-"": "",
-"": ""
+	"field.code": "Code",
+	"field.num_licenses": "Num. licenses",
+	"field.pack": "Pack",
+	"field.id": "ID",
+	"field.init_valid_date": "Init valid date",
+	"field.end_valid_date": "End valid date",
+	"field.license_preactivation": "License preactivation",
+	"field.license_type_id": "License type",
+	"field.organization_id": "Organization",
+	"field.licensetype_code": "License type",
+	"field.organization_name": "Organization",
+	"field.preactivation_valid_period": "Preactivation valid period",
+	"field.renew_valid_period": "Renew valid period",
+	"field.application_name": "Application name",
+	"field.status": "Status",
+	"field.metadata": "Metadata",
+	"field.key": "Parameter",
+	"field.value": "Value",
+	"field.comments": "Comments",
+	"": ""
 }
\ No newline at end of file

--
Gitblit v1.3.2