diff options
Diffstat (limited to 'webapp/src/app/pages/sdks')
10 files changed, 466 insertions, 35 deletions
diff --git a/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.html b/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.html index 0c2787c..2edc0d3 100644 --- a/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.html +++ b/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.html @@ -3,10 +3,10 @@ <div class="row"> <div class="col-12 col-md-8"> - {{ labelGet(sdk) }} + {{ sdk.name }} </div> <div class="col-6 col-md-4 text-right" role="group"> - <button class="btn btn-outline-danger btn-tn btn-xds" (click)="delete(sdk)"> + <button class="btn btn-outline-danger btn-tn btn-xds" (click)="remove(sdk)"> <span class="fa fa-trash fa-size-x2"></span> </button> </div> @@ -24,19 +24,24 @@ <td>{{ sdk.id }}</td> </tr> <tr> + <th><span class="fa fa-fw fa-file-text-o"></span> <span>Description</span></th> + <td>{{ sdk.description }}</td> + </tr> + <tr> <th><span class="fa fa-fw fa-user"></span> <span>Profile</span></th> <td>{{ sdk.profile }}</td> - </tr> <tr> - <th><span class="fa fa-fw fa-tasks"></span> <span>Architecture</span></th> - <td>{{ sdk.arch }}</td> </tr> <tr> - <th><span class="fa fa-fw fa-code-fork"></span> <span>Version</span></th> - <td>{{ sdk.version }}</td> + <th><span class="fa fa-fw fa-tasks"></span> <span>Architecture</span></th> + <td>{{ sdk.arch }}</td> + </tr> + <tr> + <th><span class="fa fa-fw fa-code-fork"></span> <span>Version</span></th> + <td>{{ sdk.version }}</td> </tr> <tr> - <th><span class="fa fa-fw fa-folder-open-o"></span> <span>Sdk path</span></th> - <td>{{ sdk.path}}</td> + <th><span class="fa fa-fw fa-folder-open-o"></span> <span>Sdk path</span></th> + <td>{{ sdk.path}}</td> </tr> </tbody> </table> diff --git a/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.ts b/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.ts index d41e2fb..997f01d 100644 --- a/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.ts +++ b/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.ts @@ -20,6 +20,8 @@ import { Component, Input, Pipe, PipeTransform } from '@angular/core'; import { SdkService, ISdk } from '../../../@core-xds/services/sdk.service'; import { AlertService } from '../../../@core-xds/services/alert.service'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ConfirmModalComponent, EType } from '../../confirm/confirm-modal/confirm-modal.component'; @Component({ selector: 'xds-sdk-card', @@ -36,18 +38,32 @@ export class SdkCardComponent { constructor( private alert: AlertService, private sdkSvr: SdkService, + private modalService: NgbModal, ) { } - labelGet(sdk: ISdk) { - return sdk.profile + '-' + sdk.arch + '-' + sdk.version; - } + remove(sdk: ISdk) { + const modal = this.modalService.open(ConfirmModalComponent, { + size: 'lg', + backdrop: 'static', + container: 'nb-layout', + }); + modal.componentInstance.title = 'Confirm SDK deletion'; + modal.componentInstance.type = EType.YesNo; + modal.componentInstance.question = ` + Do you <b>permanently remove '` + sdk.name + `'</b> SDK ? + <br><br> + <i><small>(SDK ID: ` + sdk.id + ` )</small></i>`; - delete(sdk: ISdk) { - this.sdkSvr.delete(sdk).subscribe( - res => { }, - err => this.alert.error('ERROR delete: ' + err), - ); + modal.result + .then(res => { + if (res === 'yes') { + this.sdkSvr.remove(sdk).subscribe( + r => { }, + err => this.alert.error(err), + ); + } + }); } } diff --git a/webapp/src/app/pages/sdks/sdk-management/sdk-install.component.ts b/webapp/src/app/pages/sdks/sdk-management/sdk-install.component.ts new file mode 100644 index 0000000..c9c518f --- /dev/null +++ b/webapp/src/app/pages/sdks/sdk-management/sdk-install.component.ts @@ -0,0 +1,131 @@ +/** +* @license +* Copyright (C) 2017 "IoT.bzh" +* Author Sebastien Douheret <sebastien@iot.bzh> +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { Component, OnInit, Input, ViewChild, AfterViewChecked, ElementRef } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { AlertService } from '../../../@core-xds/services/alert.service'; +import { SdkService, ISdk } from '../../../@core-xds/services/sdk.service'; + +@Component({ + selector: 'xds-sdk-install-modal', + template: ` + <div tabindex="-1"> + <div class="modal-header"> + SDK installation + </div> + + <div class="modal-body row"> + <div class="col-12 text-center"> + Installation of <b> {{ sdk?.name }} '</b> <span [innerHTML]="instStatus"></span> + </div> + <br> + <br> + <div class="col-12 text-center"> + <textarea rows="20" class="textarea-scroll" #scrollOutput [innerHtml]="installOutput"></textarea> + </div> + <div class="col-12 text-center"> + <button type="button" class="btn" tabindex="1" + [ngClass]="(btnName=='Cancel')?'btn-default':'btn-primary'" + (click)="onBtnClick()">{{ btnName }}</button> + </div> + </div> + + <!-- <div *ngIf="footer!=''" class="modal-footer"> + <div class="col-12 text-center"> + </div> + </div> --> + </div> + `, + styles: [` + .btn { + margin-top: 2em; + min-width: 10em; + } + .textarea-scroll { + font-family: monospace; + width: 100%; + overflow-y: scroll; + `], +}) + +export class SdkInstallComponent implements OnInit { + @Input() sdk; + @ViewChild('scrollOutput') private scrollContainer: ElementRef; + + constructor( + private modalRef: NgbActiveModal, + private sanitizer: DomSanitizer, + private alert: AlertService, + private sdkSvr: SdkService, + ) { } + + onInstallSub: any; + installOutput = ''; + btnName = 'Cancel'; + instStatus = ''; + + ngOnInit() { + this.instStatus = 'in progress...'; + + this.onInstallSub = this.sdkSvr.onInstall().subscribe(ev => { + if (ev.exited) { + this.btnName = 'OK'; + this.instStatus = '<font color="green"> Done. </font>'; + + if (ev.code === 0) { + this.alert.info('SDK ' + ev.sdk.name + ' successfully installed.'); + + } else { + if (ev.sdk.lastError !== '') { + this.alert.error(ev.sdk.lastError); + } else { + this.alert.error('SDK ' + ev.sdk.name + ' installation failed. ' + ev.error); + } + } + + } else { + if (ev.stdout !== '') { + this.installOutput += ev.stdout; + } + if (ev.stderr !== '') { + this.installOutput += ev.stderr; + } + this._scrollToBottom(); + } + }); + } + + onBtnClick(): void { + this.onInstallSub.unsubscribe(); + if (this.btnName === 'Cancel') { + this.btnName = 'OK'; + this.instStatus = '<b><font color="red"> ABORTED </font></b>'; + this.sdkSvr.abortInstall(this.sdk).subscribe(r => { }, err => this.alert.error(err)); + } else { + this.modalRef.close(); + } + } + + private _scrollToBottom(): void { + try { + this.scrollContainer.nativeElement.scrollTop = this.scrollContainer.nativeElement.scrollHeight; + } catch (err) { } + } +} diff --git a/webapp/src/app/pages/sdks/sdk-management/sdk-management.component.html b/webapp/src/app/pages/sdks/sdk-management/sdk-management.component.html new file mode 100644 index 0000000..0587194 --- /dev/null +++ b/webapp/src/app/pages/sdks/sdk-management/sdk-management.component.html @@ -0,0 +1,26 @@ +<div class="row"> + <div class="col-12"> + <nb-card-body> + <div class="col-9"> + <nb-actions size="medium"> + <nb-action> + <a href="#/pages/sdks" class="btn" role="Button"> + <i id="hack-i" class="fa fa-list-alt"></i> + <span id="hack-span">Switch to basic SDKs view</span> + </a> + </nb-action> + </nb-actions> + </div> + </nb-card-body> + </div> + + <nb-card> + <nb-card-header> + <span>SDKs Management</span> + </nb-card-header> + + <nb-card-body> + <ng2-smart-table [settings]="settings" [source]="sdks" (custom)="onCustom($event)"></ng2-smart-table> + </nb-card-body> + </nb-card> +</div> diff --git a/webapp/src/app/pages/sdks/sdk-management/sdk-management.component.scss b/webapp/src/app/pages/sdks/sdk-management/sdk-management.component.scss new file mode 100644 index 0000000..0ba2741 --- /dev/null +++ b/webapp/src/app/pages/sdks/sdk-management/sdk-management.component.scss @@ -0,0 +1,83 @@ +@import '../../../@theme/styles/themes'; +@import '~@nebular/theme/components/card/card.component.theme'; +// FIXME SEB: remove this ugly hack and use nb-theme +#hack-i { + color: #a4abb3; // nb-theme(color-fg); + font-size: 1.5rem; + margin-right: 1rem; +} + +#hack-span { + font-family: nb-theme(font-secondary); + font-weight: nb-theme(font-weight-bold); + color: #2a2a2a; // nb-theme(color-fg-heading); + text-transform: uppercase; +} + +@include nb-install-component() { + nb-card-body { + display: flex; + align-items: center; + } + .action-groups-header { + flex-basis: 20%; + color: nb-theme(card-header-fg-heading); + font-family: nb-theme(card-header-font-family); + font-size: nb-theme(card-header-font-size); + font-weight: nb-theme(card-header-font-weight); + } + .nb-actions { + flex-basis: 80%; + } + .right { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + order: 1; + flex-direction: row-reverse; + } + nb-actions > nb-action { + padding: 0; + } + nb-action { + i { + color: nb-theme(color-fg); + font-size: 1.5rem; + margin-right: 1rem; + } + span { + font-family: nb-theme(font-secondary); + font-weight: nb-theme(font-weight-bold); + color: nb-theme(color-fg-heading); + text-transform: uppercase; + } + button { + margin: 0 auto; + padding: 0; + cursor: pointer; + border: none; + background: none; + display: flex; + align-items: center; + &:focus { + box-shadow: none; + outline: none; + } + } + } +} + +td.ng2-smart-actions { + height: auto !important; +} + +nav.ng2-smart-pagination-nav { + margin-left: auto; + margin-right: auto; +} + +.ng2-smart-pagination .page-link.page-link-prev, +.page-link.page-link-next { + font-size: 1em !important; +} diff --git a/webapp/src/app/pages/sdks/sdk-management/sdk-management.component.ts b/webapp/src/app/pages/sdks/sdk-management/sdk-management.component.ts new file mode 100644 index 0000000..c885238 --- /dev/null +++ b/webapp/src/app/pages/sdks/sdk-management/sdk-management.component.ts @@ -0,0 +1,168 @@ +/** +* @license +* Copyright (C) 2017 "IoT.bzh" +* Author Sebastien Douheret <sebastien@iot.bzh> +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { Component, ViewEncapsulation, OnInit, isDevMode } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { LocalDataSource } from 'ng2-smart-table'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ConfirmModalComponent, EType } from '../../confirm/confirm-modal/confirm-modal.component'; +import { SdkInstallComponent } from './sdk-install.component'; + +import { AlertService } from '../../../@core-xds/services/alert.service'; +import { SdkService, ISdk } from '../../../@core-xds/services/sdk.service'; +import { ISdkMessage } from '../../../@core-xds/services/xdsagent.service'; + +interface ISdkMgt extends ISdk { + link: string; + selected: boolean; +} + +/* + * FIXME / TODO: + * - support install of multi SDKs (see settings.selectMode: 'multi') + * - add Uninstall button (use delete) + * - add (mouseover) to display description, date, size, ... + */ + +@Component({ + selector: 'xds-sdk-management', + templateUrl: 'sdk-management.component.html', + styleUrls: ['sdk-management.component.scss'], + encapsulation: ViewEncapsulation.None, +}) + +export class SdkManagementComponent implements OnInit { + + sdks$: Observable<ISdk[]>; + sdks: ISdkMgt[]; + source: LocalDataSource = new LocalDataSource(); + + settings = { + mode: 'external', + actions: { + add: false, + edit: false, + delete: false, // TODO, add delete == uninstall + custom: [ + { name: 'install', title: '<i class="nb-plus"></i>' }, + ], + }, + delete: { + deleteButtonContent: '<i class="nb-trash"></i>', + confirmDelete: true, + }, + columns: { + name: { title: 'Name', editable: false }, + profile: { title: 'Profile', editable: false, filter: {} }, + arch: { title: 'Architecture', editable: false, filter: {} }, + version: { title: 'Version', editable: false }, + // TODO: add status when delete supported: + // status: { title: 'Status', editable: false }, + link: { title: 'Link', editable: false, type: 'html', filter: false, width: '2%' }, + }, + }; + + constructor( + private alert: AlertService, + private sdkSvr: SdkService, + private modalService: NgbModal, + ) { } + + ngOnInit() { + this.sdkSvr.Sdks$.subscribe(sdks => { + const profMap = {}; + const archMap = {}; + this.sdks = []; + sdks.forEach(s => { + // only display not installed SDK + if (s.status !== 'Not Installed') { + return; + } + profMap[s.profile] = s.profile; + archMap[s.arch] = s.arch; + + const sm = <ISdkMgt>s; + sm.selected = false; + if (s.url !== '') { + sm.link = '<a href="' + s.url.substr(0, s.url.lastIndexOf('/')) + '" target="_blank" class="fa fa-external-link"></a>'; + } + this.sdks.push(sm); + + }); + + // Add text box filter for Profile and Arch columns + const profList = []; Object.keys(profMap).forEach(a => profList.push({ value: a, title: a })); + this.settings.columns.profile.filter = { + type: 'list', + config: { selectText: 'Select...', list: profList }, + }; + + const archList = []; Object.keys(archMap).forEach(a => archList.push({ value: a, title: a })); + this.settings.columns.arch.filter = { + type: 'list', + config: { selectText: 'Select...', list: archList }, + }; + + // update sources + this.source.load(this.sdks); + + }); + } + + onCustom(event): void { + if (event.action === 'install') { + const sdk = <ISdkMgt>event.data; + const modal = this.modalService.open(ConfirmModalComponent, { + size: 'lg', + backdrop: 'static', + container: 'nb-layout', + }); + modal.componentInstance.title = 'Confirm SDK installation'; + modal.componentInstance.type = EType.YesNo; + modal.componentInstance.question = ` + Please confirm installation of <b>` + sdk.name + `'</b> SDK ?<br> + <br> + <i><small>(size: ` + sdk.size + `, date: ` + sdk.date + `)</small></i>`; + + modal.result.then(res => { + if (res === 'yes') { + // Request installation + this.sdkSvr.install(sdk).subscribe(r => { }, err => this.alert.error(err)); + + const modalInstall = this.modalService.open(SdkInstallComponent, { + size: 'lg', + backdrop: 'static', + container: 'nb-layout', + }); + modalInstall.componentInstance.sdk = sdk; + } + }); + + + } else if (event.action === 'uninstall') { + // TODO + + } else { + /* tslint:disable:no-console */ + if (isDevMode) { + console.error('onCustom: unknown event action: ', event); + } + } + } + +} diff --git a/webapp/src/app/pages/sdks/sdks.component.html b/webapp/src/app/pages/sdks/sdks.component.html index 94c2501..9f9204f 100644 --- a/webapp/src/app/pages/sdks/sdks.component.html +++ b/webapp/src/app/pages/sdks/sdks.component.html @@ -4,10 +4,10 @@ <div class="col-9"> <nb-actions size="medium"> <nb-action> - <button (click)="add()"> - <i class="nb-plus"></i> - <span>Add new SDK</span> - </button> + <a href="#/pages/sdks/management" class="btn" role="Button"> + <i class="fa fa-wrench"></i> + <span>SDKs Management</span> + </a> </nb-action> </nb-actions> </div> @@ -20,7 +20,10 @@ </div> </nb-card-body> </div> - <div class="col-md-6 col-lg-6" *ngFor="let sdk of (sdks$ | async)"> - <xds-sdk-card [sdk]="sdk"></xds-sdk-card> - </div> + + <ng-container *ngFor="let sdk of (sdks$ | async)"> + <div class="col-md-6 col-lg-6" *ngIf="isVisible(sdk)"> + <xds-sdk-card [sdk]="sdk"></xds-sdk-card> + </div> + </ng-container> </div> diff --git a/webapp/src/app/pages/sdks/sdks.component.scss b/webapp/src/app/pages/sdks/sdks.component.scss index 92a8807..a125e8d 100644 --- a/webapp/src/app/pages/sdks/sdks.component.scss +++ b/webapp/src/app/pages/sdks/sdks.component.scss @@ -31,7 +31,7 @@ nb-action { i { color: nb-theme(color-fg); - font-size: 2.5rem; + font-size: 1.5rem; margin-right: 1rem; } span { diff --git a/webapp/src/app/pages/sdks/sdks.component.ts b/webapp/src/app/pages/sdks/sdks.component.ts index f9e31a4..12d2f71 100644 --- a/webapp/src/app/pages/sdks/sdks.component.ts +++ b/webapp/src/app/pages/sdks/sdks.component.ts @@ -20,7 +20,6 @@ import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -// import { SdkAddModalComponent } from './sdk-add-modal/sdk-add-modal.component'; import { SdkService, ISdk } from '../../@core-xds/services/sdk.service'; @@ -44,10 +43,7 @@ export class SdksComponent implements OnInit { this.sdks$ = this.sdkSvr.Sdks$; } - add() { - /* SEB TODO - const activeModal = this.modalService.open(SdkAddModalComponent, { size: 'lg', container: 'nb-layout' }); - activeModal.componentInstance.modalHeader = 'Large Modal'; - */ + isVisible(sdk: ISdk) { + return sdk.status === 'Installed' || sdk.status === 'Installing'; } } diff --git a/webapp/src/app/pages/sdks/sdks.module.ts b/webapp/src/app/pages/sdks/sdks.module.ts index d717d86..17ede0e 100644 --- a/webapp/src/app/pages/sdks/sdks.module.ts +++ b/webapp/src/app/pages/sdks/sdks.module.ts @@ -21,20 +21,23 @@ import { ThemeModule } from '../../@theme/theme.module'; import { SdksComponent } from './sdks.component'; import { SdkCardComponent } from './sdk-card/sdk-card.component'; -// import { SdkAddModalComponent } from './sdk-add-modal/sdk-add-modal.component'; - +import { SdkManagementComponent } from './sdk-management/sdk-management.component'; +import { SdkInstallComponent } from './sdk-management/sdk-install.component'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; @NgModule({ imports: [ ThemeModule, + Ng2SmartTableModule, ], declarations: [ SdksComponent, SdkCardComponent, - // SdkAddModalComponent, + SdkManagementComponent, + SdkInstallComponent, ], entryComponents: [ - // SdkAddModalComponent, + SdkInstallComponent, ], }) export class SdksModule { } |