From 0b3d4dd3b9e45e6a06aadff2dc856666945ed518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Agneray?= <francois.agneray@lam.fr> Date: Fri, 25 Jun 2021 16:23:39 +0200 Subject: [PATCH] Admin settings => done --- client/src/app/admin/components/index.ts | 4 +- .../settings/form-option.component.html | 8 +- .../settings/form-option.component.ts | 33 +++++-- .../settings/form-select.component.html | 6 +- .../settings/form-select.component.ts | 30 ++++--- .../app/admin/components/settings/index.ts | 4 +- .../settings/option-list.component.ts | 32 ------- ...onent.html => option-table.component.html} | 27 ++---- .../settings/option-table.component.ts | 14 +++ .../settings/select-buttons.component.html | 54 ++++-------- .../settings/select-buttons.component.ts | 23 ++--- .../settings/select-list.component.html | 8 +- .../settings/select-list.component.scss | 8 -- .../settings/select-list.component.ts | 1 - .../components/shared/add-btn.component.html | 12 +++ .../components/shared/add-btn.component.ts | 23 +++++ .../components/shared/edit-btn.component.html | 12 +++ .../components/shared/edit-btn.component.ts | 23 +++++ .../src/app/admin/components/shared/index.ts | 9 ++ .../settings/settings.component.html | 21 +++-- .../containers/settings/settings.component.ts | 24 +++--- .../store/actions/select-option.actions.ts | 9 ++ .../metamodel/store/actions/select.actions.ts | 9 ++ .../store/effects/select-option.effects.ts | 85 +++++++++++++++++- .../metamodel/store/effects/select.effects.ts | 86 ++++++++++++++++++- .../store/reducers/select-option.reducer.ts | 9 ++ .../store/reducers/select.reducer.ts | 9 ++ client/src/app/shared/shared.module.ts | 3 + client/src/styles.scss | 14 +++ 29 files changed, 429 insertions(+), 171 deletions(-) delete mode 100644 client/src/app/admin/components/settings/option-list.component.ts rename client/src/app/admin/components/settings/{option-list.component.html => option-table.component.html} (52%) create mode 100644 client/src/app/admin/components/settings/option-table.component.ts delete mode 100644 client/src/app/admin/components/settings/select-list.component.scss create mode 100644 client/src/app/admin/components/shared/add-btn.component.html create mode 100644 client/src/app/admin/components/shared/add-btn.component.ts create mode 100644 client/src/app/admin/components/shared/edit-btn.component.html create mode 100644 client/src/app/admin/components/shared/edit-btn.component.ts create mode 100644 client/src/app/admin/components/shared/index.ts diff --git a/client/src/app/admin/components/index.ts b/client/src/app/admin/components/index.ts index 585ffe5d..f1a04357 100644 --- a/client/src/app/admin/components/index.ts +++ b/client/src/app/admin/components/index.ts @@ -1,12 +1,12 @@ +import { sharedComponents } from "./shared"; import { InstanceCardComponent } from "./instance/instance-card.component"; -import { DeleteBtnComponent } from "./shared/delete-btn.component"; import { SurveyTableComponent } from "./survey/survey-table.component"; import { DatabaseTableComponent } from "./database/database-table.component"; import { settingsComponents } from './settings'; export const dummiesComponents = [ + sharedComponents, InstanceCardComponent, - DeleteBtnComponent, SurveyTableComponent, DatabaseTableComponent, settingsComponents diff --git a/client/src/app/admin/components/settings/form-option.component.html b/client/src/app/admin/components/settings/form-option.component.html index 54407fdf..d957337c 100644 --- a/client/src/app/admin/components/settings/form-option.component.html +++ b/client/src/app/admin/components/settings/form-option.component.html @@ -1,15 +1,15 @@ -<form name="form" (ngSubmit)="f.form.valid && emit(f.form.value)" #f="ngForm" novalidate> +<form [formGroup]="selectOptionForm" (ngSubmit)="submit()" novalidate> <div class="form-group"> <label for="label">Label</label> - <input type="text" class="form-control" name="label" [ngModel]="model.label" #label="ngModel" required> + <input type="text" class="form-control" id="label" name="label" formControlName="label"> </div> <div class="form-group"> <label for="value">Value</label> - <input type="text" class="form-control" name="value" [ngModel]="model.value" #value="ngModel" required> + <input type="text" class="form-control" id="value" name="value" formControlName="value"> </div> <div class="form-group"> <label for="display">Display</label> - <input type="number" class="form-control" name="display" [ngModel]="model.display" #displays="ngModel" required> + <input type="number" class="form-control" id="display" name="display" formControlName="display"> </div> <div class="form-group"> <ng-content></ng-content> diff --git a/client/src/app/admin/components/settings/form-option.component.ts b/client/src/app/admin/components/settings/form-option.component.ts index 1ff1b0fd..62655a0b 100644 --- a/client/src/app/admin/components/settings/form-option.component.ts +++ b/client/src/app/admin/components/settings/form-option.component.ts @@ -1,5 +1,5 @@ -import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core'; -import { NgForm } from '@angular/forms'; +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; import { SelectOption } from 'src/app/metamodel/store/models'; @@ -8,11 +8,30 @@ import { SelectOption } from 'src/app/metamodel/store/models'; templateUrl: 'form-option.component.html' }) export class FormOptionComponent { - @ViewChild(NgForm, {static: true}) ngForm: NgForm; - @Input() model: SelectOption; - @Output() submitted: EventEmitter<SelectOption> = new EventEmitter(); + @Input() selectOption: SelectOption; + @Output() onSubmit: EventEmitter<SelectOption> = new EventEmitter(); - emit(option: SelectOption) { - this.submitted.emit({id: this.model.id, ...option}); + public selectOptionForm = new FormGroup({ + label: new FormControl('', [Validators.required]), + value: new FormControl('', [Validators.required]), + display: new FormControl('', [Validators.required]) + }); + + ngOnInit() { + if (this.selectOption) { + this.selectOptionForm.patchValue(this.selectOption); + } + } + + submit() { + if (this.selectOption) { + this.onSubmit.emit({ + ...this.selectOption, + ...this.selectOptionForm.value + }); + } else { + this.onSubmit.emit(this.selectOptionForm.value); + this.selectOptionForm.reset(); + } } } diff --git a/client/src/app/admin/components/settings/form-select.component.html b/client/src/app/admin/components/settings/form-select.component.html index f5957ca0..e53bb31a 100644 --- a/client/src/app/admin/components/settings/form-select.component.html +++ b/client/src/app/admin/components/settings/form-select.component.html @@ -1,11 +1,11 @@ -<form [formGroup]="selectForm"> +<form [formGroup]="selectForm" (ngSubmit)="onSubmit.emit(selectForm.getRawValue())" novalidate> <div class="form-group"> <label for="name">Name</label> - <input type="text" class="form-control" name="name" [ngModel]="model.name" #name="ngModel" [disabled]="model.name" required> + <input type="text" class="form-control" id="name" name="name" formControlName="name"> </div> <div class="form-group"> <label for="label">Label</label> - <input type="text" class="form-control" name="label" [ngModel]="model.label" #label="ngModel" required> + <input type="text" class="form-control" id="label" name="label" formControlName="label"> </div> <div class="form-group"> <ng-content></ng-content> diff --git a/client/src/app/admin/components/settings/form-select.component.ts b/client/src/app/admin/components/settings/form-select.component.ts index a27114cc..a3dfc0fc 100644 --- a/client/src/app/admin/components/settings/form-select.component.ts +++ b/client/src/app/admin/components/settings/form-select.component.ts @@ -1,5 +1,5 @@ -import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core'; -import { NgForm, FormGroup, FormControl } from '@angular/forms'; +import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; import { Select } from 'src/app/metamodel/store/models'; @@ -7,19 +7,23 @@ import { Select } from 'src/app/metamodel/store/models'; selector: 'app-form-select', templateUrl: 'form-select.component.html' }) -export class FormSelectComponent { - @ViewChild(NgForm, {static: true}) ngForm: NgForm; - @Input() model: Select; - @Output() submitted: EventEmitter<Select> = new EventEmitter(); +export class FormSelectComponent implements OnInit, OnChanges { + @Input() select: Select; + @Output() onSubmit: EventEmitter<Select> = new EventEmitter(); - selectForm = new FormGroup({ - name: new FormControl(''), - label: new FormControl('') + public selectForm = new FormGroup({ + name: new FormControl('', [Validators.required, Validators.pattern('[a-z1-9_]*')]), + label: new FormControl('', [Validators.required]) }); - emit(select: Select) { - let selectEmitted: Select; - (this.model.name) ? selectEmitted = {name: this.model.name, ...select} : selectEmitted = select; - this.submitted.emit(selectEmitted); + ngOnInit() { + if (this.select) { + this.selectForm.setValue(this.select); + this.selectForm.controls.name.disable(); + } + } + + ngOnChanges(changes: SimpleChanges) { + this.selectForm.setValue(changes.select.currentValue); } } diff --git a/client/src/app/admin/components/settings/index.ts b/client/src/app/admin/components/settings/index.ts index 9364ca7e..ed1b9a88 100644 --- a/client/src/app/admin/components/settings/index.ts +++ b/client/src/app/admin/components/settings/index.ts @@ -1,13 +1,13 @@ import { SelectListComponent } from "./select-list.component"; import { SelectButtonsComponent } from "./select-buttons.component"; -import { OptionListComponent } from "./option-list.component"; +import { OptionTableComponent } from "./option-table.component"; import { FormSelectComponent } from "./form-select.component"; import { FormOptionComponent } from "./form-option.component"; export const settingsComponents = [ SelectListComponent, SelectButtonsComponent, - OptionListComponent, + OptionTableComponent, FormSelectComponent, FormOptionComponent ]; diff --git a/client/src/app/admin/components/settings/option-list.component.ts b/client/src/app/admin/components/settings/option-list.component.ts deleted file mode 100644 index c1cfb8ed..00000000 --- a/client/src/app/admin/components/settings/option-list.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter, TemplateRef } from '@angular/core'; - -import { BsModalService } from 'ngx-bootstrap/modal'; -import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; - -import { SelectOption } from 'src/app/metamodel/store/models'; - -@Component({ - selector: 'app-option-list', - templateUrl: 'option-list.component.html', - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class OptionListComponent { - @Input() optionList: SelectOption[]; - @Output() editOption: EventEmitter<SelectOption> = new EventEmitter(); - @Output() deleteOption: EventEmitter<SelectOption> = new EventEmitter(); - - modalRef: BsModalRef; - optionSelected: SelectOption; - - constructor(private modalService: BsModalService) { } - - openModal(template: TemplateRef<any>, option: SelectOption) { - this.optionSelected = option; - this.modalRef = this.modalService.show(template); - } - - confirmEdit(option: SelectOption) { - this.editOption.emit(option); - this.modalRef.hide(); - } -} diff --git a/client/src/app/admin/components/settings/option-list.component.html b/client/src/app/admin/components/settings/option-table.component.html similarity index 52% rename from client/src/app/admin/components/settings/option-list.component.html rename to client/src/app/admin/components/settings/option-table.component.html index 82399054..e7cba6c2 100644 --- a/client/src/app/admin/components/settings/option-list.component.html +++ b/client/src/app/admin/components/settings/option-table.component.html @@ -15,9 +15,15 @@ <td class="align-middle">{{ option.value }}</td> <td class="align-middle">{{ option.display }}</td> <td class="align-middle"> - <button title="Edit this option" (click)="openModal(templateForEdit, option); $event.stopPropagation()" class="btn btn-outline-primary"> - <span class="fas fa-edit"></span> - </button> + <app-edit-btn [type]="'option'" [type]="'option'" [label]="option.label" #editBtn> + <app-form-option [selectOption]="option" (onSubmit)="editOption.emit($event)" #formAddOption> + <button [disabled]="!formAddOption.selectOptionForm.valid || formAddOption.selectOptionForm.pristine" (click)="editBtn.modalRef.hide()" type="submit" class="btn btn-primary"> + <span class="fa fa-database"></span> Edit select option + </button> + + <button (click)="editBtn.modalRef.hide()" type="button" class="btn btn-danger">Cancel</button> + </app-form-option> + </app-edit-btn> </td> <td class="align-middle"> <app-delete-btn @@ -30,18 +36,3 @@ </tbody> </table> </div> - -<ng-template #templateForEdit> - <div class="modal-header"> - <h4 class="modal-title pull-left"><strong>{{ optionSelected.label }}</strong></h4> - </div> - <div class="modal-body"> - <app-form-option [model]="optionSelected" (submitted)="confirmEdit($event)" #formEditOption> - <button [disabled]="!formEditOption.ngForm.form.valid || formEditOption.ngForm.form.pristine" type="submit" class="btn btn-primary"> - <span class="fa fa-database"></span> Update option information - </button> - - <button (click)="modalRef.hide()" class="btn btn-danger">Cancel</button> - </app-form-option> - </div> -</ng-template> diff --git a/client/src/app/admin/components/settings/option-table.component.ts b/client/src/app/admin/components/settings/option-table.component.ts new file mode 100644 index 00000000..32417d3c --- /dev/null +++ b/client/src/app/admin/components/settings/option-table.component.ts @@ -0,0 +1,14 @@ +import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter } from '@angular/core'; + +import { SelectOption } from 'src/app/metamodel/store/models'; + +@Component({ + selector: 'app-option-table', + templateUrl: 'option-table.component.html', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class OptionTableComponent { + @Input() optionList: SelectOption[]; + @Output() editOption: EventEmitter<SelectOption> = new EventEmitter(); + @Output() deleteOption: EventEmitter<SelectOption> = new EventEmitter(); +} diff --git a/client/src/app/admin/components/settings/select-buttons.component.html b/client/src/app/admin/components/settings/select-buttons.component.html index 8a1f50bc..4da6a2ff 100644 --- a/client/src/app/admin/components/settings/select-buttons.component.html +++ b/client/src/app/admin/components/settings/select-buttons.component.html @@ -1,43 +1,25 @@ -<button title="Add option" class="btn btn-outline-success" (click)="openModal(templateForAddOption)"> - <span class="fas fa-plus"></span> -</button> +<app-add-btn [type]="'option'" [label]="getFormAddOptionLabel()" #addBtn> + <app-form-option (onSubmit)="addNewSelectOption($event)" #formAddOption> + <button [disabled]="!formAddOption.selectOptionForm.valid || formAddOption.selectOptionForm.pristine" (click)="addBtn.modalRef.hide()" type="submit" class="btn btn-primary"> + <span class="fa fa-database"></span> Add new option + </button> + + <button (click)="addBtn.modalRef.hide()" type="button" class="btn btn-danger">Cancel</button> + </app-form-option> +</app-add-btn> -<button title="Edit select" class="btn btn-outline-primary" (click)="openModal(templateForEdit)"> - <span class="fas fa-edit"></span> -</button> +<app-edit-btn [type]="'select'" [label]="select.label" #editBtn> + <app-form-select [select]="select" (onSubmit)="editSelect.emit($event)" #formEditSelect> + <button [disabled]="!formEditSelect.selectForm.valid || formEditSelect.selectForm.pristine" (click)="editBtn.modalRef.hide()" type="submit" class="btn btn-primary"> + <span class="fa fa-database"></span> Update select information + </button> + + <button (click)="editBtn.modalRef.hide()" type="button" class="btn btn-danger">Cancel</button> + </app-form-select> +</app-edit-btn> <app-delete-btn [type]="'select'" [label]="select.label" (confirm)="deleteSelect.emit(select)"> </app-delete-btn> - -<ng-template #templateForEdit> - <div class="modal-header"> - <h4 class="modal-title pull-left"><strong>{{ select.label }}</strong></h4> - </div> - <div class="modal-body"> - <app-form-select [model]="select" (submitted)="confirmEdit($event)" #formEditSelect> - <button [disabled]="!formEditSelect.ngForm.form.valid || formEditSelect.ngForm.form.pristine" type="submit" class="btn btn-primary"> - <span class="fa fa-database"></span> Update select information - </button> - - <button (click)="modalRef.hide()" class="btn btn-danger">Cancel</button> - </app-form-select> - </div> -</ng-template> - -<ng-template #templateForAddOption> - <div class="modal-header"> - <h4 class="modal-title pull-left"><strong>Add new option for : {{ select.label }}</strong></h4> - </div> - <div class="modal-body"> - <app-form-option (submitted)="addNewOption($event)" #formAddOption> - <button [disabled]="!formAddOption.ngForm.form.valid || formAddOption.ngForm.form.pristine" type="submit" class="btn btn-primary"> - <span class="fa fa-database"></span> Add new option - </button> - - <button (click)="modalRef.hide()" class="btn btn-danger">Cancel</button> - </app-form-option> - </div> -</ng-template> diff --git a/client/src/app/admin/components/settings/select-buttons.component.ts b/client/src/app/admin/components/settings/select-buttons.component.ts index d1f495cd..640892b5 100644 --- a/client/src/app/admin/components/settings/select-buttons.component.ts +++ b/client/src/app/admin/components/settings/select-buttons.component.ts @@ -1,7 +1,4 @@ -import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter, TemplateRef } from '@angular/core'; - -import { BsModalService } from 'ngx-bootstrap/modal'; -import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; +import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter } from '@angular/core'; import { Select, SelectOption } from 'src/app/metamodel/store/models'; @@ -16,21 +13,11 @@ export class SelectButtonsComponent { @Output() editSelect: EventEmitter<Select> = new EventEmitter(); @Output() addOption: EventEmitter<SelectOption> = new EventEmitter(); - modalRef: BsModalRef; - - constructor(private modalService: BsModalService) { } - - openModal(template: TemplateRef<any>) { - this.modalRef = this.modalService.show(template); - } - - confirmEdit(select: Select) { - this.editSelect.emit(select); - this.modalRef.hide(); + getFormAddOptionLabel() { + return `Add new option for : ${this.select.label}`; } - addNewOption(option: SelectOption) { - this.addOption.emit({...option, select_name: this.select.name}); - this.modalRef.hide(); + addNewSelectOption(selectOption: SelectOption) { + this.addOption.emit({...selectOption, select_name: this.select.name}); } } diff --git a/client/src/app/admin/components/settings/select-list.component.html b/client/src/app/admin/components/settings/select-list.component.html index acd01265..ec36de71 100644 --- a/client/src/app/admin/components/settings/select-list.component.html +++ b/client/src/app/admin/components/settings/select-list.component.html @@ -3,7 +3,7 @@ <a class="nav-link" routerLink="/admin/settings/{{select.name}}" routerLinkActive="active">{{ select.label }}</a> </li> <li class="nav-item"> - <a class="nav-link add-select" (click)="openModal(templateForNew); $event.stopPropagation()"><span class="fas fa-plus"></span></a> + <a class="nav-link add-select pointer" (click)="openModal(templateForNew); $event.stopPropagation()"><span class="fas fa-plus"></span></a> </li> </ul> @@ -12,12 +12,12 @@ <h4 class="modal-title pull-left"><strong>Add new select</strong></h4> </div> <div class="modal-body"> - <app-form-select (submitted)="confirmAdd($event)" #formNewSelect> - <button [disabled]="!formNewSelect.ngForm.form.valid || formNewSelect.ngForm.form.pristine" type="submit" class="btn btn-primary"> + <app-form-select (onSubmit)="confirmAdd($event)" #formNewSelect> + <button [disabled]="!formNewSelect.selectForm.valid || formNewSelect.selectForm.pristine" type="submit" class="btn btn-primary"> <span class="fa fa-database"></span> Add select </button> - <button (click)="modalRef.hide()" class="btn btn-danger">Cancel</button> + <button (click)="modalRef.hide(); $event.stopPropagation()" type="button" class="btn btn-danger">Cancel</button> </app-form-select> </div> </ng-template> diff --git a/client/src/app/admin/components/settings/select-list.component.scss b/client/src/app/admin/components/settings/select-list.component.scss deleted file mode 100644 index c3b50f96..00000000 --- a/client/src/app/admin/components/settings/select-list.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -.add-select { - cursor: pointer; - color: #007bff; -} - -.add-select:hover { - color: #007bff; -} diff --git a/client/src/app/admin/components/settings/select-list.component.ts b/client/src/app/admin/components/settings/select-list.component.ts index 199392ff..5ad9a6e7 100644 --- a/client/src/app/admin/components/settings/select-list.component.ts +++ b/client/src/app/admin/components/settings/select-list.component.ts @@ -8,7 +8,6 @@ import { Select } from 'src/app/metamodel/store/models'; @Component({ selector: 'app-select-list', templateUrl: 'select-list.component.html', - styleUrls: ['select-list.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class SelectListComponent { diff --git a/client/src/app/admin/components/shared/add-btn.component.html b/client/src/app/admin/components/shared/add-btn.component.html new file mode 100644 index 00000000..bafcf26f --- /dev/null +++ b/client/src/app/admin/components/shared/add-btn.component.html @@ -0,0 +1,12 @@ +<button title="Add {{ type }}" class="btn btn-outline-success" (click)="openModal(template)"> + <span class="fas fa-plus"></span> +</button> + +<ng-template #template> + <div class="modal-header"> + <h4 class="modal-title pull-left"><strong>{{ label }}</strong></h4> + </div> + <div class="modal-body"> + <ng-content></ng-content> + </div> +</ng-template> diff --git a/client/src/app/admin/components/shared/add-btn.component.ts b/client/src/app/admin/components/shared/add-btn.component.ts new file mode 100644 index 00000000..fc9035a5 --- /dev/null +++ b/client/src/app/admin/components/shared/add-btn.component.ts @@ -0,0 +1,23 @@ +import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter, TemplateRef } from '@angular/core'; + +import { BsModalService } from 'ngx-bootstrap/modal'; +import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; + +@Component({ + selector: 'app-add-btn', + templateUrl: 'add-btn.component.html', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class AddBtnComponent { + @Input() type: string; + @Input() label: string; + @Input() disabled: boolean = false; + + modalRef: BsModalRef; + + constructor(private modalService: BsModalService) { } + + openModal(template: TemplateRef<any>) { + this.modalRef = this.modalService.show(template); + } +} diff --git a/client/src/app/admin/components/shared/edit-btn.component.html b/client/src/app/admin/components/shared/edit-btn.component.html new file mode 100644 index 00000000..892b9382 --- /dev/null +++ b/client/src/app/admin/components/shared/edit-btn.component.html @@ -0,0 +1,12 @@ +<button [disabled]="disabled" title="Edit this {{ type }}" (click)="openModal(template)" class="btn btn-outline-primary"> + <span class="fas fa-edit"></span> +</button> + +<ng-template #template> + <div class="modal-header"> + <h4 class="modal-title pull-left"><strong>{{ label }}</strong></h4> + </div> + <div class="modal-body"> + <ng-content></ng-content> + </div> +</ng-template> diff --git a/client/src/app/admin/components/shared/edit-btn.component.ts b/client/src/app/admin/components/shared/edit-btn.component.ts new file mode 100644 index 00000000..29717291 --- /dev/null +++ b/client/src/app/admin/components/shared/edit-btn.component.ts @@ -0,0 +1,23 @@ +import { Component, Input, ChangeDetectionStrategy, TemplateRef } from '@angular/core'; + +import { BsModalService } from 'ngx-bootstrap/modal'; +import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; + +@Component({ + selector: 'app-edit-btn', + templateUrl: 'edit-btn.component.html', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class EditBtnComponent { + @Input() type: string; + @Input() label: string; + @Input() disabled: boolean = false; + + modalRef: BsModalRef; + + constructor(private modalService: BsModalService) { } + + openModal(template: TemplateRef<any>) { + this.modalRef = this.modalService.show(template); + } +} diff --git a/client/src/app/admin/components/shared/index.ts b/client/src/app/admin/components/shared/index.ts new file mode 100644 index 00000000..9fff83b3 --- /dev/null +++ b/client/src/app/admin/components/shared/index.ts @@ -0,0 +1,9 @@ +import { AddBtnComponent } from "./add-btn.component"; +import { EditBtnComponent } from "./edit-btn.component"; +import { DeleteBtnComponent } from "./delete-btn.component"; + +export const sharedComponents = [ + AddBtnComponent, + EditBtnComponent, + DeleteBtnComponent +]; diff --git a/client/src/app/admin/containers/settings/settings.component.html b/client/src/app/admin/containers/settings/settings.component.html index d82e1911..fffd6d48 100644 --- a/client/src/app/admin/containers/settings/settings.component.html +++ b/client/src/app/admin/containers/settings/settings.component.html @@ -3,7 +3,8 @@ <ol class="breadcrumb"> <li class="breadcrumb-item active">Settings</li> <li *ngIf="(currentSelect | async)" class="breadcrumb-item active" aria-current="page"> - {{ (currentSelect |Â async).label }}</li> + {{ (currentSelect |Â async).label }} + </li> </ol> </nav> @@ -12,22 +13,30 @@ <div *ngIf="(selectListIsLoaded | async) && (optionListIsLoaded | async)"> <div class="card text-center"> <div class="card-header"> - <app-select-list [selectList]="selectList | async" (addSelect)="addSelect($event)"></app-select-list> + <app-select-list + [selectList]="selectList | async" + (addSelect)="addSelect($event)"> + </app-select-list> </div> <div *ngIf="(currentSelect | async)" class="card-body"> <div class="row"> <div class="col text-right"> - <app-select-buttons [select]="currentSelect | async" (deleteSelect)="deleteSelect($event)" - (editSelect)="editSelect($event)" (addOption)="addOption($event)"> + <app-select-buttons + [select]="currentSelect | async" + (deleteSelect)="deleteSelect($event)" + (editSelect)="editSelect($event)" + (addOption)="addOption($event)"> </app-select-buttons> </div> </div> <div class="row mt-1"> <div class="col-12"> - <app-option-list [optionList]="optionList | async" (editOption)="editOption($event)" + <app-option-table + [optionList]="optionList | async" + (editOption)="editOption($event)" (deleteOption)="deleteOption($event)"> - </app-option-list> + </app-option-table> </div> </div> </div> diff --git a/client/src/app/admin/containers/settings/settings.component.ts b/client/src/app/admin/containers/settings/settings.component.ts index e7b49566..ef58d756 100644 --- a/client/src/app/admin/containers/settings/settings.component.ts +++ b/client/src/app/admin/containers/settings/settings.component.ts @@ -37,27 +37,27 @@ export class SettingsComponent implements OnInit { this.store.dispatch(optionActions.loadSelectOptionList()); } - addSelect(settingsSelect: Select) { - // this.store.dispatch(new selectActions.AddNewSelectAction(settingsSelect)); + addSelect(select: Select) { + this.store.dispatch(selectActions.addSelect({ select })); } - editSelect(settingsSelect: Select) { - // this.store.dispatch(new selectActions.EditSelectAction(settingsSelect)); + editSelect(select: Select) { + this.store.dispatch(selectActions.editSelect({ select })); } - deleteSelect(settingsSelect: Select) { - // this.store.dispatch(new selectActions.DeleteSelectAction(settingsSelect)); + deleteSelect(select: Select) { + this.store.dispatch(selectActions.deleteSelect({ select })); } - addOption(settingsSelectOption: SelectOption) { - // this.store.dispatch(new optionActions.AddNewSelectOptionAction(settingsSelectOption)); + addOption(selectOption: SelectOption) { + this.store.dispatch(optionActions.addSelectOption({ selectOption })); } - editOption(settingsSelectOption: SelectOption) { - // this.store.dispatch(new optionActions.EditSelectOptionAction(settingsSelectOption)); + editOption(selectOption: SelectOption) { + this.store.dispatch(optionActions.editSelectOption({ selectOption })); } - deleteOption(settingsSelectOption: SelectOption) { - // this.store.dispatch(new optionActions.DeleteSelectOptionAction(settingsSelectOption)); + deleteOption(selectOption: SelectOption) { + this.store.dispatch(optionActions.deleteSelectOption({ selectOption })); } } diff --git a/client/src/app/metamodel/store/actions/select-option.actions.ts b/client/src/app/metamodel/store/actions/select-option.actions.ts index 0fe84a33..2397f7db 100644 --- a/client/src/app/metamodel/store/actions/select-option.actions.ts +++ b/client/src/app/metamodel/store/actions/select-option.actions.ts @@ -5,3 +5,12 @@ import { SelectOption } from '../models'; export const loadSelectOptionList = createAction('[Metamodel] Load Select Option List'); export const loadSelectOptionListSuccess = createAction('[Metamodel] Load Select Option List Success', props<{ selectOptions: SelectOption[] }>()); export const loadSelectOptionListFail = createAction('[Metamodel] Load Select Option List Fail'); +export const addSelectOption = createAction('[Metamodel] Add Select Option', props<{ selectOption: SelectOption }>()); +export const addSelectOptionSuccess = createAction('[Metamodel] Add Select Option Success', props<{ selectOption: SelectOption }>()); +export const addSelectOptionFail = createAction('[Metamodel] Add Select Option Fail'); +export const editSelectOption = createAction('[Metamodel] Edit Select Option', props<{ selectOption: SelectOption }>()); +export const editSelectOptionSuccess = createAction('[Metamodel] Edit Select Option Success', props<{ selectOption: SelectOption }>()); +export const editSelectOptionFail = createAction('[Metamodel] Edit Select Option Fail'); +export const deleteSelectOption = createAction('[Metamodel] Delete Select Option', props<{ selectOption: SelectOption }>()); +export const deleteSelectOptionSuccess = createAction('[Metamodel] Delete Select Option Success', props<{ selectOption: SelectOption }>()); +export const deleteSelectOptionFail = createAction('[Metamodel] Delete Select Option Fail'); diff --git a/client/src/app/metamodel/store/actions/select.actions.ts b/client/src/app/metamodel/store/actions/select.actions.ts index ca8585bd..2b6caea7 100644 --- a/client/src/app/metamodel/store/actions/select.actions.ts +++ b/client/src/app/metamodel/store/actions/select.actions.ts @@ -5,3 +5,12 @@ import { Select } from '../models'; export const loadSelectList = createAction('[Metamodel] Load Select List'); export const loadSelectListSuccess = createAction('[Metamodel] Load Select List Success', props<{ selects: Select[] }>()); export const loadSelectListFail = createAction('[Metamodel] Load Select List Fail'); +export const addSelect = createAction('[Metamodel] Add Select', props<{ select: Select }>()); +export const addSelectSuccess = createAction('[Metamodel] Add Select Success', props<{ select: Select }>()); +export const addSelectFail = createAction('[Metamodel] Add Select Fail'); +export const editSelect = createAction('[Metamodel] Edit Select', props<{ select: Select }>()); +export const editSelectSuccess = createAction('[Metamodel] Edit Select Success', props<{ select: Select }>()); +export const editSelectFail = createAction('[Metamodel] Edit Select Fail'); +export const deleteSelect = createAction('[Metamodel] Delete Select', props<{ select: Select }>()); +export const deleteSelectSuccess = createAction('[Metamodel] Delete Select Success', props<{ select: Select }>()); +export const deleteSelectFail = createAction('[Metamodel] Delete Select Fail'); diff --git a/client/src/app/metamodel/store/effects/select-option.effects.ts b/client/src/app/metamodel/store/effects/select-option.effects.ts index d94e21c0..b8fc3427 100644 --- a/client/src/app/metamodel/store/effects/select-option.effects.ts +++ b/client/src/app/metamodel/store/effects/select-option.effects.ts @@ -1,7 +1,9 @@ import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { of } from 'rxjs'; -import { map, mergeMap, catchError } from 'rxjs/operators'; +import { map, tap, mergeMap, catchError } from 'rxjs/operators'; + +import { ToastrService } from 'ngx-toastr'; import * as selectOptionActions from '../actions/select-option.actions'; import { SelectOptionService } from '../services/select-option.service'; @@ -21,8 +23,87 @@ export class SelectOptionEffects { ) ); + addSelectOption$ = createEffect(() => + this.actions$.pipe( + ofType(selectOptionActions.addSelectOption), + mergeMap(action => this.selectOptionService.addSelectOption(action.selectOption) + .pipe( + map(selectOption => selectOptionActions.addSelectOptionSuccess({ selectOption })), + catchError(() => of(selectOptionActions.addSelectOptionFail())) + ) + ) + ) + ); + + addSelectOptionSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(selectOptionActions.addSelectOptionSuccess), + tap(() => this.toastr.success('Select option successfully added', 'The new select option was added into the database')) + ), { dispatch: false} + ); + + addSelectOptionFail$ = createEffect(() => + this.actions$.pipe( + ofType(selectOptionActions.addSelectOptionFail), + tap(() => this.toastr.error('Failure to add select option', 'The new select option could not be added into the database')) + ), { dispatch: false} + ); + + editSelectOption$ = createEffect(() => + this.actions$.pipe( + ofType(selectOptionActions.editSelectOption), + mergeMap(action => this.selectOptionService.editSelectOption(action.selectOption) + .pipe( + map(selectOption => selectOptionActions.editSelectOptionSuccess({ selectOption })), + catchError(() => of(selectOptionActions.editSelectOptionFail())) + ) + ) + ) + ); + + editSelectOptionSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(selectOptionActions.editSelectOptionSuccess), + tap(() => this.toastr.success('Select option successfully edited', 'The existing select option has been edited into the database')) + ), { dispatch: false} + ); + + editSelectOptionFail$ = createEffect(() => + this.actions$.pipe( + ofType(selectOptionActions.editSelectOptionFail), + tap(() => this.toastr.error('Failure to edit select option', 'The existing select option could not be edited into the database')) + ), { dispatch: false} + ); + + deleteSelectOption$ = createEffect(() => + this.actions$.pipe( + ofType(selectOptionActions.deleteSelectOption), + mergeMap(action => this.selectOptionService.deleteSelectOption(action.selectOption.id) + .pipe( + map(() => selectOptionActions.deleteSelectOptionSuccess({ selectOption: action.selectOption })), + catchError(() => of(selectOptionActions.deleteSelectOptionFail())) + ) + ) + ) + ); + + deleteSelectOptionSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(selectOptionActions.deleteSelectOptionSuccess), + tap(() => this.toastr.success('Select option successfully deleted', 'The existing select option has been deleted')) + ), { dispatch: false} + ); + + deleteSelectOptionFail$ = createEffect(() => + this.actions$.pipe( + ofType(selectOptionActions.deleteSelectOptionFail), + tap(() => this.toastr.error('Failure to delete select option', 'The existing select option could not be deleted from the database')) + ), { dispatch: false} + ); + constructor( private actions$: Actions, - private selectOptionService: SelectOptionService + private selectOptionService: SelectOptionService, + private toastr: ToastrService ) {} } diff --git a/client/src/app/metamodel/store/effects/select.effects.ts b/client/src/app/metamodel/store/effects/select.effects.ts index 6d88ca12..f735b4e9 100644 --- a/client/src/app/metamodel/store/effects/select.effects.ts +++ b/client/src/app/metamodel/store/effects/select.effects.ts @@ -1,14 +1,15 @@ import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { of } from 'rxjs'; -import { map, mergeMap, catchError } from 'rxjs/operators'; +import { map, tap, mergeMap, catchError } from 'rxjs/operators'; + +import { ToastrService } from 'ngx-toastr'; import * as selectActions from '../actions/select.actions'; import { SelectService } from '../services/select.service'; @Injectable() export class SelectEffects { - loadSelects$ = createEffect(() => this.actions$.pipe( ofType(selectActions.loadSelectList), @@ -20,9 +21,88 @@ export class SelectEffects { ) ) ); + + addSelect$ = createEffect(() => + this.actions$.pipe( + ofType(selectActions.addSelect), + mergeMap(action => this.selectService.addSelect(action.select) + .pipe( + map(select => selectActions.addSelectSuccess({ select })), + catchError(() => of(selectActions.addSelectFail())) + ) + ) + ) + ); + + addSelectSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(selectActions.addSelectSuccess), + tap(() => this.toastr.success('Select successfully added', 'The new select was added into the database')) + ), { dispatch: false} + ); + + addSelectFail$ = createEffect(() => + this.actions$.pipe( + ofType(selectActions.addSelectFail), + tap(() => this.toastr.error('Failure to add select', 'The new select could not be added into the database')) + ), { dispatch: false} + ); + + editSelect$ = createEffect(() => + this.actions$.pipe( + ofType(selectActions.editSelect), + mergeMap(action => this.selectService.editSelect(action.select) + .pipe( + map(select => selectActions.editSelectSuccess({ select })), + catchError(() => of(selectActions.editSelectFail())) + ) + ) + ) + ); + + editSelectSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(selectActions.editSelectSuccess), + tap(() => this.toastr.success('Select successfully edited', 'The existing select has been edited into the database')) + ), { dispatch: false} + ); + + editSelectFail$ = createEffect(() => + this.actions$.pipe( + ofType(selectActions.editSelectFail), + tap(() => this.toastr.error('Failure to edit select', 'The existing select could not be edited into the database')) + ), { dispatch: false} + ); + + deleteSelect$ = createEffect(() => + this.actions$.pipe( + ofType(selectActions.deleteSelect), + mergeMap(action => this.selectService.deleteSelect(action.select.name) + .pipe( + map(() => selectActions.deleteSelectSuccess({ select: action.select })), + catchError(() => of(selectActions.deleteSelectFail())) + ) + ) + ) + ); + + deleteSelectSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(selectActions.deleteSelectSuccess), + tap(() => this.toastr.success('Select successfully deleted', 'The existing select has been deleted')) + ), { dispatch: false} + ); + + deleteSelectFail$ = createEffect(() => + this.actions$.pipe( + ofType(selectActions.deleteSelectFail), + tap(() => this.toastr.error('Failure to delete select', 'The existing select could not be deleted from the database')) + ), { dispatch: false} + ); constructor( private actions$: Actions, - private selectService: SelectService + private selectService: SelectService, + private toastr: ToastrService ) {} } diff --git a/client/src/app/metamodel/store/reducers/select-option.reducer.ts b/client/src/app/metamodel/store/reducers/select-option.reducer.ts index 1c7cc6b2..55eddafa 100644 --- a/client/src/app/metamodel/store/reducers/select-option.reducer.ts +++ b/client/src/app/metamodel/store/reducers/select-option.reducer.ts @@ -39,6 +39,15 @@ export const selectOptionReducer = createReducer( ...state, selectOptionListIsLoaded: false } + }), + on(selectOptionActions.addSelectOptionSuccess, (state, { selectOption }) => { + return adapter.addOne(selectOption, state) + }), + on(selectOptionActions.editSelectOptionSuccess, (state, { selectOption }) => { + return adapter.setOne(selectOption, state) + }), + on(selectOptionActions.deleteSelectOptionSuccess, (state, { selectOption }) => { + return adapter.removeOne(selectOption.id, state) }) ); diff --git a/client/src/app/metamodel/store/reducers/select.reducer.ts b/client/src/app/metamodel/store/reducers/select.reducer.ts index 779b7915..bef99bfd 100644 --- a/client/src/app/metamodel/store/reducers/select.reducer.ts +++ b/client/src/app/metamodel/store/reducers/select.reducer.ts @@ -42,6 +42,15 @@ export const selectReducer = createReducer( ...state, selectListIsLoaded: false } + }), + on(selectActions.addSelectSuccess, (state, { select }) => { + return adapter.addOne(select, state) + }), + on(selectActions.editSelectSuccess, (state, { select }) => { + return adapter.setOne(select, state) + }), + on(selectActions.deleteSelectSuccess, (state, { select }) => { + return adapter.removeOne(select.name, state) }) ); diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index d367b071..8cf0cf93 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ToastrModule } from 'ngx-toastr'; import { CollapseModule } from 'ngx-bootstrap/collapse'; import { BsDropdownModule } from 'ngx-bootstrap/dropdown'; import { ModalModule } from 'ngx-bootstrap/modal'; @@ -18,6 +19,7 @@ import { sharedComponents } from './components'; RouterModule, FormsModule, ReactiveFormsModule, + ToastrModule.forRoot(), CollapseModule.forRoot(), BsDropdownModule.forRoot(), ModalModule.forRoot() @@ -26,6 +28,7 @@ import { sharedComponents } from './components'; CommonModule, FormsModule, ReactiveFormsModule, + ToastrModule, CollapseModule, BsDropdownModule, ModalModule, diff --git a/client/src/styles.scss b/client/src/styles.scss index 5e2753dc..74dcd7b6 100644 --- a/client/src/styles.scss +++ b/client/src/styles.scss @@ -18,14 +18,28 @@ @import "~bootstrap/scss/dropdown"; @import "~bootstrap/scss/modal"; @import "~bootstrap/scss/tables"; +@import "~bootstrap/scss/forms"; @import "~bootstrap/scss/utilities"; +/* Import ngx-toastr bootstrap 4 alert styled design */ + +@import '~ngx-toastr/toastr-bs4-alert'; + /* Global styles */ main { margin-top: 100px; } +/* Angular forms */ +.ng-valid:not(form) { + border-left: 5px solid #42A948; /* green */ +} + +.ng-invalid:not(form) { + border-left: 5px solid #a94442; /* red */ +} + /* Utilities */ .pointer { -- GitLab