Merge pull request #26 in ~VLE2FE/definma-ui from development to master
* commit 'bf02c1b99ec50f665bfece94e451b7954dd281a6': added required parameter range added notes.comments field
This commit is contained in:
		@@ -37,7 +37,9 @@
 | 
			
		||||
  </ng-container>
 | 
			
		||||
 | 
			
		||||
  <div *rbSubBrandHeader>
 | 
			
		||||
    <rb-icon-button icon="bug" mode="secondary" class="space-right" [rbModal]="bugModal">Bug</rb-icon-button>
 | 
			
		||||
    <rb-icon-button icon="bug" mode="secondary" class="space-right" [rbModal]="bugModal" *ngIf="false">
 | 
			
		||||
      Bug
 | 
			
		||||
    </rb-icon-button>
 | 
			
		||||
    <ng-template let-close="close" #bugModal>
 | 
			
		||||
      <h3>Report a bug</h3>
 | 
			
		||||
      <rb-form-textarea class="bug-textarea" label="What did you do?" [(ngModel)]="bugReport.do"></rb-form-textarea>
 | 
			
		||||
@@ -46,7 +48,7 @@
 | 
			
		||||
        <rb-icon-button icon="mail" mode="primary" (mouseup)="closeBugReport(close)">Send report</rb-icon-button>
 | 
			
		||||
      </a>
 | 
			
		||||
    </ng-template>
 | 
			
		||||
    <span class="dev-label" *ngIf="devMode">DEVELOPMENT</span>
 | 
			
		||||
    <span class="dev-label" *ngIf="devMode && false">DEVELOPMENT</span>
 | 
			
		||||
    DeFinMa
 | 
			
		||||
  </div>
 | 
			
		||||
</rb-full-header>
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,3 @@
 | 
			
		||||
<app-login></app-login>
 | 
			
		||||
<app-login *ngIf="!login.isLoggedIn"></app-login>
 | 
			
		||||
 | 
			
		||||
<img src="/assets/imgs/key-visual.png" alt="" class="key-visual">
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
app-login {
 | 
			
		||||
  float: left;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.key-visual {
 | 
			
		||||
  width: 70%;
 | 
			
		||||
  float: right;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import { Component, OnInit } from '@angular/core';
 | 
			
		||||
import {LoginService} from '../services/login.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-home',
 | 
			
		||||
@@ -7,7 +8,9 @@ import { Component, OnInit } from '@angular/core';
 | 
			
		||||
})
 | 
			
		||||
