diff options
author | Sebastien Douheret <sebastien.douheret@iot.bzh> | 2017-11-20 17:23:04 +0100 |
---|---|---|
committer | Sebastien Douheret <sebastien.douheret@iot.bzh> | 2017-11-20 23:35:27 +0100 |
commit | 38c0c21a969e621c725245ce91c78e77076c5ce7 (patch) | |
tree | 0fbc61c45dc97836ca77c1426b31764a8ff158f0 /webapp/src/app/pages/projects | |
parent | b0d130807fb9bf36f5ac1abe21cbd558eb86d5cc (diff) |
New dashboard look & feel
Dashboard is a fork of ngx-admin release v2.0.1 (sha be7649a9a2da835)
Signed-off-by: Sebastien Douheret <sebastien.douheret@iot.bzh>
Diffstat (limited to 'webapp/src/app/pages/projects')
10 files changed, 531 insertions, 0 deletions
diff --git a/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.html b/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.html new file mode 100644 index 0000000..e2c6748 --- /dev/null +++ b/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.html @@ -0,0 +1,51 @@ +<div class="modal-header"> + <span>Add a new project</span> + <button class="close" aria-label="Close" (click)="closeModal()"> + <span aria-hidden="true">×</span> + </button> +</div> + +<div class="modal-body row"> + <div class="col-12"> + <form [formGroup]="addProjectForm" (ngSubmit)="onSubmit()"> + + <div class="form-group row"> + <label for="sharing-type" class="col-sm-3 col-form-label">Sharing Type</label> + <div class="col-sm-9"> + <select id="select-sharing-type" class="form-control" formControlName="type"> + <option *ngFor="let t of projectTypes" [value]="t.value">{{t.display}} + </option> + </select> + </div> + </div> + + <div class="form-group row"> + <label for="select-local-path" class="col-sm-3 col-form-label">Local Path</label> + <div class="col-sm-9"> + <input type="text" id="inputLocalPath" class="form-control" formControlName="pathCli" placeholder="/tmp/myProject" (change)="onChangeLocalProject($event)"> + </div> + </div> + + <div class="form-group row"> + <label for="select-server-path" class="col-sm-3 col-form-label">Server Path</label> + <div class="col-sm-9"> + <input type="text" id="inputServerPath" class="form-control" formControlName="pathSvr"> + </div> + </div> + + <div class="form-group row"> + <label for="select-label" class="col-sm-3 col-form-label">Label</label> + <div class="col-sm-9"> + <input type="text" id="inputLabel" class="form-control" formControlName="label" (keyup)="onKeyLabel($event)"> + </div> + </div> + + <div class="offset-sm-3 col-sm-9"> + <button class="btn btn-md btn-secondary" (click)="cancelAction=true; closeModal()"> Cancel </button> + <button class="btn btn-md btn-primary" type="submit" [disabled]="!addProjectForm.valid">Add Folder</button> + </div> + </form> + </div> +</div> +<div class="modal-footer form-group"> +</div> diff --git a/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.scss b/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.scss new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.scss @@ -0,0 +1 @@ + diff --git a/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.ts b/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.ts new file mode 100644 index 0000000..640ac5c --- /dev/null +++ b/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.ts @@ -0,0 +1,143 @@ +import { Component, Input, ViewChild, OnInit } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { FormControl, FormGroup, Validators, ValidationErrors, 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 '../../../@core-xds/services/alert.service'; +import { ProjectService, IProject, ProjectType, ProjectTypes } from '../../../@core-xds/services/project.service'; +import { XDSConfigService } from '../../../@core-xds/services/xds-config.service'; + + +@Component({ + selector: 'xds-project-add-modal', + templateUrl: 'project-add-modal.component.html', + styleUrls: ['project-add-modal.component.scss'] +}) +export class ProjectAddModalComponent implements OnInit { + // @Input('server-id') serverID: string; + private serverID: string; + + cancelAction = false; + userEditedLabel = false; + projectTypes = ProjectTypes; + + addProjectForm: FormGroup; + typeCtrl: FormControl; + pathCliCtrl: FormControl; + pathSvrCtrl: FormControl; + + constructor( + private alert: AlertService, + private projectSvr: ProjectService, + private XdsConfigSvr: XDSConfigService, + private fb: FormBuilder, + private activeModal: NgbActiveModal + ) { + // Define types (first one is special/placeholder) + this.projectTypes.unshift({ value: ProjectType.UNSET, display: '--Select a type--' }); + + this.typeCtrl = new FormControl(this.projectTypes[0].value, this.validatorProjType); + this.pathCliCtrl = new FormControl('', this.validatorProjPath); + this.pathSvrCtrl = new FormControl({ value: '', disabled: true }, this.validatorProjPath); + + this.addProjectForm = fb.group({ + type: this.typeCtrl, + pathCli: this.pathCliCtrl, + pathSvr: this.pathSvrCtrl, + label: ['', Validators.nullValidator], + }); + } + + + ngOnInit() { + // Update server ID + this.serverID = this.XdsConfigSvr.getCurServer().id; + this.XdsConfigSvr.onCurServer().subscribe(svr => this.serverID = svr.id); + + // Auto create label name + this.pathCliCtrl.valueChanges + .debounceTime(100) + .filter(n => n) + .map(n => { + const 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 => { + const dis = (valType === String(ProjectType.SYNCTHING)); + this.pathSvrCtrl.reset({ value: '', disabled: dis }); + }); + } + + closeModal() { + this.activeModal.close(); + } + + onKeyLabel(event: any) { + this.userEditedLabel = (this.addProjectForm.value.label !== ''); + } + + onChangeLocalProject(e) { + } + + onSubmit() { + if (this.cancelAction) { + return; + } + + const formVal = this.addProjectForm.value; + + const type = formVal['type'].value; + this.projectSvr.Add({ + serverId: this.serverID, + label: formVal['label'], + pathClient: formVal['pathCli'], + pathServer: formVal['pathSvr'], + type: formVal['type'], + // FIXME: allow to set defaultSdkID from New Project config panel + }) + .subscribe(prj => { + this.alert.info('Project ' + prj.label + ' successfully created.'); + this.closeModal(); + + // Reset Value for the next creation + this.addProjectForm.reset(); + const selectedType = this.projectTypes[0].value; + this.addProjectForm.patchValue({ type: selectedType }); + + }, + err => { + this.alert.error(err, 60); + this.closeModal(); + }); + } + + private validatorProjType(g: FormGroup): ValidationErrors | null { + return (g.value !== ProjectType.UNSET) ? null : { validatorProjType: { valid: false } }; + } + + private validatorProjPath(g: FormGroup): ValidationErrors | null { + return (g.disabled || g.value !== '') ? null : { validatorProjPath: { valid: false } }; + } + +} diff --git a/webapp/src/app/pages/projects/project-card/project-card.component.html b/webapp/src/app/pages/projects/project-card/project-card.component.html new file mode 100644 index 0000000..c54d581 --- /dev/null +++ b/webapp/src/app/pages/projects/project-card/project-card.component.html @@ -0,0 +1,65 @@ +<nb-card class="xds-projects"> + <nb-card-header> + + <div class="row"> + <div class="col-12 col-md-8"> + {{ project.label }} + </div> + <div class="col-6 col-md-4 text-right" role="group"> + <button class="btn btn-outline-danger btn-tn btn-xds" (click)="delete(project)"> + <span class="fa fa-trash fa-size-x2"></span> + </button> + </div> + </div> + </nb-card-header> + + <nb-card-body> + <table class="table table-striped"> + <tbody> + <tr> + <th> + <span class="fa fa-fw fa-id-badge"></span> + <span>Project ID</span> + </th> + <td>{{ project.id }}</td> + </tr> + <tr> + <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-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-outline-info btn-tn btn-xds" (click)="sync(project)" style="margin-left:2em;"> + <span class="fa fa-refresh fa-size-x2"></span> + </button> + </td> + </tr> + </tbody> + </table> + </nb-card-body> + + <nb-card-footer> + <!-- <pre>{{project | json}}</pre> --> + </nb-card-footer> +</nb-card> diff --git a/webapp/src/app/pages/projects/project-card/project-card.component.scss b/webapp/src/app/pages/projects/project-card/project-card.component.scss new file mode 100644 index 0000000..a433f58 --- /dev/null +++ b/webapp/src/app/pages/projects/project-card/project-card.component.scss @@ -0,0 +1,54 @@ +@import '~@nebular/theme/styles/global/bootstrap/buttons'; + +.xds-project-card .icon { + padding: 0.75rem 0; + font-size: 1.75rem; +} + +nb-card-body { + padding: 0; +} + +nb-card-footer { + text-align: right; +} + +.fa-big { + font-size: 20px; + font-weight: bold; +} + +.fa-size-x2 { + font-size: 20px; +} + +th span { + font-weight: 100; +} + +th label { + font-weight: 100; + margin-bottom: 0; +} + +tr.info>th { + vertical-align: middle; +} + +tr.info>td { + vertical-align: middle; +} + +.btn-outline-danger.btn-xds { + color: #ff4c6a; + &:focus { + color: white; + } +} + +.btn-outline-info.btn-xds { + color: #4ca6ff; + &:focus { + color: white; + } +} diff --git a/webapp/src/app/pages/projects/project-card/project-card.component.ts b/webapp/src/app/pages/projects/project-card/project-card.component.ts new file mode 100644 index 0000000..160c4c8 --- /dev/null +++ b/webapp/src/app/pages/projects/project-card/project-card.component.ts @@ -0,0 +1,52 @@ +import { Component, Input, Pipe, PipeTransform } from '@angular/core'; +import { ProjectService, IProject, ProjectType, ProjectTypeEnum } from '../../../@core-xds/services/project.service'; +import { AlertService } from '../../../@core-xds/services/alert.service'; + + +@Component({ + selector: 'xds-project-card', + styleUrls: ['./project-card.component.scss'], + templateUrl: './project-card.component.html', +}) +export class ProjectCardComponent { + + // FIXME workaround of https://github.com/angular/angular-cli/issues/2034 + // should be removed with angular 5 + // @Input() project: IProject; + @Input() project = <IProject>null; + + constructor( + private alert: AlertService, + private projectSvr: ProjectService + ) { + } + + delete(prj: IProject) { + this.projectSvr.Delete(prj).subscribe( + res => { }, + err => this.alert.error('ERROR delete: ' + err) + ); + } + + sync(prj: IProject) { + this.projectSvr.Sync(prj).subscribe( + res => { }, + err => this.alert.error('ERROR: ' + err) + ); + } +} + +// Make Project type human readable +@Pipe({ + name: 'readableType' +}) + +export class ProjectReadableTypePipe implements PipeTransform { + transform(type: ProjectTypeEnum): string { + switch (type) { + case ProjectType.NATIVE_PATHMAP: return 'Native (path mapping)'; + case ProjectType.SYNCTHING: return 'Cloud (Syncthing)'; + default: return String(type); + } + } +} diff --git a/webapp/src/app/pages/projects/projects.component.html b/webapp/src/app/pages/projects/projects.component.html new file mode 100644 index 0000000..662dfcc --- /dev/null +++ b/webapp/src/app/pages/projects/projects.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> + <button (click)="add()"> + <i class="nb-plus"></i> + <span>Add Project</span> + </button> + </nb-action> + </nb-actions> + </div> + <div class="col-3 right"> + <nb-actions size="medium"> + <nb-action> + <nb-search type="rotate-layout"></nb-search> + </nb-action> + </nb-actions> + </div> + </nb-card-body> + </div> + <div class="col-md-12 col-lg-12 col-xxxl-6" *ngFor="let prj of (projects$ | async)"> + <xds-project-card [project]="prj"></xds-project-card> + </div> +</div> diff --git a/webapp/src/app/pages/projects/projects.component.scss b/webapp/src/app/pages/projects/projects.component.scss new file mode 100644 index 0000000..3631fbb --- /dev/null +++ b/webapp/src/app/pages/projects/projects.component.scss @@ -0,0 +1,83 @@ +@import '../../@theme/styles/themes'; +@import '~@nebular/theme/components/card/card.component.theme'; +@import '~bootstrap/scss/mixins/breakpoints'; +@import '~@nebular/theme/styles/global/bootstrap/breakpoints'; +@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: 2.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; + } + } + } + @include media-breakpoint-down(md) { + nb-actions nb-action { + padding: 0 0.75rem; + } + } + @include media-breakpoint-down(sm) { + nb-card-body { + padding: 1rem; + } + nb-action { + font-size: 0.75rem; + i { + font-size: 2rem; + margin-right: 0.5rem; + } + } + } + @include media-breakpoint-down(is) { + nb-action i { + font-size: 1.75rem; + margin: 0; + } + span { + display: none; + } + } +} diff --git a/webapp/src/app/pages/projects/projects.component.ts b/webapp/src/app/pages/projects/projects.component.ts new file mode 100644 index 0000000..179bbd0 --- /dev/null +++ b/webapp/src/app/pages/projects/projects.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; + +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ProjectAddModalComponent } from './project-add-modal/project-add-modal.component'; + +import { ProjectService, IProject } from '../../@core-xds/services/project.service'; + +@Component({ + selector: 'xds-projects', + styleUrls: ['./projects.component.scss'], + templateUrl: './projects.component.html', +}) +export class ProjectsComponent implements OnInit { + + projects$: Observable<IProject[]>; + projects: IProject[]; + + constructor( + private projectSvr: ProjectService, + private modalService: NgbModal, + ) { + } + + ngOnInit() { + this.projects$ = this.projectSvr.Projects$; + } + + add() { + const activeModal = this.modalService.open(ProjectAddModalComponent, { size: 'lg', container: 'nb-layout' }); + activeModal.componentInstance.modalHeader = 'Large Modal'; + } +} diff --git a/webapp/src/app/pages/projects/projects.module.ts b/webapp/src/app/pages/projects/projects.module.ts new file mode 100644 index 0000000..48f37ce --- /dev/null +++ b/webapp/src/app/pages/projects/projects.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { ThemeModule } from '../../@theme/theme.module'; + +import { ProjectsComponent } from './projects.component'; +import { ProjectCardComponent, ProjectReadableTypePipe } from './project-card/project-card.component'; +import { ProjectAddModalComponent } from './project-add-modal/project-add-modal.component'; + + +@NgModule({ + imports: [ + ThemeModule, + ], + declarations: [ + ProjectsComponent, + ProjectCardComponent, + ProjectAddModalComponent, + ProjectReadableTypePipe, + ], + entryComponents: [ + ProjectAddModalComponent + ], +}) +export class ProjectsModule { } |