rsanchez
2017-03-14 38b0782c887f046426c31766901906c614d73140
#3527 feature - Added pack form
6 files added
10 files modified
changed files
securis/src/main/java/net/curisit/securis/DevFilter.java patch | view | blame | history
securis/src/main/webapp/assets/securis.css patch | view | blame | history
securis/src/main/webapp/src/app/app.module.ts patch | view | blame | history
securis/src/main/webapp/src/app/common/default.requests.options.ts patch | view | blame | history
securis/src/main/webapp/src/app/common/error.checker.ts patch | view | blame | history
securis/src/main/webapp/src/app/common/i18n.ts patch | view | blame | history
securis/src/main/webapp/src/app/common/session.ts patch | view | blame | history
securis/src/main/webapp/src/app/forms/license.form.component.ts patch | view | blame | history
securis/src/main/webapp/src/app/forms/license.form.html patch | view | blame | history
securis/src/main/webapp/src/app/forms/pack.form.component.ts patch | view | blame | history
securis/src/main/webapp/src/app/forms/pack.form.html patch | view | blame | history
securis/src/main/webapp/src/app/license.list.component.html patch | view | blame | history
securis/src/main/webapp/src/app/license.list.component.ts patch | view | blame | history
securis/src/main/webapp/src/app/pack.list.component.html patch | view | blame | history
securis/src/main/webapp/src/app/pack.list.component.ts patch | view | blame | history
securis/src/main/webapp/src/lang/messages_en.json patch | view | blame | history
securis/src/main/java/net/curisit/securis/DevFilter.java
....@@ -37,7 +37,7 @@
3737 res.addHeader("Access-Control-Allow-Headers", "X-SECURIS-TOKEN, Content-Type");
3838 res.addHeader("Access-Control-Expose-Headers", "X-SECURIS-ERROR-MSG, X-SECURIS-ERROR-CODE, Content-Type");
3939
40
- LOG.info("Added header to: " + res.getHeaderNames());
40
+ // LOG.info("Added header to: " + res.getHeaderNames());
4141 if (!req.getMethod().equals("OPTIONS")) {
4242 fc.doFilter(sreq, sres);
4343 }
securis/src/main/webapp/assets/securis.css
....@@ -28,4 +28,17 @@
2828 .td-data-table-row > .td-data-table-cell:first-child,
2929 .td-data-table-row > .td-data-table-column:first-child {
3030 max-width: 30px !important;
31
+}
32
+
33
+md-dialog-actions > .mat-button {
34
+ margin-left: 5px !important;
35
+ margin-right: 5px !important;
36
+}
37
+
38
+.label-value {
39
+ margin-top: 20px;
40
+}
41
+
42
+input:read-only {
43
+ color: rgba(0, 0, 0, 0.50) !important;
3144 }
securis/src/main/webapp/src/app/app.module.ts
....@@ -1,3 +1,4 @@
1
+import { ErrorCheckerComponent } from './common/error.checker';
12 import { NgModule } from '@angular/core';
23 import { BrowserModule } from '@angular/platform-browser';
34 import { CommonModule } from '@angular/common';
....@@ -11,6 +12,7 @@
1112 import { ToastModule } from 'ng2-toastr/ng2-toastr';
1213
1314 import { AppHomeComponent } from './app.component';
15
+import { I18nDirective } from './common/i18n';
1416 import { UserService } from './user.service';
1517 import { PacksService } from './resources/packs';
1618 import { LicenseTypesService } from './resources/license_types';
....@@ -25,6 +27,7 @@
2527 import { appRoutes, appRoutingProviders } from './app.routes';
2628 import { requestOptionsProvider, requestBackendProvider } from './common/default.requests.options';
2729 import { LocaleServiceModule } from './common/i18n';
30
+import { SeCurisSession } from './common/session';
2831 import { PackFormComponent } from "./forms/pack.form.component";
2932
3033 @NgModule({
....@@ -48,11 +51,14 @@
4851 PackListComponent,
4952 PackFormComponent,
5053 LoginFormComponent,
54
+ ErrorCheckerComponent,
55
+ I18nDirective,
5156 AppHomeComponent
5257 ],
5358 bootstrap: [ AppHomeComponent ],
5459 entryComponents: [ PackFormComponent ],
5560 providers: [
61
+ SeCurisSession,
5662 UserService,
5763 PacksService,
5864 LicensesService,
securis/src/main/webapp/src/app/common/default.requests.options.ts
....@@ -31,7 +31,7 @@
3131
3232 }
3333 }
34
-
34
+
3535 @Injectable()
3636 export class ApiXHRBackend extends XHRBackend {
3737
....@@ -40,7 +40,7 @@
4040 }
4141
4242 createConnection(request: Request): XHRConnection {
43
- if (!request.url.endsWith('.js') && !request.url.endsWith('.html') && !request.url.endsWith('.svg')){
43
+ if (!request.url.endsWith('.js') && !request.url.endsWith('.json') && !request.url.endsWith('.html') && !request.url.endsWith('.svg')){
4444 request.url = 'http://localhost:8080/securis/' + request.url; // prefix base url
4545 }
4646 return super.createConnection(request);
securis/src/main/webapp/src/app/common/error.checker.ts
....@@ -0,0 +1,55 @@
1
+import { Http } from '@angular/http';
2
+import { PacksService } from '../resources/packs';
3
+import { LocaleService } from '../common/i18n';
4
+import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
5
+import { IPageChangeEvent } from '@covalent/core';
6
+import { Component, Input, AfterViewInit } from '@angular/core';
7
+import { TdMediaService } from '@covalent/core';
8
+
9
+@Component({
10
+ selector: 'error-checker',
11
+ template: `
12
+ <div *ngIf="formField?.touched && formField.invalid" layout="column">
13
+ <span *ngFor="let err of getFieldErrors()" align="end">{{err}}</span>
14
+ </div>`
15
+})
16
+export class ErrorCheckerComponent {
17
+
18
+ @Input() formField: any;
19
+ @Input() fieldName: string;
20
+
21
+ constructor(private $L: LocaleService) {
22
+ console.log('contructor: ' + this.formField);
23
+ }
24
+
25
+ getFieldErrors() : string[] {
26
+ if (this.formField.valid) {
27
+ return []
28
+ } else {
29
+ return (<string[]>Object.keys(this.formField.errors)).map((err:string) => this.getErrorMsg(err));
30
+ }
31
+ }
32
+
33
+ private updateFieldErrors() {
34
+ }
35
+
36
+ private getErrorMsg(err: string) : string{
37
+ switch(err) {
38
+ case 'required': {
39
+ return this.fieldName + ' '+ this.$L.get('is required');
40
+ }
41
+ case 'number': {
42
+ return this.fieldName + ' '+ this.$L.get('should be a number');
43
+ }
44
+ default: {
45
+ return this.fieldName + ' '+ this.$L.get('unknown error') + ' ' + err;
46
+ }
47
+ }
48
+ }
49
+
50
+ log(obj: any) {
51
+ console.log(obj)
52
+ }
53
+
54
+}
55
+
securis/src/main/webapp/src/app/common/i18n.ts
....@@ -5,13 +5,14 @@
55 // Use as reference: https://github.com/ngx-translate/core/tree/master/src
66 @Injectable()
77 export class LocaleService {
8
- get URL_TPL() {return 'src/lang/messages-{0}.json'};
8
+ get URL_TPL() {return 'src/lang/messages_{0}.json'};
99
10
+ private _devLang : string = 'en';
1011 private _currentLang : string = null;
1112 private _messages : any = null;
1213 private _elements = new Array<ElementRef>();
1314 private constructor(private http: Http, @Inject('INITIAL_LANG') initLang: string) {
14
- this._currentLang = initLang || this.getBrowserLang();
15
+ this.lang = initLang || this.getBrowserLang();
1516 }
1617
1718 get lang() : string {
....@@ -21,7 +22,7 @@
2122 set lang(newLang: string) {
2223 this._currentLang = newLang;
2324 this.http.get(this._format(this.URL_TPL, newLang)).subscribe((data) => {
24
- this._messages = data;
25
+ this._messages = data.json();
2526 this.reloadTexts();
2627 });
2728 }
....@@ -48,7 +49,7 @@
4849 /**
4950 * It works similar to MessageFormat in Java
5051 */
51
- _format(str: string, ...params: Array<string>) {
52
+ _format(str: string, ...params: string[]) {
5253
5354 return str.replace(/\{(\d+)\}/g, function(match, index) {
5455 return params[index];
....@@ -78,7 +79,7 @@
7879 * $L.get('hello'); // This returns "hola"
7980 * $L.get('Hello {0}!!', 'John'); // This returns: "Hola John!!" if language is spanish
8081 */
81
- get(msg: string) : string {
82
+ get(msg: string, ...params: string[] ) : string {
8283 if (msg == null) {
8384 return '';
8485 }
....@@ -90,16 +91,15 @@
9091 trans_msg = this._messages[msg];
9192 } else if (this._messages[msg.toLowerCase()]) {
9293 trans_msg = this._messages[msg.toLowerCase()];
93
- } else {
94
- this._currentLang !== 'es' && console.error("Missing i18 key: " + msg);
94
+ } else {
95
+ (this._currentLang !== this._devLang) && console.error("Missing i18 key: " + msg);
9596 trans_msg = msg;
9697 }
9798 }
9899 // Enviar evento cuando el idioma cambia al $rootScope
99100
100101 if (arguments.length === 1) return trans_msg;
101
- var params = Array.prototype.slice.call(arguments, 1);
102
- return this._format.apply(trans_msg, params);
102
+ return this._format(trans_msg, ...params);
103103 }
104104
105105 }
....@@ -108,6 +108,7 @@
108108 @Directive({ selector: '[i18n]' })
109109 export class I18nDirective {
110110 constructor(private el: ElementRef, private renderer: Renderer, private $L: LocaleService) {
111
+ console.log(el);
111112 }
112113
113114 ngAfterViewChecked() {
....@@ -124,7 +125,6 @@
124125 })
125126 export class LocaleServiceModule {
126127 static withConfig(initLang?: string): ModuleWithProviders {
127
- console.log('Init lang with ' + initLang);
128128 return {
129129 ngModule: LocaleServiceModule,
130130 providers: [
securis/src/main/webapp/src/app/common/session.ts
....@@ -0,0 +1,30 @@
1
+import { LocalStorageService } from 'angular-2-local-storage';
2
+import { Injectable } from '@angular/core';
3
+
4
+
5
+@Injectable()
6
+export class SeCurisSession {
7
+
8
+ sessionData: any = {}
9
+
10
+ constructor(private store: LocalStorageService) {
11
+
12
+ }
13
+
14
+ public get(key: string): any {
15
+ return this.sessionData[key];
16
+ }
17
+
18
+ public exists(key: string): boolean {
19
+ return this.sessionData[key] != undefined;
20
+ }
21
+
22
+ public set(key: string, value: any): void {
23
+ this.sessionData[key] = value;
24
+ }
25
+
26
+ public remove(key: string) : void {
27
+ delete this.sessionData[key];
28
+ }
29
+}
30
+
securis/src/main/webapp/src/app/forms/license.form.component.ts
....@@ -0,0 +1,53 @@
1
+import { Http } from '@angular/http';
2
+import { LicensesService } from '../resources/licenses';
3
+import { LocaleService } from '../common/i18n';
4
+import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
5
+import { IPageChangeEvent } from '@covalent/core';
6
+import { Component, AfterViewInit } from '@angular/core';
7
+import { TdMediaService } from '@covalent/core';
8
+import { IComboOption } from './base';
9
+
10
+@Component({
11
+ selector: 'license-form',
12
+ templateUrl: 'src/app/forms/license.form.html'
13
+})
14
+export class LicenseFormComponent implements AfterViewInit {
15
+
16
+ form_title: string = 'Title';
17
+ form_subtitle: string = '';
18
+ data: any = {};
19
+ isNew : boolean = true;
20
+
21
+ constructor(private http: Http,
22
+ private packs: LicensesService,
23
+ private $L: LocaleService) {
24
+
25
+ }
26
+
27
+ private loadCombos(): void {
28
+ /*
29
+ this.http.get('organization')
30
+ .map(response => response.json().map((org : any) => <IComboOption>{id: org.id, label: `(${org.code}) ${org.name}`}))
31
+ .subscribe(
32
+ data => this.organizations = (<IComboOption[]>data).sort((e1, e2) => e1.label.localeCompare(e2.label)),
33
+ err => console.error('Error loading orgs')
34
+ );
35
+ */
36
+ }
37
+
38
+ log(obj: any) {
39
+ console.log(obj)
40
+ }
41
+
42
+ ngOnInit(): void {
43
+ this.loadCombos();
44
+ this.data = {};
45
+ this.form_title = this.$L.get('License data');
46
+ this.form_subtitle = this.$L.get(this.isNew ? 'Create a new license': 'Modify the license data') ;
47
+ }
48
+
49
+
50
+ ngAfterViewInit(): void {
51
+ }
52
+}
53
+
securis/src/main/webapp/src/app/forms/license.form.html
....@@ -0,0 +1,75 @@
1
+<form #packForm="ngForm" class="inset" (keyup.enter)="save()">
2
+ <md-card>
3
+ <md-card-title>
4
+ {{form_title}}
5
+ </md-card-title>
6
+ <md-card-subtitle>
7
+ {{form_subtitle}}
8
+ </md-card-subtitle>
9
+<md-divider></md-divider>
10
+ <md-card-content>
11
+ <div layout="column" layout-align="start center" layout-fill>
12
+ <div layout="row" layout-fill layout-padding>
13
+ <div layout="column" layout-fill flex="50">
14
+ <md-input-container>
15
+ <input mdInput maxLength="50" type="text" [(ngModel)]="data.code" name="code" required />
16
+ <md-placeholder>
17
+ <span i18n>Code</span>
18
+ </md-placeholder>
19
+ </md-input-container>
20
+ {{log(packForm.controls)}}
21
+ <div *ngIf="packForm.controls.code?.touched && packForm.controls.code.invalid" layout="column">
22
+ <span *ngIf="packForm.controls.code.errors.required" align="end">Code is required</span>
23
+ <span align="end">Code is ok</span>
24
+ </div>
25
+ </div>
26
+ <!-- TODO: <div class="alert inline-alert alert-warning" ng-show="packForm.code.$invalid">
27
+ <span class="glyphicon glyphicon-warning-sign"></span>
28
+ <span ng-show="packForm.code.$error.maxlength" ng-bind="maxLengthErrorMsg('Code', maxlength.code)"
29
+ class="ng-binding ng-hide">Code length is too long (max: 50).</span>
30
+ <span ng-show="packForm.code.$error.required" ng-bind="mandatoryFieldErrorMsg('Code')" class="ng-binding">'Code' is required.</span>
31
+ </div> -->
32
+ <md-input-container flex="50">
33
+ <input mdInput type="number" type="text" [(ngModel)]="data.num_licenses" name="num_licenses" required />
34
+ <md-placeholder>
35
+ <span i18n>Num. licenses</span>
36
+ </md-placeholder>
37
+ </md-input-container>
38
+ </div>
39
+ <div layout="row" layout-align="start center" layout-fill layout-padding>
40
+ <md-input-container flex>
41
+ <input mdInput type="date" type="text" [(ngModel)]="data.init_valid_date" name="init_valid_date" required />
42
+ <md-placeholder>
43
+ <span i18n>Initial date</span>
44
+ </md-placeholder>
45
+ <md-hint align="end">YYYY-MM-DD</md-hint>
46
+ </md-input-container>
47
+ <md-input-container flex>
48
+ <input mdInput type="date" type="text" [(ngModel)]="data.end_valid_date" name="end_valid_date" required />
49
+ <md-placeholder>
50
+ <span i18n>End date</span>
51
+ </md-placeholder>
52
+ <md-hint align="end">YYYY-MM-DD</md-hint>
53
+ </md-input-container>
54
+ </div>
55
+ <div layout="row" layout-fill layout-padding>
56
+ <md-select flex placeholder="Organization" [(ngModel)]="data.organization_id" name="organization_id">
57
+ <md-option *ngFor="let org of organizations" [value]="org.id">
58
+ {{org.label}}
59
+ </md-option>
60
+ </md-select>
61
+ <md-select flex placeholder="License type" [(ngModel)]="data.license_type_id" name="license_type_id">
62
+ <md-option *ngFor="let lt of licensetypes" [value]="lt.id">
63
+ {{lt.label}}
64
+ </md-option>
65
+ </md-select>
66
+ </div>
67
+ </div>
68
+</md-card-content>
69
+<md-divider></md-divider>
70
+<md-card-actions>
71
+ <button [disabled]="!packForm.form.valid" md-raised-button color="primary" (click)="save()">Save</button>
72
+ <button md-button md-dialog-close>Cancel</button>
73
+</md-card-actions>
74
+</md-card>
75
+</form>
securis/src/main/webapp/src/app/forms/pack.form.component.ts
....@@ -1,11 +1,16 @@
11 import { Http } from '@angular/http';
2
+import { ToastsManager } from 'ng2-toastr/ng2-toastr';
3
+
24 import { PacksService } from '../resources/packs';
5
+import { LicenseTypesService } from '../resources/license_types';
36 import { LocaleService } from '../common/i18n';
47 import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
58 import { IPageChangeEvent } from '@covalent/core';
69 import { Component, AfterViewInit } from '@angular/core';
710 import { TdMediaService } from '@covalent/core';
811 import { IComboOption } from './base';
12
+
13
+
914
1015 @Component({
1116 selector: 'pack-form',
....@@ -19,11 +24,35 @@
1924 licensetypes : IComboOption[];
2025 data: any = {};
2126 isNew : boolean = true;
22
-
27
+ fields: any = {
28
+ 'code': '',
29
+ 'num_licenses': '',
30
+ 'init_valid_date': '',
31
+ 'end_valid_date': '',
32
+ 'license_preactivation': '',
33
+ 'license_type_id': '',
34
+ 'organization_id': '',
35
+ 'licensetype_code': '',
36
+ 'organization_name': '',
37
+ 'preactivation_valid_period': '',
38
+ 'renew_valid_period': '',
39
+ 'application_name': '',
40
+ 'status': '',
41
+ 'metadata': '',
42
+ 'comments': '',
43
+ 'key': '',
44
+ 'value': '',
45
+ }
2346 constructor(private http: Http,
47
+ private toaster: ToastsManager,
48
+ private licenseTypes: LicenseTypesService,
2449 private packs: PacksService,
2550 private $L: LocaleService) {
26
-
51
+ Object.keys(this.fields).forEach(k => this.fields[k] = $L.get(`field.${k}`));
52
+ }
53
+
54
+ public getFieldName(fieldId: string) : string {
55
+ return this.fields[fieldId];
2756 }
2857
2958 private loadCombos(): void {
....@@ -33,23 +62,47 @@
3362 data => this.organizations = (<IComboOption[]>data).sort((e1, e2) => e1.label.localeCompare(e2.label)),
3463 err => console.error('Error loading orgs')
3564 );
36
- this.http.get('licensetype')
37
- .map(response => response.json().map((lt : any) => <IComboOption>{id: lt.id, label: `(${lt.code}) ${lt.name}`}))
65
+ this.licenseTypes.get()
66
+ .map(list => list.map((lt : any) => <IComboOption>{id: lt.id, label: `(${lt.code}) ${lt.name}`}))
3867 .subscribe(
3968 data => this.licensetypes = (<IComboOption[]>data).sort((e1, e2) => e1.label.localeCompare(e2.label)),
4069 err => console.error('Error loading license types')
4170 );
4271 }
4372
44
- log(obj: any) {
45
- console.log(obj)
73
+ save() {
74
+ var command = this.isNew ? this.packs.create(this.data) : this.packs.modify(this.data.id, this.data);
75
+ command.subscribe(
76
+ data => this.toaster.success(this.$L.get('Pack saved sucessfully')),
77
+ err => {
78
+ console.error(err);
79
+ this.toaster.success(this.$L.get('Error saving pack'));
80
+ }
81
+ );
4682 }
83
+
84
+ changeLicType(event) {
85
+ this.licenseTypes.get(this.data.license_type_id)
86
+ .map(lt_data => lt_data.metadata)
87
+ .subscribe(
88
+ metadata => this.data.metadata = metadata,
89
+ err => {
90
+ console.error('Error loading license type metadata');
91
+ console.error(err);
92
+ }
93
+ );
94
+ }
95
+
96
+ changeOrg(event) {
97
+ console.log(event);
98
+ console.log(this.data.organization_id);
99
+ }
47100
48101 ngOnInit(): void {
49102 this.loadCombos();
50
- this.data = {};
103
+ // this.data = {};
51104 this.form_title = this.$L.get('Pack data');
52
- this.form_subtitle = this.$L.get('Create a new licenses pack');
105
+ this.form_subtitle = this.$L.get(this.isNew ? 'Create a new licenses pack': 'Modify the licenses pack data') ;
53106 }
54107
55108
securis/src/main/webapp/src/app/forms/pack.form.html
....@@ -1,75 +1,174 @@
1
-<form #packForm="ngForm" class="inset" (keyup.enter)="save()">
2
- <md-card>
3
- <md-card-title>
4
- {{form_title}}
5
- </md-card-title>
6
- <md-card-subtitle>
7
- {{form_subtitle}}
8
- </md-card-subtitle>
1
+<md-toolbar>
2
+ <span md-dialog-title>{{form_title}}</span>
3
+ <span flex></span>
4
+ <button md-icon-button (click)="save()"><md-icon>save</md-icon></button>
5
+ <button md-icon-button md-dialog-close><md-icon>close</md-icon></button>
6
+</md-toolbar>
7
+<p class="md-subhead">{{form_subtitle}}</p>
98 <md-divider></md-divider>
10
- <md-card-content>
11
- <div layout="column" layout-align="start center" layout-fill>
9
+<form #packForm="ngForm" class="inset" >
10
+<div layout="column" >
11
+<md-dialog-content>
12
+ <div layout="column" layout-align="start center">
1213 <div layout="row" layout-fill layout-padding>
13
- <div layout="column" layout-fill flex="50">
14
- <md-input-container>
15
- <input mdInput maxLength="50" type="text" [(ngModel)]="data.code" name="code" required />
16
- <md-placeholder>
17
- <span i18n>Code</span>
18
- </md-placeholder>
19
- </md-input-container>
20
- {{log(packForm.controls)}}
21
- <div *ngIf="packForm.controls.code?.touched && packForm.controls.code.invalid" layout="column">
22
- <span *ngIf="packForm.controls.code.errors.required" align="end">Code is required</span>
23
- <span align="end">Code is ok</span>
14
+ <div layout="column" layout-fill flex="15" *ngIf="!isNew">
15
+ <md-input-container>
16
+ <input mdInput maxLength="50" type="text" [(ngModel)]="data.id" name="id" required [readonly]="!isNew" />
17
+ <md-placeholder>
18
+ <span i18n="field.id"></span>
19
+ </md-placeholder>
20
+ </md-input-container>
2421 </div>
22
+ <div layout="column" layout-fill flex>
23
+ <md-input-container>
24
+ <input mdInput maxLength="50" type="text" [(ngModel)]="data.code" name="code" required [readonly]="!isNew" />
25
+ <md-placeholder>
26
+ <span i18n="field.code"></span>
27
+ </md-placeholder>
28
+ </md-input-container>
29
+ <error-checker [fieldName]="getFieldName('code')" [formField]="packForm.controls.code"></error-checker>
2530 </div>
26
- <!-- TODO: <div class="alert inline-alert alert-warning" ng-show="packForm.code.$invalid">
27
- <span class="glyphicon glyphicon-warning-sign"></span>
28
- <span ng-show="packForm.code.$error.maxlength" ng-bind="maxLengthErrorMsg('Code', maxlength.code)"
29
- class="ng-binding ng-hide">Code length is too long (max: 50).</span>
30
- <span ng-show="packForm.code.$error.required" ng-bind="mandatoryFieldErrorMsg('Code')" class="ng-binding">'Code' is required.</span>
31
- </div> -->
32
- <md-input-container flex="50">
33
- <input mdInput type="number" type="text" [(ngModel)]="data.num_licenses" name="num_licenses" required />
34
- <md-placeholder>
35
- <span i18n>Num. licenses</span>
36
- </md-placeholder>
37
- </md-input-container>
38
- </div>
39
- <div layout="row" layout-align="start center" layout-fill layout-padding>
40
- <md-input-container flex>
41
- <input mdInput type="date" type="text" [(ngModel)]="data.init_valid_date" name="init_valid_date" required />
42
- <md-placeholder>
43
- <span i18n>Initial date</span>
44
- </md-placeholder>
45
- <md-hint align="end">YYYY-MM-DD</md-hint>
46
- </md-input-container>
47
- <md-input-container flex>
48
- <input mdInput type="date" type="text" [(ngModel)]="data.end_valid_date" name="end_valid_date" required />
49
- <md-placeholder>
50
- <span i18n>End date</span>
51
- </md-placeholder>
52
- <md-hint align="end">YYYY-MM-DD</md-hint>
53
- </md-input-container>
31
+ <div layout="column" layout-fill flex>
32
+ <md-input-container>
33
+ <input mdInput type="number" [(ngModel)]="data.num_licenses" name="num_licenses" required />
34
+ <md-placeholder>
35
+ <span i18n="field.num_licenses"></span>
36
+ </md-placeholder>
37
+ </md-input-container>
38
+ <error-checker [fieldName]="getFieldName('num_licenses')" [formField]="packForm.controls.num_licenses"></error-checker>
39
+ </div>
5440 </div>
5541 <div layout="row" layout-fill layout-padding>
56
- <md-select flex placeholder="Organization" [(ngModel)]="data.organization_id" name="organization_id">
57
- <md-option *ngFor="let org of organizations" [value]="org.id">
58
- {{org.label}}
59
- </md-option>
60
- </md-select>
61
- <md-select flex placeholder="License type" [(ngModel)]="data.license_type_id" name="license_type_id">
62
- <md-option *ngFor="let lt of licensetypes" [value]="lt.id">
63
- {{lt.label}}
64
- </md-option>
65
- </md-select>
42
+ <div layout="column" layout-fill flex>
43
+ <md-input-container flex>
44
+ <input mdInput type="date" [(ngModel)]="data.init_valid_date" name="init_valid_date" required />
45
+ <md-placeholder>
46
+ <span i18n="field.end_valid_date"></span>
47
+ </md-placeholder>
48
+ </md-input-container>
49
+ <error-checker [fieldName]="getFieldName('init_valid_date')" [formField]="packForm.controls.init_valid_date"></error-checker>
50
+ </div>
51
+ <div layout="column" layout-fill flex>
52
+ <md-input-container flex>
53
+ <input mdInput type="date" [(ngModel)]="data.end_valid_date" name="end_valid_date" required />
54
+ <md-placeholder>
55
+ <span i18n="field.end_valid_date"></span>
56
+ </md-placeholder>
57
+ </md-input-container>
58
+ <error-checker [fieldName]="getFieldName('end_valid_date')" [formField]="packForm.controls.end_valid_date"></error-checker>
59
+ </div>
60
+ </div>
61
+ <div layout="row" layout-fill layout-padding *ngIf="isNew">
62
+ <div layout="column" layout-fill flex>
63
+ <md-select [placeholder]="getFieldName('organization_id')" flex [(ngModel)]="data.organization_id" name="organization_id" (change)="changeOrg($event)">
64
+ <md-option *ngFor="let org of organizations" [value]="org.id">
65
+ {{org.label}}
66
+ </md-option>
67
+ </md-select>
68
+ <error-checker [fieldName]="getFieldName('organization_id')" [formField]="packForm.controls.organization_id"></error-checker>
69
+ </div>
70
+ <div layout="column" layout-fill flex>
71
+ <md-select flex [placeholder]="getFieldName('license_type_id')" [(ngModel)]="data.license_type_id" name="license_type_id" (change)="changeLicType($event)">
72
+ <md-option *ngFor="let lt of licensetypes" [value]="lt.id">
73
+ {{lt.label}}
74
+ </md-option>
75
+ </md-select>
76
+ <error-checker [fieldName]="getFieldName('license_type_id')" [formField]="packForm.controls.license_type_id"></error-checker>
77
+ </div>
78
+ </div>
79
+ <div layout="row" layout-fill layout-padding *ngIf="!isNew">
80
+ <div layout="column" layout-fill flex>
81
+ <div layout="column" class="mat-input-container" flex>
82
+ <label class="mat-input-placeholder mat-float">
83
+ <span class="placeholder" i18n="field.organization_id"></span>
84
+ </label>
85
+ <div class="label-value mat-input-element">{{data.organization_name}}</div>
86
+ </div>
87
+ </div>
88
+ <div layout="column" layout-fill flex>
89
+ <div layout="column" class="mat-input-container" flex>
90
+ <label class="mat-input-placeholder mat-float">
91
+ <span class="placeholder" i18n="field.license_type_id"></span>
92
+ </label>
93
+ <div class="label-value mat-input-element">{{data.licensetype_code}}</div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ <div layout="row" layout-fill layout-padding >
98
+ <div layout="column" layout-fill flex>
99
+ <md-input-container flex>
100
+ <input mdInput type="number" [(ngModel)]="data.preactivation_valid_period" name="preactivation_valid_period" required />
101
+ <md-placeholder>
102
+ <span i18n="field.preactivation_valid_period"></span>
103
+ </md-placeholder>
104
+ <md-hint align="end">days</md-hint>
105
+ </md-input-container>
106
+ <error-checker [fieldName]="getFieldName('preactivation_valid_period')" [formField]="packForm.controls.preactivation_valid_period"></error-checker>
107
+ </div>
108
+ <div layout="column" layout-fill flex>
109
+ <md-input-container flex>
110
+ <input mdInput type="number"[(ngModel)]="data.renew_valid_period" name="renew_valid_period" required />
111
+ <md-placeholder>
112
+ <span i18n="field.renew_valid_period"></span>
113
+ </md-placeholder>
114
+ <md-hint align="end">days</md-hint>
115
+ </md-input-container>
116
+ <error-checker [fieldName]="getFieldName('renew_valid_period')" [formField]="packForm.controls.renew_valid_period"></error-checker>
117
+ </div>
118
+ </div>
119
+ <div layout="row" layout-fill layout-padding>
120
+ <div layout="column" layout-fill flex>
121
+ <md-input-container flex>
122
+ <textarea mdInput type="text" type="text" [(ngModel)]="data.comments" name="comments" maxlength="1024" ></textarea>
123
+ <md-placeholder>
124
+ <span i18n="field.comments"></span>
125
+ </md-placeholder>
126
+ <md-hint align="end">(max 1024)</md-hint>
127
+ </md-input-container>
128
+ </div>
129
+ </div>
130
+ <div layout="row" layout-fill layout-padding *ngIf="!isNew">
131
+ <div layout="column" layout-fill flex>
132
+ <div layout="column" class="mat-input-container" flex>
133
+ <label class="mat-input-placeholder mat-float">
134
+ <span class="placeholder" i18n="field.created_by_name"></span>
135
+ </label>
136
+ <div class="label-value mat-input-element">{{data.created_by_name}}</div>
137
+ </div>
138
+ </div>
139
+ <div layout="column" layout-fill flex>
140
+ <div layout="column" class="mat-input-container" flex>
141
+ <label class="mat-input-placeholder mat-float">
142
+ <span class="placeholder" i18n="field.creation_timestamp"></span>
143
+ </label>
144
+ <div class="label-value mat-input-element">{{data.creation_timestamp | date: 'medium'}}</div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+ <div layout="column" layout-fill>
149
+ <span class="md-title" i18n>License metadata</span>
150
+ <div layout="row" layout-fill layout-padding *ngFor="let pair of data.metadata">
151
+ <md-input-container flex="40">
152
+ <input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.key" readonly />
153
+ <md-placeholder>
154
+ <span i18n="field.key"></span>
155
+ </md-placeholder>
156
+ </md-input-container>
157
+ <md-input-container flex>
158
+ <input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.value" [readonly]="pair.readonly" [required]="pair.required" />
159
+ <md-placeholder>
160
+ <span i18n="field.value"></span>
161
+ </md-placeholder>
162
+ </md-input-container>
163
+ </div>
66164 </div>
67165 </div>
68
-</md-card-content>
166
+</md-dialog-content>
167
+<div flex></div>
69168 <md-divider></md-divider>
70
-<md-card-actions>
71
- <button [disabled]="!packForm.form.valid" md-raised-button color="primary" (click)="save()">Save</button>
72
- <button md-button md-dialog-close>Cancel</button>
73
-</md-card-actions>
74
-</md-card>
169
+<md-dialog-actions layout="row" layout-align="end center">
170
+ <button [disabled]="!packForm.form.valid" md-raised-button color="primary" (click)="save()">Save</button>
171
+ <button md-button md-dialog-close>Cancel</button>
172
+</md-dialog-actions>
173
+</div>
75174 </form>
securis/src/main/webapp/src/app/license.list.component.html
....@@ -0,0 +1,51 @@
1
+<md-toolbar role="toolbar" class="mat-secondary">
2
+ <span class="push-left-sm">
3
+ <span class="md-title" i18n>License Packs</span>
4
+ </span>
5
+ <span class="push-left-sm" *ngIf="filteredItems < data.length">
6
+ <span class="md-body-1">{{filteredItems}} of {{data.length}} packs filtered</span>
7
+ </span>
8
+ <td-search-box #searchBox class="push-right-sm" placeholder="Search here" (searchDebounce)="search($event)" flex>
9
+ </td-search-box>
10
+ <button md-mini-fab color="accent" (click)="createPack()">
11
+ <md-icon>add</md-icon>
12
+ </button>
13
+</md-toolbar>
14
+<md-divider></md-divider>
15
+<div layout="row" layout-align="center center">
16
+ <div flex="80" layout="column" layout-align="end center" >
17
+<td-data-table [data]="filteredData" [columns]="columns" style="width: 100%">
18
+ <template tdDataTableTemplate="used_licenses" let-row="row">
19
+ <div layout="row">
20
+ <td-notification-count color="secondary" [notifications]="row['num_licenses']">
21
+ </td-notification-count>
22
+ <td-notification-count color="primary" [notifications]="row['num_available']">
23
+ </td-notification-count>
24
+ </div>
25
+ </template>
26
+ <template tdDataTableTemplate="code" let-row="row" let-value="value">
27
+ <div layout="row" layout-align="start center">
28
+ <span style="white-space: nowrap">{{value}}</span>
29
+ </div>
30
+ </template>
31
+ <template tdDataTableTemplate="menu" let-row="row" let-index="index">
32
+ <div layout="row" layout-align="end center">
33
+ <button md-icon-button (click)="editPack(row)" color="primary"><md-icon>edit</md-icon></button>
34
+ <button md-icon-button [mdMenuTriggerFor]="packMenu" aria-label="Pack menu">
35
+ <md-icon>more_vert</md-icon>
36
+ </button>
37
+
38
+ <md-menu #packMenu="mdMenu">
39
+ <button md-menu-item *ngFor="let action of pack_menu_options" (click)="packAction(action.command, row)" [disabled]="!isActionAvailable(row)">
40
+ <md-icon *ngIf="!!action.icon">{{ action.icon }}</md-icon> {{ action.name }}
41
+ </button>
42
+ </md-menu>
43
+ <button md-icon-button (click)="showLicenses(row)" color="accent"><md-icon>arrow_forward</md-icon></button>
44
+ </div>
45
+ </template>
46
+</td-data-table>
47
+<td-paging-bar #pagingBar [pageSizes]="[10, 20, 40]" [total]="filteredTotal" (change)="page($event)" align="end">
48
+ <span td-paging-bar-label hide-xs>Rows per page:</span> {{pagingBar.range}} <span hide-xs>of {{pagingBar.total}}</span>
49
+</td-paging-bar>
50
+</div>
51
+</div>
securis/src/main/webapp/src/app/license.list.component.ts
....@@ -0,0 +1,151 @@
1
+import { Router } from '@angular/router';
2
+import { MdDialog } from '@angular/material';
3
+import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
4
+import { IPageChangeEvent } from '@covalent/core';
5
+import { Component, AfterViewInit } from '@angular/core';
6
+import { TdMediaService } from '@covalent/core';
7
+import { LicensesService } from './resources/licenses';
8
+import { LicenseFormComponent } from './forms/license.form.component';
9
+
10
+var lic_example = { activation_code: '19fa8d30-29cb-4b59-81b5-3837af8204b6',
11
+ code: 'CISA02-494-1',
12
+ code_suffix: 1,
13
+ created_by_id: '_client',
14
+ creation_timestamp: 1447848747000,
15
+ email: 'ccalvo@curisit.net',
16
+ expiration_date: 1450440747000,
17
+ full_name: 'César SA',
18
+ id: 110,
19
+ 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=="}',
20
+ modification_timestamp: 1447848747000,
21
+ pack_code: 'CISA02',
22
+ pack_id: 18,
23
+ 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"}',
24
+ status: 'AC' }
25
+
26
+@Component({
27
+ selector: 'license-list',
28
+ templateUrl: 'src/app/license.list.component.html'
29
+})
30
+export class LicenseListComponent implements AfterViewInit {
31
+ data: any[] = [];
32
+ columns: ITdDataTableColumn[] = [
33
+ { name: 'code', label: 'Code', tooltip: 'License pack code' },
34
+ { name: 'application_name', label: 'App name' },
35
+ { name: 'licensetype_code', label: 'License type' },
36
+ { name: 'organization_name', label: 'Organization' },
37
+ { name: 'used_licenses', label: 'Lics', tooltip: 'Initial/Available pack licenses' },
38
+ { name: 'menu', label: '' }
39
+ ];
40
+
41
+ filteredData: any[] = this.data;
42
+ filteredTotal: number = this.data.length;
43
+
44
+ searchTerm: string = '';
45
+ fromRow: number = 1;
46
+ currentPage: number = 1;
47
+ pageSize: number = 10;
48
+ sortBy: string = 'application_name';
49
+ sortOrder: TdDataTableSortingOrder = TdDataTableSortingOrder.Descending;
50
+ filteredItems = this.data.length;
51
+ license_menu_options : any[] = [{
52
+ icon: 'edit',
53
+ command: 'edit',
54
+ name: 'Edit'
55
+ },{
56
+ icon: 'cancel',
57
+ command: 'cancel',
58
+ name: 'Cancel'
59
+ }]
60
+
61
+ licenseAction(action: any) {
62
+ console.log(action.command);
63
+ }
64
+
65
+ isActionAvailable(pack : any) : boolean {
66
+ return true;
67
+ }
68
+
69
+ constructor(private _dataTableService: TdDataTableService,
70
+ private media: TdMediaService,
71
+ private router: Router,
72
+ private dialog: MdDialog,
73
+ private licenseForm: LicenseFormComponent,
74
+ private licenses: LicensesService) {
75
+ this.licenses.get().subscribe(
76
+ list => {
77
+ this.data = list;
78
+ this.filter();
79
+ },
80
+ err => console.error(err)
81
+ );
82
+ }
83
+
84
+ ngOnInit(): void {
85
+ this.filter();
86
+ }
87
+
88
+ createPack() : void {
89
+ var ref = this.dialog.open(LicenseFormComponent, {
90
+ height: '50%', // can be px or %
91
+ width: '40%', // can be px or %
92
+ });
93
+ ref.componentInstance.isNew = true;
94
+ ref.afterClosed().subscribe(result => {
95
+ console.log(result);
96
+ this.filter();
97
+ });
98
+ }
99
+
100
+ editLicense(lic: any) : void {
101
+ var ref = this.dialog.open(LicenseFormComponent, {
102
+ height: '50%', // can be px or %
103
+ width: '40%', // can be px or %
104
+ });
105
+ ref.componentInstance.isNew = false;
106
+ ref.componentInstance.data = lic;
107
+ ref.afterClosed().subscribe(result => {
108
+ console.log(result);
109
+ this.filter();
110
+ });
111
+ }
112
+
113
+
114
+ sort(sortEvent: ITdDataTableSortChangeEvent): void {
115
+ this.sortBy = sortEvent.name;
116
+ this.sortOrder = sortEvent.order;
117
+ this.filter();
118
+ }
119
+
120
+ search(searchTerm: string): void {
121
+ this.searchTerm = searchTerm;
122
+ this.filter();
123
+ }
124
+
125
+ page(pagingEvent: IPageChangeEvent): void {
126
+ this.fromRow = pagingEvent.fromRow;
127
+ this.currentPage = pagingEvent.page;
128
+ this.pageSize = pagingEvent.pageSize;
129
+ this.filter();
130
+ }
131
+
132
+ loadData() {
133
+
134
+ }
135
+
136
+ filter(): void {
137
+ let newData: any[] = this.data;
138
+ newData = this._dataTableService.filterData(newData, this.searchTerm, true);
139
+ this.filteredTotal = newData.length;
140
+ this.filteredItems = newData.length;
141
+ newData = this._dataTableService.sortData(newData, this.sortBy, this.sortOrder);
142
+ newData = this._dataTableService.pageData(newData, this.fromRow, this.currentPage * this.pageSize);
143
+ this.filteredData = newData;
144
+ }
145
+
146
+ ngAfterViewInit(): void {
147
+ this.media.broadcast();
148
+
149
+ }
150
+}
151
+
securis/src/main/webapp/src/app/pack.list.component.html
....@@ -12,27 +12,40 @@
1212 </button>
1313 </md-toolbar>
1414 <md-divider></md-divider>
15
-<td-data-table [data]="filteredData" [columns]="columns">
16
-
15
+<div layout="row" layout-align="center center">
16
+ <div flex="80" layout="column" layout-align="end center" >
17
+<td-data-table [data]="filteredData" [columns]="columns" style="width: 100%">
1718 <template tdDataTableTemplate="used_licenses" let-row="row">
1819 <div layout="row">
1920 <td-notification-count color="secondary" [notifications]="row['num_licenses']">
2021 </td-notification-count>
21
- <td-notification-count color="green" [notifications]="row['num_available']">
22
+ <td-notification-count color="primary" [notifications]="row['num_available']">
2223 </td-notification-count>
2324 </div>
2425 </template>
25
- <template tdDataTableTemplate="lics" let-row="row">
26
- <button md-icon-button color="primary"><md-icon>arrow_right</md-icon></button>
26
+ <template tdDataTableTemplate="code" let-row="row" let-value="value">
27
+ <div layout="row" layout-align="start center">
28
+ <span style="white-space: nowrap">{{value}}</span>
29
+ </div>
2730 </template>
28
- <template tdDataTableTemplate="menu" let-row="row">
29
- <div layout="row">
30
- <button md-icon-button color="primary"><md-icon>arrow_right</md-icon></button>
31
- <button md-icon-button><md-icon>edit</md-icon></button>
32
- <button md-icon-button><md-icon>more_vert</md-icon></button>
31
+ <template tdDataTableTemplate="menu" let-row="row" let-index="index">
32
+ <div layout="row" layout-align="end center">
33
+ <button md-icon-button (click)="editPack(row)" color="primary"><md-icon>edit</md-icon></button>
34
+ <button md-icon-button [mdMenuTriggerFor]="packMenu" aria-label="Pack menu">
35
+ <md-icon>more_vert</md-icon>
36
+ </button>
37
+
38
+ <md-menu #packMenu="mdMenu">
39
+ <button md-menu-item *ngFor="let action of pack_menu_options" (click)="packAction(action.command, row)" [disabled]="!isActionAvailable(row)">
40
+ <md-icon *ngIf="!!action.icon">{{ action.icon }}</md-icon> {{ action.name }}
41
+ </button>
42
+ </md-menu>
43
+ <button md-icon-button (click)="showLicenses(row)" color="accent"><md-icon>arrow_forward</md-icon></button>
3344 </div>
3445 </template>
3546 </td-data-table>
36
-<td-paging-bar #pagingBar [pageSizes]="[10, 20, 40]" [total]="filteredTotal" (change)="page($event)">
47
+<td-paging-bar #pagingBar [pageSizes]="[10, 20, 40]" [total]="filteredTotal" (change)="page($event)" align="end">
3748 <span td-paging-bar-label hide-xs>Rows per page:</span> {{pagingBar.range}} <span hide-xs>of {{pagingBar.total}}</span>
38
-</td-paging-bar>
49
+</td-paging-bar>
50
+</div>
51
+</div>
securis/src/main/webapp/src/app/pack.list.component.ts
....@@ -1,3 +1,4 @@
1
+import { Router } from '@angular/router';
12 import { MdDialog } from '@angular/material';
23 import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
34 import { IPageChangeEvent } from '@covalent/core';
....@@ -42,7 +43,6 @@
4243 export class PackListComponent implements AfterViewInit {
4344 data: any[] = [];
4445 columns: ITdDataTableColumn[] = [
45
- { name: 'lics', label: '' },
4646 { name: 'code', label: 'Code', tooltip: 'License pack code' },
4747 { name: 'application_name', label: 'App name' },
4848 { name: 'licensetype_code', label: 'License type' },
....@@ -61,9 +61,25 @@
6161 sortBy: string = 'application_name';
6262 sortOrder: TdDataTableSortingOrder = TdDataTableSortingOrder.Descending;
6363 filteredItems = this.data.length;
64
+ pack_menu_options : any[] = [{
65
+ command: 'edit',
66
+ name: 'Edit'
67
+ },{
68
+ command: 'cancel',
69
+ name: 'Cancel'
70
+ }]
71
+
72
+ packAction(action: any) {
73
+ console.log(action.command);
74
+ }
75
+
76
+ isActionAvailable(pack : any) : boolean {
77
+ return true;
78
+ }
6479
6580 constructor(private _dataTableService: TdDataTableService,
6681 private media: TdMediaService,
82
+ private router: Router,
6783 private dialog: MdDialog,
6884 private packForm: PackFormComponent,
6985 private packs: PacksService) {
....@@ -92,9 +108,13 @@
92108 });
93109 }
94110
111
+ showLicenses(pack: any) : void {
112
+ this.router.navigateByUrl('/licenses');
113
+ }
114
+
95115 editPack(pack: any) : void {
96116 var ref = this.dialog.open(PackFormComponent, {
97
- height: '50%', // can be px or %
117
+ height: '70%', // can be px or %
98118 width: '40%', // can be px or %
99119 });
100120 ref.componentInstance.isNew = false;
securis/src/main/webapp/src/lang/messages_en.json
....@@ -1,4 +1,22 @@
11 {
2
-"": "",
3
-"": ""
2
+ "field.code": "Code",
3
+ "field.num_licenses": "Num. licenses",
4
+ "field.pack": "Pack",
5
+ "field.id": "ID",
6
+ "field.init_valid_date": "Init valid date",
7
+ "field.end_valid_date": "End valid date",
8
+ "field.license_preactivation": "License preactivation",
9
+ "field.license_type_id": "License type",
10
+ "field.organization_id": "Organization",
11
+ "field.licensetype_code": "License type",
12
+ "field.organization_name": "Organization",
13
+ "field.preactivation_valid_period": "Preactivation valid period",
14
+ "field.renew_valid_period": "Renew valid period",
15
+ "field.application_name": "Application name",
16
+ "field.status": "Status",
17
+ "field.metadata": "Metadata",
18
+ "field.key": "Parameter",
19
+ "field.value": "Value",
20
+ "field.comments": "Comments",
21
+ "": ""
422 }