diff options
Diffstat (limited to 'webapp/src/app/projects')
5 files changed, 289 insertions, 18 deletions
diff --git a/webapp/src/app/projects/projectAddModal.component.css b/webapp/src/app/projects/projectAddModal.component.css new file mode 100644 index 0000000..77f73a5 --- /dev/null +++ b/webapp/src/app/projects/projectAddModal.component.css @@ -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 index 0000000..dc84985 --- /dev/null +++ b/webapp/src/app/projects/projectAddModal.component.html @@ -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">×</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 index 0000000..7ef5b5e --- /dev/null +++ b/webapp/src/app/projects/projectAddModal.component.ts @@ -0,0 +1,152 @@ +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 => { + let last = n.split('/'); + let nm = n; + if (last.length > 0) { + nm = last.pop(); + if (nm === "" && last.length > 0) { + nm = last.pop(); + } + } + return "Project_" + nm; + }) + .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('NO files'); + } + let dir = e.target.files[0].webkitRelativePath; + console.log("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(); + }); + } + +} diff --git a/webapp/src/app/projects/projectCard.component.ts b/webapp/src/app/projects/projectCard.component.ts index 7a7fa21..a7ca9a3 100644 --- a/webapp/src/app/projects/projectCard.component.ts +++ b/webapp/src/app/projects/projectCard.component.ts @@ -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', @@ -7,7 +8,9 @@ import { ConfigService, IProject, ProjectType } from "../services/config.service <div class="row"> <div class="col-xs-12"> <div class="text-right" role="group"> - <button class="btn btn-link" (click)="delete(project)"><span class="fa fa-trash fa-size-x2"></span></button> + <button class="btn btn-link" (click)="delete(project)"> + <span class="fa fa-trash fa-size-x2"></span> + </button> </div> </div> </div> @@ -19,14 +22,25 @@ import { ConfigService, IProject, ProjectType } from "../services/config.service <td>{{ project.id }}</td> </tr> <tr> - <th><span class="fa fa-fw fa-folder-open-o"></span> <span>Folder path</span></th> - <td>{{ project.path}}</td> + <th><span class="fa fa-fw fa-exchange"></span> <span>Sharing type</span></th> + <td>{{ project.type | readableType }}</td> </tr> <tr> - <th><span class="fa fa-fw fa-exchange"></span> <span>Synchronization type</span></th> - <td>{{ project.type | readableType }}</td> + <th><span class="fa fa-fw fa-folder-open-o"></span> <span>Local path</span></th> + <td>{{ project.pathClient }}</td> + </tr> + <tr *ngIf="project.pathServer && project.pathServer != ''"> + <th><span class="fa fa-fw fa-folder-open-o"></span> <span>Server path</span></th> + <td>{{ project.pathServer }}</td> + </tr> + <tr> + <th><span class="fa fa-fw fa-flag"></span> <span>Status</span></th> + <td>{{ project.status }} - {{ project.isInSync ? "Up to Date" : "Out of Sync"}} + <button *ngIf="!project.isInSync" class="btn btn-link" (click)="sync(project)"> + <span class="fa fa-refresh fa-size-x2"></span> + </button> + </td> </tr> - </tbody> </table > `, @@ -37,12 +51,26 @@ 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); + }); + } + + sync(prj: IProject) { + this.configSvr.syncProject(prj) + .subscribe(res => { + }, err => { + this.alert.error("ERROR: " + err); + }); } } @@ -53,11 +81,11 @@ export class ProjectCardComponent { }) export class ProjectReadableTypePipe implements PipeTransform { - transform(type: ProjectType): string { - switch (+type) { - case ProjectType.NATIVE: return "Native"; - case ProjectType.SYNCTHING: return "Cloud (Syncthing)"; - default: return String(type); + transform(type: ProjectType): string { + switch (type) { + case ProjectType.NATIVE_PATHMAP: return "Native (path mapping)"; + case ProjectType.SYNCTHING: return "Cloud (Syncthing)"; + default: return String(type); + } } - } -}
\ No newline at end of file +} diff --git a/webapp/src/app/projects/projectsListAccordion.component.ts b/webapp/src/app/projects/projectsListAccordion.component.ts index 1b43cea..6e697f4 100644 --- a/webapp/src/app/projects/projectsListAccordion.component.ts +++ b/webapp/src/app/projects/projectsListAccordion.component.ts @@ -5,12 +5,25 @@ import { IProject } from "../services/config.service"; @Component({ selector: 'projects-list-accordion', template: ` + <style> + .fa.fa-exclamation-triangle { + margin-right: 2em; + color: red; + } + .fa.fa-refresh { + margin-right: 10px; + color: darkviolet; + } + </style> <accordion> <accordion-group #group *ngFor="let prj of projects"> <div accordion-heading> {{ prj.label }} - <i class="pull-right float-xs-right fa" - [ngClass]="{'fa-chevron-down': group.isOpen, 'fa-chevron-right': !group.isOpen}"></i> + <div class="pull-right"> + <i *ngIf="prj.status == 'Syncing'" class="fa fa-refresh faa-spin animated"></i> + <i *ngIf="!prj.isInSync && prj.status != 'Syncing'" class="fa fa-exclamation-triangle"></i> + <i class="fa" [ngClass]="{'fa-chevron-down': group.isOpen, 'fa-chevron-right': !group.isOpen}"></i> + </div> </div> <project-card [project]="prj"></project-card> </accordion-group> |