Moved project creation in a modal windows
authorSebastien Douheret <sebastien.douheret@iot.bzh>
Wed, 16 Aug 2017 12:24:49 +0000 (14:24 +0200)
committerSebastien Douheret <sebastien.douheret@iot.bzh>
Wed, 16 Aug 2017 12:27:40 +0000 (14:27 +0200)
Signed-off-by: Sebastien Douheret <sebastien.douheret@iot.bzh>
16 files changed:
.vscode/settings.json
lib/folder/folder-pathmap.go
lib/model/folders.go
webapp/src/app/app.module.ts
webapp/src/app/config/config.component.css
webapp/src/app/config/config.component.html
webapp/src/app/config/config.component.ts
webapp/src/app/projects/projectAddModal.component.css [new file with mode: 0644]
webapp/src/app/projects/projectAddModal.component.html [new file with mode: 0644]
webapp/src/app/projects/projectAddModal.component.ts [new file with mode: 0644]
webapp/src/app/projects/projectCard.component.ts
webapp/src/app/sdks/sdkAddModal.component.html [new file with mode: 0644]
webapp/src/app/sdks/sdkAddModal.component.ts [new file with mode: 0644]
webapp/src/app/services/alert.service.ts
webapp/src/app/services/config.service.ts
webapp/src/systemjs.config.js

index 60fab57..7ccd637 100644 (file)
@@ -12,7 +12,6 @@
         "webapp/dist": true,
         "webapp/node_modules": true
     },