export class HomeComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  constructor() { }
 | 
			
		||||
  constructor(
 | 
			
		||||
    public login: LoginService
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -139,6 +139,10 @@
 | 
			
		||||
                      [disabled]="!sampleForm.form.valid">
 | 
			
		||||
        Save sample
 | 
			
		||||
      </rb-icon-button>
 | 
			
		||||
      <rb-icon-button class="delete-sample" icon="delete" mode="danger" *ngIf="samples.length > 1"
 | 
			
		||||
                      (click)="deleteConfirm(modalDeleteConfirm)">
 | 
			
		||||
        Delete samples
 | 
			
		||||
      </rb-icon-button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <ng-template #generateSamples>
 | 
			
		||||
      <rb-form-input type="number" name="sample-count" label="number of samples" pattern="^\d+?$" required
 | 
			
		||||
@@ -285,15 +289,10 @@
 | 
			
		||||
                        [disabled]="!cmForm.form.valid">
 | 
			
		||||
          Summary
 | 
			
		||||
        </rb-icon-button>
 | 
			
		||||
        <rb-icon-button class="delete-sample" icon="delete" mode="danger" *ngIf="mode !== 'new'"
 | 
			
		||||
        <rb-icon-button class="delete-sample" icon="delete" mode="danger" *ngIf="samples.length === 1"
 | 
			
		||||
                        (click)="deleteConfirm(modalDeleteConfirm)">
 | 
			
		||||
          Delete sample
 | 
			
		||||
        </rb-icon-button>
 | 
			
		||||
        <ng-template #modalDeleteConfirm>
 | 
			
		||||
          <rb-alert alertTitle="Are you sure?" type="danger" okBtnLabel="Delete sample" cancelBtnLabel="Cancel">
 | 
			
		||||
            Do you really want to delete this sample?
 | 
			
		||||
          </rb-alert>
 | 
			
		||||
        </ng-template>
 | 
			
		||||
      </div>
 | 
			
		||||
    </form>
 | 
			
		||||
  </div>
 | 
			
		||||
@@ -322,3 +321,9 @@
 | 
			
		||||
    </rb-icon-button>
 | 
			
		||||
  </div>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
<ng-template #modalDeleteConfirm>
 | 
			
		||||
  <rb-alert alertTitle="Are you sure?" type="danger" [okBtnLabel]="'Delete sample' + (samples.length > 1 ? 's' : '')" cancelBtnLabel="Cancel">
 | 
			
		||||
    Do you really want to delete {{samples.length > 1 ? 'these samples' : 'this sample'}}?
 | 
			
		||||
  </rb-alert>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 
 | 
			
		||||
@@ -176,57 +176,56 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
			
		||||
        this.view.base = true;
 | 
			
		||||
      }
 | 
			
		||||
      this.loading += sampleIds.length;
 | 
			
		||||
      this.samples = [];
 | 
			
		||||
      sampleIds.forEach((sampleId, i) => {
 | 
			
		||||
        this.api.get<SampleModel>('/sample/' + sampleId, sData => {
 | 
			
		||||
          this.samples.push(new SampleModel().deserialize(sData));
 | 
			
		||||
          if (i === 0) {
 | 
			
		||||
            this.baseSample.deserialize(sData);
 | 
			
		||||
            this.material = new MaterialModel().deserialize(sData.material);  // read material
 | 
			
		||||
            this.customFields = this.baseSample.notes.custom_fields && this.baseSample.notes.custom_fields !== {} ?  // read custom fields
 | 
			
		||||
              Object.keys(this.baseSample.notes.custom_fields).map(e => [e, this.baseSample.notes.custom_fields[e]]) : [];
 | 
			
		||||
            if (this.baseSample.notes.sample_references.length) {  // read sample references
 | 
			
		||||
              this.sampleReferences = [];
 | 
			
		||||
              this.sampleReferenceAutocomplete = [];
 | 
			
		||||
              let loadCounter = this.baseSample.notes.sample_references.length;  // count down instances still loading
 | 
			
		||||
              this.baseSample.notes.sample_references.forEach(reference => {
 | 
			
		||||
                this.api.get<SampleModel>('/sample/' + reference.sample_id, srData => {  // get sample numbers for ids
 | 
			
		||||
                  this.sampleReferences.push([srData.number, reference.relation, reference.sample_id]);
 | 
			
		||||
                  this.sampleReferenceAutocomplete.push([srData.number]);
 | 
			
		||||
                  if (!--loadCounter) {  // insert empty template when all instances were loaded
 | 
			
		||||
                    this.sampleReferences.push(['', '', '']);
 | 
			
		||||
                    this.sampleReferenceAutocomplete.push([]);
 | 
			
		||||
                  }
 | 
			
		||||
                });
 | 
			
		||||
              });
 | 
			
		||||
      this.api.get<SampleModel>('/sample/' + sampleIds[0], sData => {  // special treatment for first id
 | 
			
		||||
        this.samples = [new SampleModel().deserialize(sData)];
 | 
			
		||||
        this.baseSample.deserialize(sData);
 | 
			
		||||
        this.material = new MaterialModel().deserialize(sData.material);  // read material
 | 
			
		||||
        this.customFields = this.baseSample.notes.custom_fields && this.baseSample.notes.custom_fields !== {} ?  // read custom fields
 | 
			
		||||
          Object.keys(this.baseSample.notes.custom_fields).map(e => [e, this.baseSample.notes.custom_fields[e]]) : [];
 | 
			
		||||
        if (this.baseSample.notes.sample_references.length) {  // read sample references
 | 
			
		||||
          this.sampleReferences = [];
 | 
			
		||||
          this.sampleReferenceAutocomplete = [];
 | 
			
		||||
          let loadCounter = this.baseSample.notes.sample_references.length;  // count down instances still loading
 | 
			
		||||
          this.baseSample.notes.sample_references.forEach(reference => {
 | 
			
		||||
            this.api.get<SampleModel>('/sample/' + reference.sample_id, srData => {  // get sample numbers for ids
 | 
			
		||||
              this.sampleReferences.push([srData.number, reference.relation, reference.sample_id]);
 | 
			
		||||
              this.sampleReferenceAutocomplete.push([srData.number]);
 | 
			
		||||
              if (!--loadCounter) {  // insert empty template when all instances were loaded
 | 
			
		||||
                this.sampleReferences.push(['', '', '']);
 | 
			
		||||
                this.sampleReferenceAutocomplete.push([]);
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
        if (this.mode === 'editOne') {
 | 
			
		||||
          this.charts = [[]];
 | 
			
		||||
          let spectrumCounter = 0;  // generate charts for spectrum measurements
 | 
			
		||||
          this.samples[0].measurements.forEach((measurement, i) => {
 | 
			
		||||
            this.charts[0].push(cloneDeep(this.chartInit));
 | 
			
		||||
            if (measurement.values.dpt) {
 | 
			
		||||
              setTimeout(() => {
 | 
			
		||||
                this.generateChart(measurement.values.dpt, 0, i);
 | 
			
		||||
              }, spectrumCounter * 20);  // generate charts one after another to avoid freezing the UI
 | 
			
		||||
              spectrumCounter ++;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.mode === 'editOne') {
 | 
			
		||||
              this.charts = [[]];
 | 
			
		||||
              let spectrumCounter = 0;  // generate charts for spectrum measurements
 | 
			
		||||
              this.samples[i].measurements.forEach((measurement, j) => {
 | 
			
		||||
                this.charts[i].push(cloneDeep(this.chartInit));
 | 
			
		||||
                if (measurement.values.dpt) {
 | 
			
		||||
                  setTimeout(() => {
 | 
			
		||||
                    this.generateChart(measurement.values.dpt, 0, j);
 | 
			
		||||
                  }, spectrumCounter * 20);  // generate charts one after another to avoid freezing the UI
 | 
			
		||||
                  spectrumCounter ++;
 | 
			
		||||
                }
 | 
			
		||||
              });
 | 
			
		||||
            }
 | 
			
		||||
            this.checkFormAfterInit = true;
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
        this.loading--;
 | 
			
		||||
        sampleIds.slice(1).forEach(sampleId => {
 | 
			
		||||
          this.api.get<SampleModel>('/sample/' + sampleId, data => {
 | 
			
		||||
            this.samples.push(new SampleModel().deserialize(data));
 | 
			
		||||
            ['type', 'color', 'batch', 'notes'].forEach((key) => {
 | 
			
		||||
              console.log(isEqual(sData[key], this.baseSample[key]));
 | 
			
		||||
              if (!isEqual(sData[key], this.baseSample[key])) {
 | 
			
		||||
              console.log(isEqual(data[key], this.baseSample[key]));
 | 
			
		||||
              if (!isEqual(data[key], this.baseSample[key])) {
 | 
			
		||||
                this.baseSample[key] = undefined;
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
            if (!isEqual(sData.material.name, this.baseSample.material.name)) {
 | 
			
		||||
            if (!isEqual(data.material.name, this.baseSample.material.name)) {
 | 
			
		||||
              this.baseSample.material.name = undefined;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          this.loading--;
 | 
			
		||||
            this.loading--;
 | 
			
		||||
            this.checkFormAfterInit = true;
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
@@ -240,12 +239,12 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
			
		||||
      this.samples.forEach((gSample, gIndex) => {
 | 
			
		||||
        if (this.d.id.conditionTemplates[gSample.condition.condition_template]) {
 | 
			
		||||
          this.d.id.conditionTemplates[gSample.condition.condition_template].parameters.forEach((parameter, pIndex) => {
 | 
			
		||||
            this.attachValidator(this.cmForm, `conditionParameter-${gIndex}-${pIndex}`, parameter.range, true);
 | 
			
		||||
            this.attachValidator(this.cmForm, `conditionParameter-${gIndex}-${pIndex}`, parameter.range);
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
        gSample.measurements.forEach((measurement, mIndex) => {
 | 
			
		||||
          this.d.id.measurementTemplates[measurement.measurement_template].parameters.forEach((parameter, pIndex) => {
 | 
			
		||||
            this.attachValidator(this.cmForm, `measurementParameter-${gIndex}-${mIndex}-${pIndex}`, parameter.range, false);
 | 
			
		||||
            this.attachValidator(this.cmForm, `measurementParameter-${gIndex}-${mIndex}-${pIndex}`, parameter.range);
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
@@ -253,7 +252,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
			
		||||
 | 
			
		||||
    if (this.sampleForm && this.material.properties.material_template) {  // material template is set
 | 
			
		||||
      this.d.id.materialTemplates[this.material.properties.material_template].parameters.forEach((parameter, i) => {
 | 
			
		||||
        this.attachValidator(this.sampleForm, 'materialParameter' + i, parameter.range, true);
 | 
			
		||||
        this.attachValidator(this.sampleForm, 'materialParameter' + i, parameter.range);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -289,10 +288,10 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // attach validators specified in range to input with name
 | 
			
		||||
  attachValidator(form, name: string, range: {[prop: string]: any}, required: boolean) {
 | 
			
		||||
  attachValidator(form, name: string, range: {[prop: string]: any}) {
 | 
			
		||||
    if (form && form.form.get(name)) {
 | 
			
		||||
      const validators = [];
 | 
			
		||||
      if (required) {
 | 
			
		||||
      if (range.hasOwnProperty('required')) {
 | 
			
		||||
        validators.push(Validators.required);
 | 
			
		||||
      }
 | 
			
		||||
      if (range.hasOwnProperty('values')) {
 | 
			
		||||
@@ -531,7 +530,9 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
			
		||||
  deleteConfirm(modal) {
 | 
			
		||||
    this.modal.open(modal).then(result => {
 | 
			
		||||
      if (result) {
 | 
			
		||||
        this.api.delete('/sample/' + this.baseSample._id);
 | 
			
		||||
        this.samples.forEach(sample => {
 | 
			
		||||
          this.api.delete('/sample/' + sample._id);
 | 
			
		||||
        });
 | 
			
		||||
        this.router.navigate(['/samples']);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -199,6 +199,7 @@
 | 
			
		||||
    <td *ngFor="let key of activeTemplateKeys.condition">
 | 
			
		||||
      {{sample.condition ? sample.condition[key[1]] : '' | exists}}
 | 
			
		||||
    </td>
 | 
			
		||||
    <td *ngIf="isActiveKey['notes.comment']">{{sample.notes | exists: 'comment'}}</td>
 | 
			
		||||
    <td *ngIf="isActiveKey['notes']">{{sample.notes | object: ['_id', 'sample_references']}}</td>
 | 
			
		||||
    <td *ngFor="let key of activeTemplateKeys.measurements">{{sample[key[1]] | exists: key[2]}}</td>
 | 
			
		||||
    <td *ngIf="isActiveKey['status']">{{sample.status}}</td>
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ export class SamplesComponent implements OnInit {
 | 
			
		||||
      {field: 'type', label: 'Type', active: false, autocomplete: [], mode: 'eq', values: ['']},
 | 
			
		||||
      {field: 'color', label: 'Color', active: false, autocomplete: [], mode: 'eq', values: ['']},
 | 
			
		||||
      {field: 'batch', label: 'Batch', active: false, autocomplete: [], mode: 'eq', values: ['']},
 | 
			
		||||
      // {field: 'notes.comment', label: 'Comment', active: false, autocomplete: [], mode: 'eq', values: ['']},
 | 
			
		||||
      {field: 'notes.comment', label: 'Comment', active: false, autocomplete: [], mode: 'eq', values: ['']},
 | 
			
		||||
      {field: 'added', label: 'Added', active: false, autocomplete: [], mode: 'eq', values: ['']}
 | 
			
		||||
    ]
 | 
			
		||||
  };
 | 
			
		||||
@@ -80,7 +80,7 @@ export class SamplesComponent implements OnInit {
 | 
			
		||||
    {id: 'type', label: 'Type', active: true, sortable: true},
 | 
			
		||||
    {id: 'color', label: 'Color', active: false, sortable: true},
 | 
			
		||||
    {id: 'batch', label: 'Batch', active: true, sortable: true},
 | 
			
		||||
    // {id: 'notes.comment', label: 'Comment', active: false, sortable: false},
 | 
			
		||||
    {id: 'notes.comment', label: 'Comment', active: false, sortable: false},
 | 
			
		||||
    {id: 'notes', label: 'Notes', active: false, sortable: false},
 | 
			
		||||
    {id: 'status', label: 'Status', active: false, sortable: true},
 | 
			
		||||
    {id: 'added', label: 'Added', active: true, sortable: true}
 | 
			
		||||
@@ -129,7 +129,7 @@ export class SamplesComponent implements OnInit {
 | 
			
		||||
    this.d.load('userKey', onLoad);
 | 
			
		||||
    this.d.load('conditionTemplates', onLoad);
 | 
			
		||||
    this.loadTemplateKeys('material', 'type', onLoad);
 | 
			
		||||
    this.loadTemplateKeys('condition', 'notes', onLoad);
 | 
			
		||||
    this.loadTemplateKeys('condition', 'notes.comment', onLoad);
 | 
			
		||||
    this.loadTemplateKeys('measurement', 'status', onLoad);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -177,6 +177,10 @@ export class SamplesComponent implements OnInit {
 | 
			
		||||
          key.active = event[key.id];
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      const sortId = this.filters.sort.replace(/(-asc|-desc)/, '');
 | 
			
		||||
      if (event.hasOwnProperty(sortId) && !event[sortId]) {  // reset sort if sort field was unselected
 | 
			
		||||
        this.setSort('_id-asc');
 | 
			
		||||
      }
 | 
			
		||||
      this.updateActiveKeys();
 | 
			
		||||
    }
 | 
			
		||||
    this.loadSamplesQueue.push(options);
 | 
			
		||||
@@ -254,8 +258,8 @@ export class SamplesComponent implements OnInit {
 | 
			
		||||
          e.values = e.values.map(el => new Date(new Date(el).getTime() - new Date(el).getTimezoneOffset() * 60000).toISOString());
 | 
			
		||||
        }
 | 
			
		||||
        if (e.mode === 'null') {
 | 
			
		||||
          e.mode = 'eq';
 | 
			
		||||
          e.values[0] = null;
 | 
			
		||||
          e.mode = 'in';
 | 
			
		||||
          e.values = [null, ''];
 | 
			
		||||
        }
 | 
			
		||||
        return e;
 | 
			
		||||
      })
 | 
			
		||||
@@ -346,7 +350,7 @@ export class SamplesComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  updateActiveKeys() {  // array with all activeKeys
 | 
			
		||||
    this.activeKeys = this.keys.filter(e => e.active);
 | 
			
		||||
    this.filters.filters.forEach(filter => {
 | 
			
		||||
    this.filters.filters.forEach(filter => {  // disable filters of fields not displayed
 | 
			
		||||
      if (!this.isActiveKey[filter.field]) {
 | 
			
		||||
        filter.active = false;
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,9 @@ export class LoginService implements CanActivate {
 | 
			
		||||
  logout() {
 | 
			
		||||
    this.storage.remove('basicAuth');
 | 
			
		||||
    this.loggedIn = false;
 | 
			
		||||
    this.levels.forEach(level => {
 | 
			
		||||
      this.isLevel[level] = false;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  canActivate(route: ActivatedRouteSnapshot = null, state: RouterStateSnapshot = null): Observable<boolean> {
 | 
			
		||||
 
 | 
			
		||||
@@ -153,13 +153,12 @@ export class ValidationService {
 | 
			
		||||
          max: Joi.number(),
 | 
			
		||||
 | 
			
		||||
          type: Joi.string()
 | 
			
		||||
            .valid('array')
 | 
			
		||||
            .valid('string', 'number', 'boolean', 'array'),
 | 
			
		||||
 | 
			
		||||
          required: Joi.boolean()
 | 
			
		||||
        })
 | 
			
		||||
          .oxor('values', 'min')
 | 
			
		||||
          .oxor('values', 'max')
 | 
			
		||||
          .oxor('type', 'values')
 | 
			
		||||
          .oxor('type', 'min')
 | 
			
		||||
          .oxor('type', 'max')
 | 
			
		||||
          .required()
 | 
			
		||||
          .validate(JSON.parse(data));
 | 
			
		||||
        if (error) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								src/assets/imgs/key-visual.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/imgs/key-visual.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 394 KiB  | 
@@ -42,3 +42,7 @@ button::-moz-focus-inner {
 | 
			
		||||
.space-left {
 | 
			
		||||
  margin-left: $default-spacing;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.supergraphic {
 | 
			
		||||
  background-image: url("/assets/imgs/supergraphic.svg");
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user