-
     // Specify paths/files to ignore. (Supports Globs)
     "cSpell.ignorePaths": [
         "**/node_modules/**",
index 8711df2..2ad8a93 100644 (file)
@@ -7,18 +7,22 @@ import (
        "path/filepath"
 
        common "github.com/iotbzh/xds-common/golib"
+       "github.com/iotbzh/xds-server/lib/xdsconfig"
 )
 
 // IFOLDER interface implementation for native/path mapping folders
 
 // PathMap .
 type PathMap struct {
-       config FolderConfig
+       globalConfig *xdsconfig.Config
+       config       FolderConfig
 }
 
 // NewFolderPathMap Create a new instance of PathMap
-func NewFolderPathMap() *PathMap {
-       f := PathMap{}
+func NewFolderPathMap(gc *xdsconfig.Config) *PathMap {
+       f := PathMap{
+               globalConfig: gc,
+       }
        return &f
 }
 
@@ -28,8 +32,13 @@ func (f *PathMap) Add(cfg FolderConfig) (*FolderConfig, error) {
                return nil, fmt.Errorf("ServerPath must be set")
        }
 
-       // Sanity check
+       // Use shareRootDir if ServerPath is a relative path
        dir := cfg.DataPathMap.ServerPath
+       if !filepath.IsAbs(dir) {
+               dir = filepath.Join(f.globalConfig.FileConf.ShareRootDir, dir)
+       }
+
+       // Sanity check
        if !common.Exists(dir) {
                // try to create if not existing
                if err := os.MkdirAll(dir, 0755); err != nil {
@@ -52,7 +61,8 @@ func (f *PathMap) Add(cfg FolderConfig) (*FolderConfig, error) {
        }
 
        f.config = cfg
-       f.config.RootPath = cfg.DataPathMap.ServerPath
+       f.config.RootPath = dir
+       f.config.DataPathMap.ServerPath = dir
        f.config.Status = StatusEnable
 
        return &f.config, nil
index 3c2457c..02c3254 100644 (file)
@@ -208,7 +208,7 @@ func (f *Folders) createUpdate(newF folder.FolderConfig, create bool) (*folder.F
                fld = f.SThg.NewFolderST(f.Conf)
        // PATH MAP
        case folder.TypePathMap:
-               fld = folder.NewFolderPathMap()
+               fld = folder.NewFolderPathMap(f.Conf)
        default:
                return nil, fmt.Errorf("Unsupported folder type")
        }
index 4877f6e..10ff7a4 100644 (file)
@@ -10,6 +10,7 @@ import { ModalModule } from 'ngx-bootstrap/modal';
 import { AccordionModule } from 'ngx-bootstrap/accordion';
 import { CarouselModule } from 'ngx-bootstrap/carousel';
 import { PopoverModule } from 'ngx-bootstrap/popover';
+import { CollapseModule } from 'ngx-bootstrap/collapse';
 import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
 
 // Import the application components and services.
@@ -21,9 +22,11 @@ import { DlXdsAgentComponent, CapitalizePipe } from "./config/downloadXdsAgent.c
 import { ProjectCardComponent } from "./projects/projectCard.component";
 import { ProjectReadableTypePipe } from "./projects/projectCard.component";
 import { ProjectsListAccordionComponent } from "./projects/projectsListAccordion.component";
+import { ProjectAddModalComponent} from "./projects/projectAddModal.component";
 import { SdkCardComponent } from "./sdks/sdkCard.component";
 import { SdksListAccordionComponent } from "./sdks/sdksListAccordion.component";
 import { SdkSelectDropdownComponent } from "./sdks/sdkSelectDropdown.component";
+import { SdkAddModalComponent} from "./sdks/sdkAddModal.component";
 
 import { HomeComponent } from "./home/home.component";
 import { DevelComponent } from "./devel/devel.component";
@@ -52,6 +55,7 @@ import { SdkService } from "./services/sdk.service";
         AccordionModule.forRoot(),
         CarouselModule.forRoot(),
         PopoverModule.forRoot(),
+        CollapseModule.forRoot(),
         BsDropdownModule.forRoot(),
     ],
     declarations: [
@@ -67,9 +71,11 @@ import { SdkService } from "./services/sdk.service";
         ProjectCardComponent,
         ProjectReadableTypePipe,
         ProjectsListAccordionComponent,
+        ProjectAddModalComponent,
         SdkCardComponent,
         SdksListAccordionComponent,
         SdkSelectDropdownComponent,
+        SdkAddModalComponent,
     ],
     providers: [
         AppRoutingProviders,
@@ -88,4 +94,4 @@ import { SdkService } from "./services/sdk.service";
     bootstrap: [AppComponent]
 })
 export class AppModule {
-}
\ No newline at end of file
+}
index f480857..208ce6f 100644 (file)
@@ -23,4 +23,4 @@ tr.info>th {
 
 tr.info>td {
     vertical-align: middle;
-}
\ No newline at end of file
+}
index 5211c2d..6af7f0d 100644 (file)
@@ -1,11 +1,18 @@
 <div class="panel panel-default">
-    <div class="panel-heading clearfix">
-        <h2 class="panel-title pull-left">Global Configuration</h2>
-        <div class="pull-right">
-            <span class="fa fa-fw fa-exchange fa-size-x2" [style.color]="((serverStatus$ | async)?.WS_connected)?'green':'red'"></span>
-        </div>
+    <div class="panel-heading">
+        <h2 class="panel-title" (click)="gConfigIsCollapsed = !gConfigIsCollapsed">
+            Global Configuration
+            <div class="pull-right">
+                <span class="fa fa-fw fa-exchange fa-size-x2" [style.color]="((serverStatus$ | async)?.WS_connected)?'green':'red'"></span>
+
+                <button class="btn btn-link" (click)="gConfigIsCollapsed = !gConfigIsCollapsed; $event.stopPropagation()">
+                    <span *ngIf="gConfigIsCollapsed" class="fa fa-angle-double-right fa-size-x2"></span>
+                    <span *ngIf="!gConfigIsCollapsed" class="fa fa-angle-double-down fa-size-x2"></span>
+                </button>
+            </div>
+        </h2>
     </div>
-    <div class="panel-body">
+    <div class="panel-body" [collapse]="gConfigIsCollapsed">
         <div class="row">
             <div class="col-xs-12">
                 <table class="table table-condensed">
 
 <div class="panel panel-default">
     <div class="panel-heading">
-        <h2 class="panel-title">Cross SDKs Configuration</h2>
+        <h2 class="panel-title" (click)="sdksIsCollapsed = !sdksIsCollapsed">
+            Cross SDKs
+            <div class="pull-right">
+                <button class="btn btn-link" (click)="childSdkModal.show(); $event.stopPropagation()"><span class="fa fa-plus fa-size-x2"></span></button>
+
+                <button class="btn btn-link" (click)="sdksIsCollapsed = !sdksIsCollapsed; $event.stopPropagation()">
+                    <span *ngIf="sdksIsCollapsed" class="fa fa-angle-double-right fa-size-x2"></span>
+                    <span *ngIf="!sdksIsCollapsed" class="fa fa-angle-double-down fa-size-x2"></span>
+                </button>
+            </div>
+        </h2>
     </div>
-    <div class="panel-body">
+    <div class="panel-body" [collapse]="sdksIsCollapsed">
         <div class="row col-xs-12">
             <sdks-list-accordion [sdks]="(sdks$ | async)"></sdks-list-accordion>
         </div>
 
 <div class="panel panel-default">
     <div class="panel-heading">
-        <h2 class="panel-title">Projects Configuration</h2>
-    </div>
-    <div class="panel-body">
-        <form [formGroup]="addProjectForm" (ngSubmit)="onSubmit()">
-            <div class="row ">
-                <div class="col-xs-2">
-                    <button class="btn btn-primary" type="submit" [disabled]="!addProjectForm.valid"><i class="fa fa-plus"></i>&nbsp;Add Folder</button>
-                </div>
+        <h2 class="panel-title" (click)="projectsIsCollapsed = !projectsIsCollapsed; $event.stopPropagation()">
+            Projects
+            <div class="pull-right">
+                <button class="btn btn-link" (click)="childProjectModal.show(); $event.stopPropagation()"><span class="fa fa-plus fa-size-x2"></span></button>
 
-                <div class="col-xs-6">
-                    <label>Client/Local Path </label>
-                    <input type="text" style="width:70%;" formControlName="pathCli" placeholder="myProject">
-                </div>
-                <div class="col-xs-6">
-                    <label>Server Path </label>
-                    <input type="text" style="width:70%;" formControlName="pathSvr" placeholder="myProject">
-                </div>
-                <div class="col-xs-4">
-                    <label>Label </label>
-                    <input type="text" formControlName="label" (keyup)="onKeyLabel($event)">
-                </div>
-                <div class="col-xs-4">
-                    <label>Type </label>
-                    <select class="form-control" formControlName="type">
-                        <option *ngFor="let t of projectTypes" [value]="t.value">{{t.display}}
-                        </option>
-                    </select>
-                </div>
+                <button class="btn btn-link" (click)="projectsIsCollapsed = !projectsIsCollapsed; $event.stopPropagation()">
+                        <span *ngIf="projectsIsCollapsed" class="fa fa-angle-double-right fa-size-x2"></span>
+                        <span *ngIf="!projectsIsCollapsed" class="fa fa-angle-double-down fa-size-x2"></span>
+                </button>
             </div>
-        </form>
-
+        </h2>
+    </div>
+    <div class="panel-body" [collapse]="projectsIsCollapsed">
         <div class="row col-xs-12">
             <projects-list-accordion [projects]="(config$ | async).projects"></projects-list-accordion>
         </div>
     </div>
 </div>
 
+<!-- Modals -->
+<project-add-modal #childProjectModal [title]="'Add a new project'">
+</project-add-modal>
+<sdk-add-modal  #childSdkModal [title]="'Add a new SDK'">
+</sdk-add-modal>
 
 <!-- only for debug -->
 <div *ngIf="false" class="row">
index 0df707b..b107e81 100644 (file)
@@ -1,19 +1,16 @@
-import { Component, OnInit } from "@angular/core";
+import { Component, ViewChild, OnInit } from "@angular/core";
 import { Observable } from 'rxjs/Observable';
 import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms';
+import { CollapseModule } from 'ngx-bootstrap/collapse';
 
-// Import RxJs required methods
-import 'rxjs/add/operator/map';
-import 'rxjs/add/operator/filter';
-import 'rxjs/add/operator/debounceTime';
-
-import { ConfigService, IConfig, IProject, ProjectType, ProjectTypes,
-    IxdsAgentPackage } from "../services/config.service";
+import { ConfigService, IConfig, IxdsAgentPackage } from "../services/config.service";
 import { XDSServerService, IServerStatus, IXDSAgentInfo } from "../services/xdsserver.service";
 import { XDSAgentService, IAgentStatus } from "../services/xdsagent.service";
 import { SyncthingService, ISyncThingStatus } from "../services/syncthing.service";
 import { AlertService } from "../services/alert.service";
 import { ISdk, SdkService } from "../services/sdk.service";
+import { ProjectAddModalComponent } from "../projects/projectAddModal.component";
+import { SdkAddModalComponent } from "../sdks/sdkAddModal.component";
 
 @Component({
     templateUrl: './app/config/config.component.html',
@@ -24,6 +21,8 @@ import { ISdk, SdkService } from "../services/sdk.service";
 // and from http://plnkr.co/edit/vCdjZM?p=preview
 
 export class ConfigComponent implements OnInit {
+    @ViewChild('childProjectModal') childProjectModal: ProjectAddModalComponent;
+    @ViewChild('childSdkModal') childSdkModal: SdkAddModalComponent;
 
     config$: Observable<IConfig>;
     sdks$: Observable<ISdk[]>;
@@ -34,22 +33,21 @@ export class ConfigComponent implements OnInit {
     curProj: number;
     userEditedLabel: boolean = false;
     xdsAgentPackages: IxdsAgentPackage[] = [];
-    projectTypes = ProjectTypes;
+
+    gConfigIsCollapsed: boolean = true;
+    sdksIsCollapsed: boolean = true;
+    projectsIsCollapsed: boolean = false;
 
     // TODO replace by reactive FormControl + add validation
     syncToolUrl: string;
     xdsAgentUrl: string;
     xdsAgentRetry: string;
-    projectsRootDir: string;
+    projectsRootDir: string;    // FIXME: should be remove when projectAddModal will always return full path
     showApplyBtn = {    // Used to show/hide Apply buttons
         "retry": false,
         "rootDir": false,
     };
 
-    addProjectForm: FormGroup;
-    pathCliCtrl = new FormControl("", Validators.required);
-    pathSvrCtrl = new FormControl("", Validators.required);
-
     constructor(
         private configSvr: ConfigService,
         private xdsServerSvr: XDSServerService,
@@ -57,19 +55,7 @@ export class ConfigComponent implements OnInit {
         private stSvr: SyncthingService,
         private sdkSvr: SdkService,
         private alert: AlertService,
-        private fb: FormBuilder
     ) {
-        // Define types (first one is special/placeholder)
-        this.projectTypes.unshift({value: -1, display: "--Select a type--"});
-        let selectedType = this.projectTypes[0].value;
-
-        this.curProj = 0;
-        this.addProjectForm = fb.group({
-            pathCli: this.pathCliCtrl,
-            pathSvr: this.pathSvrCtrl,
-            label: ["", Validators.nullValidator],
-            type: [selectedType, Validators.pattern("[0-9]+")],
-        });
     }
 
     ngOnInit() {
@@ -88,23 +74,6 @@ export class ConfigComponent implements OnInit {
             this.xdsAgentPackages = cfg.xdsAgentPackages;
         });
 
-        // Auto create label name
-        this.pathCliCtrl.valueChanges
-            .debounceTime(100)
-            .filter(n => n)
-            .map(n => "Project_" + n.split('/')[0])
-            .subscribe(value => {
-                if (value && !this.userEditedLabel) {
-                    this.addProjectForm.patchValue({ label: value });
-                }
-            });
-
-        // Select 1 first type by default
-        // SEB this.typeCtrl.setValue({type: ProjectTypes[0].value});
-    }
-
-    onKeyLabel(event: any) {
-        this.userEditedLabel = (this.addProjectForm.value.label !== "");
     }
 
     submitGlobConf(field: string) {
@@ -134,18 +103,4 @@ export class ConfigComponent implements OnInit {
         this.configSvr.loadProjects();
     }
 
-    onSubmit() {
-        let formVal = this.addProjectForm.value;
-
-        let type = formVal['type'].value;
-        let numType = Number(formVal['type']);
-        this.configSvr.addProject({
-            label: formVal['label'],
-            pathClient: formVal['pathCli'],
-            pathServer: formVal['pathSvr'],
-            type: numType,
-            // FIXME: allow to set defaultSdkID from New Project config panel
-        });
-    }
-
 }
diff --git a/webapp/src/app/projects/projectAddModal.component.css b/webapp/src/app/projects/projectAddModal.component.css
new file mode 100644 (file)
index 0000000..77f73a5
--- /dev/null
@@ -0,0 +1,24 @@
+.table-borderless>tbody>tr>td,
+.table-borderless>tbody>tr>th,
+.table-borderless>tfoot>tr>td,
+.table-borderless>tfoot>tr>th,
+.table-borderless>thead>tr>td,
+.table-borderless>thead>tr>th {
+    border: none;
+}
+
+tr>th {
+    vertical-align: middle;
+}
+
+tr>td {
+    vertical-align: middle;
+}
+
+th label {
+    margin-bottom: 0;
+}
+
+td input {
+    width: 100%;
+}
diff --git a/webapp/src/app/projects/projectAddModal.component.html b/webapp/src/app/projects/projectAddModal.component.html
new file mode 100644 (file)
index 0000000..dc84985
--- /dev/null
@@ -0,0 +1,54 @@
+<div bsModal #childProjectModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel"
+    [config]="{backdrop: 'static'}" aria-hidden="true">
+    <div class="modal-dialog modal-lg">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h4 class="modal-title pull-left">{{title}}</h4>
+                <button type="button" class="close pull-right" aria-label="Close" (click)="hide()">
+                        <span aria-hidden="true">&times;</span>
+                    </button>
+            </div>
+
+            <form [formGroup]="addProjectForm" (ngSubmit)="onSubmit()">
+                <div class="modal-body">
+                    <div class="row ">
+                        <div class="col-xs-12">
+                            <table class="table table-borderless">
+                                <tbody>
+                                    <tr>
+                                        <th><label>Sharing Type </label></th>
+                                        <td><select class="form-control" formControlName="type">
+                                            <option *ngFor="let t of projectTypes" [value]="t.value">{{t.display}}
+                                            </option>
+                                        </select>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <th><label for="select-local-path">Local Path </label></th>
+                                        <td><input type="text" id="select-local-path" formControlName="pathCli" placeholder="/tmp/myProject" (change)="onChangeLocalProject($event)"></td>
+                                    </tr>
+                                    <tr>
+                                        <th><label for="select-server-path">Server Path </label></th>
+                                        <td><input type="text" id="select-server-path" formControlName="pathSvr"></td>
+                                    </tr>
+                                    <tr>
+                                        <th><label for="select-label">Label </label></th>
+                                        <td><input type="text" formControlName="label" id="select-label" (keyup)="onKeyLabel($event)"></td>
+                                    </tr>
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <div class="pull-left">
+                        <button class="btn btn-default" (click)="cancelAction=true; hide()"> Cancel </button>
+                    </div>
+                    <div class="">
+                        <button class="btn btn-primary" type="submit" [disabled]="!addProjectForm.valid">Add Folder</button>
+                    </div>
+                </div>
+            </form>
+        </div>
+    </div>
+</div>
diff --git a/webapp/src/app/projects/projectAddModal.component.ts b/webapp/src/app/projects/projectAddModal.component.ts
new file mode 100644 (file)
index 0000000..47e9c89
--- /dev/null
@@ -0,0 +1,142 @@
+import { Component, Input, ViewChild, OnInit } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { ModalDirective } from 'ngx-bootstrap/modal';
+import { FormControl, FormGroup, Validators, FormBuilder, ValidatorFn, AbstractControl } from '@angular/forms';
+
+// Import RxJs required methods
+import 'rxjs/add/operator/map';
+import 'rxjs/add/operator/filter';
+import 'rxjs/add/operator/debounceTime';
+
+import { AlertService, IAlert } from "../services/alert.service";
+import {
+    ConfigService, IConfig, IProject, ProjectType, ProjectTypes,
+    IxdsAgentPackage
+} from "../services/config.service";
+
+
+@Component({
+    selector: 'project-add-modal',
+    templateUrl: './app/projects/projectAddModal.component.html',
+    styleUrls: ['./app/projects/projectAddModal.component.css']
+})
+export class ProjectAddModalComponent {
+    @ViewChild('childProjectModal') public childProjectModal: ModalDirective;
+    @Input() title?: string;
+
+    config$: Observable<IConfig>;
+
+    cancelAction: boolean = false;
+    userEditedLabel: boolean = false;
+    projectTypes = ProjectTypes;
+
+    addProjectForm: FormGroup;
+    typeCtrl: FormControl;
+    pathCliCtrl: FormControl;
+    pathSvrCtrl: FormControl;
+
+    constructor(
+        private alert: AlertService,
+        private configSvr: ConfigService,
+        private fb: FormBuilder
+    ) {
+        // Define types (first one is special/placeholder)
+        this.projectTypes.unshift({ value: -1, display: "--Select a type--" });
+
+        this.typeCtrl = new FormControl(this.projectTypes[0].value, Validators.pattern("[0-9]+"));
+        this.pathCliCtrl = new FormControl("", Validators.required);
+        this.pathSvrCtrl = new FormControl({ value: "", disabled: true }, [Validators.required, Validators.minLength(1)]);
+
+        this.addProjectForm = fb.group({
+            type: this.typeCtrl,
+            pathCli: this.pathCliCtrl,
+            pathSvr: this.pathSvrCtrl,
+            label: ["", Validators.nullValidator],
+        });
+    }
+
+    ngOnInit() {
+        this.config$ = this.configSvr.conf;
+
+        // Auto create label name
+        this.pathCliCtrl.valueChanges
+            .debounceTime(100)
+            .filter(n => n)
+            .map(n => "Project_" + n.split('/')[0])
+            .subscribe(value => {
+                if (value && !this.userEditedLabel) {
+                    this.addProjectForm.patchValue({ label: value });
+                }
+            });
+
+        // Handle disabling of Server path
+        this.typeCtrl.valueChanges
+            .debounceTime(500)
+            .subscribe(valType => {
+                let dis = (valType === String(ProjectType.SYNCTHING));
+                this.pathSvrCtrl.reset({ value: "", disabled: dis });
+            });
+    }
+
+    show() {
+        this.cancelAction = false;
+        this.childProjectModal.show();
+    }
+
+    hide() {
+        this.childProjectModal.hide();
+    }
+
+    onKeyLabel(event: any) {
+        this.userEditedLabel = (this.addProjectForm.value.label !== "");
+    }
+
+    /* FIXME: change input to file type
+     <td><input type="file" id="select-local-path" webkitdirectory
+     formControlName="pathCli" placeholder="myProject" (change)="onChangeLocalProject($event)"></td>
+
+    onChangeLocalProject(e) {
+        if e.target.files.length < 1 {
+            console.log('SEB NO files');
+        }
+        let dir = e.target.files[0].webkitRelativePath;
+        console.log("SEB files: " + dir);
+        let u = URL.createObjectURL(e.target.files[0]);
+    }
+    */
+    onChangeLocalProject(e) {
+    }
+
+    onSubmit() {
+        if (this.cancelAction) {
+            return;
+        }
+
+        let formVal = this.addProjectForm.value;
+
+        let type = formVal['type'].value;
+        let numType = Number(formVal['type']);
+        this.configSvr.addProject({
+            label: formVal['label'],
+            pathClient: formVal['pathCli'],
+            pathServer: formVal['pathSvr'],
+            type: numType,
+            // FIXME: allow to set defaultSdkID from New Project config panel
+        })
+            .subscribe(prj => {
+                this.alert.info("Project " + prj.label + " successfully created.");
+                this.hide();
+
+                // Reset Value for the next creation
+                this.addProjectForm.reset();
+                let selectedType = this.projectTypes[0].value;
+                this.addProjectForm.patchValue({ type: selectedType });
+
+            },
+            err => {
+                this.alert.error("Configuration ERROR: " + err, 60);
+                this.hide();
+            });
+    }
+
+}
index 23e10a6..1b89fe7 100644 (file)
@@ -1,5 +1,6 @@
 import { Component, Input, Pipe, PipeTransform } from '@angular/core';
 import { ConfigService, IProject, ProjectType } from "../services/config.service";
+import { AlertService } from "../services/alert.service";
 
 @Component({
     selector: 'project-card',
@@ -46,12 +47,19 @@ export class ProjectCardComponent {
 
     @Input() project: IProject;
 
-    constructor(private configSvr: ConfigService) {
+    constructor(
+        private alert: AlertService,
+        private configSvr: ConfigService
+    ) {
     }
 
 
     delete(prj: IProject) {
-        this.configSvr.deleteProject(prj);
+        this.configSvr.deleteProject(prj)
+            .subscribe(res => {
+            }, err => {
+                this.alert.error("Delete local ERROR: " + err);
+            });
     }
 
 }
diff --git a/webapp/src/app/sdks/sdkAddModal.component.html b/webapp/src/app/sdks/sdkAddModal.component.html
new file mode 100644 (file)
index 0000000..2c07fca
--- /dev/null
@@ -0,0 +1,23 @@
+<div bsModal #sdkChildModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel"
+    aria-hidden="true">
+    <div class="modal-dialog modal-lg">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h4 class="modal-title pull-left">{{title}}</h4>
+                <button type="button" class="close pull-right" aria-label="Close" (click)="hideChildModal()">
+                <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <ng-content select=".modal-body"> </ng-content>
+                <i>Not available for now.</i>
+            </div>
+
+            <div class="modal-footer">
+                <div class="pull-left">
+                    <button class="btn btn-default" (click)="hide()"> Cancel </button>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
diff --git a/webapp/src/app/sdks/sdkAddModal.component.ts b/webapp/src/app/sdks/sdkAddModal.component.ts
new file mode 100644 (file)
index 0000000..b6c8eb2
--- /dev/null
@@ -0,0 +1,24 @@
+import { Component, Input, ViewChild } from '@angular/core';
+import { ModalDirective } from 'ngx-bootstrap/modal';
+
+@Component({
+    selector: 'sdk-add-modal',
+    templateUrl: './app/sdks/sdkAddModal.component.html',
+})
+export class SdkAddModalComponent {
+    @ViewChild('sdkChildModal') public sdkChildModal: ModalDirective;
+
+    @Input() title?: string;
+
+    // TODO
+    constructor() {
+    }
+
+    show() {
+        this.sdkChildModal.show();
+    }
+
+    hide() {
+        this.sdkChildModal.hide();
+    }
+}
index 9dab36a..c3cae7a 100644 (file)
@@ -30,8 +30,10 @@ export class AlertService {
         this.uid = 0;
     }
 
-    public error(msg: string) {
-        this.add({ type: "danger", msg: msg, dismissible: true });
+    public error(msg: string, dismissTime?: number) {
+        this.add({
+            type: "danger", msg: msg, dismissible: true, dismissTimeout: dismissTime
+        });
     }
 
     public warning(msg: string, dismissible?: boolean) {
index c65332f..3b51768 100644 (file)
@@ -277,7 +277,7 @@ export class ConfigService {
         return id.slice(0, 15);
     }
 
-    addProject(prj: IProject) {
+    addProject(prj: IProject): Observable<IProject> {
         // Substitute tilde with to user home path
         let pathCli = prj.pathClient.trim();
         if (pathCli.charAt(0) === '~') {
@@ -304,57 +304,58 @@ export class ConfigService {
         };
         // Send config to XDS server
         let newPrj = prj;
-        this.xdsServerSvr.addProject(xdsPrj)
-            .subscribe(resStRemotePrj => {
+        return this.xdsServerSvr.addProject(xdsPrj)
+            .flatMap(resStRemotePrj => {
                 newPrj.remotePrjDef = resStRemotePrj;
                 newPrj.id = resStRemotePrj.id;
+                newPrj.pathClient = resStRemotePrj.path;
 
-                // FIXME REWORK local ST config
-                //  move logic to server side tunneling-back by WS
-                let stData = resStRemotePrj.dataCloudSync;
-
-                // Now setup local config
-                let stLocPrj: ISyncThingProject = {
-                    id: resStRemotePrj.id,
-                    label: xdsPrj.label,
-                    path: xdsPrj.path,
-                    serverSyncThingID: stData.builderSThgID
-                };
-
-                // Set local Syncthing config
-                this.stSvr.addProject(stLocPrj)
-                    .subscribe(resStLocalPrj => {
-                        newPrj.localPrjDef = resStLocalPrj;
-
-                        // FIXME: maybe reduce subject to only .project
-                        //this.confSubject.next(Object.assign({}, this.confStore).project);
-                        this.confStore.projects.push(Object.assign({}, newPrj));
-                        this.confSubject.next(Object.assign({}, this.confStore));
-                    },
-                    err => {
-                        this.alert.error("Configuration local ERROR: " + err);
-                    });
-            },
-            err => {
-                this.alert.error("Configuration remote ERROR: " + err);
+                if (newPrj.type === ProjectType.SYNCTHING) {
+                    // FIXME REWORK local ST config
+                    //  move logic to server side tunneling-back by WS
+                    let stData = resStRemotePrj.dataCloudSync;
+
+                    // Now setup local config
+                    let stLocPrj: ISyncThingProject = {
+                        id: resStRemotePrj.id,
+                        label: xdsPrj.label,
+                        path: xdsPrj.path,
+                        serverSyncThingID: stData.builderSThgID
+                    };
+
+                    // Set local Syncthing config
+                    return this.stSvr.addProject(stLocPrj);
+
+                } else {
+                    newPrj.pathServer = resStRemotePrj.dataPathMap.serverPath;
+                    return Observable.of(null);
+                }
+            })
+            .map(resStLocalPrj => {
+                newPrj.localPrjDef = resStLocalPrj;
+
+                // FIXME: maybe reduce subject to only .project
+                //this.confSubject.next(Object.assign({}, this.confStore).project);
+                this.confStore.projects.push(Object.assign({}, newPrj));
+                this.confSubject.next(Object.assign({}, this.confStore));
+
+                return newPrj;
             });
     }
 
-    deleteProject(prj: IProject) {
+    deleteProject(prj: IProject): Observable<IProject> {
         let idx = this._getProjectIdx(prj.id);
+        let delPrj = prj;
         if (idx === -1) {
             throw new Error("Invalid project id (id=" + prj.id + ")");
         }
-        this.xdsServerSvr.deleteProject(prj.id)
-            .subscribe(res => {
-                this.stSvr.deleteProject(prj.id)
-                    .subscribe(res => {
-                        this.confStore.projects.splice(idx, 1);
-                    }, err => {
-                        this.alert.error("Delete local ERROR: " + err);
-                    });
-            }, err => {
-                this.alert.error("Delete remote ERROR: " + err);
+        return this.xdsServerSvr.deleteProject(prj.id)
+            .flatMap(res => {
+                return this.stSvr.deleteProject(prj.id);
+            })
+            .map(res => {
+                this.confStore.projects.splice(idx, 1);
+                return delPrj;
             });
     }
 
index 19fe225..15c52ba 100644 (file)
@@ -39,6 +39,7 @@
             'ngx-bootstrap/carousel': 'npm:ngx-bootstrap/bundles/ngx-bootstrap.umd.min.js',
             'ngx-bootstrap/popover': 'npm:ngx-bootstrap/bundles/ngx-bootstrap.umd.min.js',
             'ngx-bootstrap/dropdown': 'npm:ngx-bootstrap/bundles/ngx-bootstrap.umd.min.js',
+            'ngx-bootstrap/collapse': 'npm:ngx-bootstrap/bundles/ngx-bootstrap.umd.min.js',
             // other libraries
             'socket.io-client': 'npm:socket.io-client/dist/socket.io.min.js'
         },
@@ -65,4 +66,4 @@
             }
         }
     });
-})(this);
\ No newline at end of file
+})(this);