From 602dfb51daf7c417d14797b6319e5528ba9aba54 Mon Sep 17 00:00:00 2001 From: VLE2FE Date: Fri, 21 Aug 2020 16:11:57 +0200 Subject: [PATCH] added batch edit --- src/app/app.component.ts | 1 - src/app/models/measurement.model.ts | 1 + src/app/models/sample.model.ts | 13 +- src/app/prediction/prediction.component.html | 10 +- .../rb-array-input.component.ts | 33 +- src/app/sample/sample.component.html | 521 ++++++++++-------- src/app/sample/sample.component.scss | 14 + src/app/sample/sample.component.ts | 243 +++++--- src/app/samples/samples.component.html | 29 +- src/app/samples/samples.component.ts | 32 +- src/styles.scss | 4 +- 11 files changed, 558 insertions(+), 343 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index a7a0a78..f8836e5 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -36,7 +36,6 @@ export class AppComponent implements OnInit{ ngOnInit() { this.login.login().then(res => { - console.log(res); if (!res) { this.router.navigate(['/']); } diff --git a/src/app/models/measurement.model.ts b/src/app/models/measurement.model.ts index 8002bc5..0b8ed3d 100644 --- a/src/app/models/measurement.model.ts +++ b/src/app/models/measurement.model.ts @@ -8,6 +8,7 @@ export class MeasurementModel extends BaseModel { sample_id: IdModel = null; measurement_template: IdModel; values: {[prop: string]: any} = {}; + status = ''; constructor(measurementTemplate: IdModel = null) { super(); diff --git a/src/app/models/sample.model.ts b/src/app/models/sample.model.ts index 9172d3a..e1d3c86 100644 --- a/src/app/models/sample.model.ts +++ b/src/app/models/sample.model.ts @@ -17,7 +17,7 @@ export class SampleModel extends BaseModel { measurements: MeasurementModel[] = []; note_id: IdModel = null; user_id: IdModel = null; - validate = false; + selected = false; notes: { comment: string, sample_references: {sample_id: IdModel, relation: string}[], @@ -42,7 +42,16 @@ export class SampleModel extends BaseModel { } sendFormat() { - return pick(this.conditionTemplateCheck(), ['color', 'type', 'batch', 'condition', 'material_id', 'notes']); + const tmp = pick(this.conditionTemplateCheck(), ['color', 'type', 'batch', 'condition', 'material_id', 'notes']); + Object.keys(tmp).forEach(key => { + if (tmp[key] === undefined) { + delete tmp[key]; + } + }); + if (this.material && this.material.name === undefined) { + delete tmp.material_id; + } + return tmp; } private conditionTemplateCheck() { diff --git a/src/app/prediction/prediction.component.html b/src/app/prediction/prediction.component.html index 4082f25..6adb6f5 100644 --- a/src/app/prediction/prediction.component.html +++ b/src/app/prediction/prediction.component.html @@ -44,9 +44,13 @@
- - multiple samples - +
+ Prediction of: + + + + +
diff --git a/src/app/rb-custom-inputs/rb-array-input/rb-array-input.component.ts b/src/app/rb-custom-inputs/rb-array-input/rb-array-input.component.ts index f7b7d50..e27d579 100644 --- a/src/app/rb-custom-inputs/rb-array-input/rb-array-input.component.ts +++ b/src/app/rb-custom-inputs/rb-array-input/rb-array-input.component.ts @@ -129,13 +129,38 @@ export class RbArrayInputComponent implements ControlValueAccessor, OnInit, Afte } writeValue(obj: any) { // add empty value on init - this.values = obj ? obj : []; - if (this.values.length === 0 || this.values[0] !== '') { - // add empty last field if pushTemplate is specified + if (obj) { if (this.pushTemplate !== null) { - this.values.push(cloneDeep(this.pushTemplate)); + this.values = [...obj.filter(e => e[this.pushPath] !== ''), cloneDeep(this.pushTemplate)]; + } + else { + this.values = obj; } } + else { + if (this.pushTemplate !== null) { + this.values = [cloneDeep(this.pushTemplate)]; + } + else { + this.values = ['']; + } + } + // this.values = obj ? obj : []; + // console.log('-----'); + // console.log(obj); + // console.log(this.pushPath); + // if (this.values && this.values.length) { + // this.values = obj.filter(e => this.pushPath ? e[this.pushPath] !== '' : e !== ''); + // } + // console.log(this.values); + // // console.log(obj.filter(e => this.pushPath ? e[this.pushPath] !== '' : e !== '')); + // // this.values = obj ? obj.filter(e => this.pushPath ? e[this.pushPath] !== '' : e !== '') : []; + // if (this.values.length === 0 || this.values[0] !== '') { + // // add empty last field if pushTemplate is specified + // if (this.pushTemplate !== null) { + // this.values.push(cloneDeep(this.pushTemplate)); + // } + // } } registerOnChange(fn: any) { diff --git a/src/app/sample/sample.component.html b/src/app/sample/sample.component.html index 2500204..61de958 100644 --- a/src/app/sample/sample.component.html +++ b/src/app/sample/sample.component.html @@ -1,263 +1,324 @@ -

{{new ? 'Add new sample' : 'Edit sample ' + sample.number}}

+

{{mode === 'new' ? 'Add new sample' : 'Edit sample '}}

+ - + -
-
-
- - Cannot be empty - Unknown material, add properties for new material - - New material -
+ -
-

Material properties

- - {{supplierInput.errors.failure}} - - - {{groupInput.errors.failure}} - - - - The specified {{modalText.list}} could not be found in the list.
- Did you mean {{modalText.suggestion}}? -
-
- - - - - - - - {{parameterInput.errors.failure}} - Cannot be empty - -
+ -
- - - - Cannot be empty - - - {{colorInput.errors.failure}} - Cannot be empty - - - {{batchInput.errors.failure}} - -
-
- -
- - {{commentInput.errors.failure}} - -
Sample references
-
+ +
- - Unknown sample number + + Cannot be empty + Unknown material, add properties for new material + + New material +
- - Cannot be empty - -
-
Additional properties
- - -
- - {{keyInput.errors.failure}} - -
- + +
+

Material properties

+ + {{supplierInput.errors.failure}} + + + {{groupInput.errors.failure}} + + + + The specified {{modalText.list}} could not be found in the list.
+ Did you mean {{modalText.suggestion}}? +
+
+ + + + + + + + {{parameterInput.errors.failure}} Cannot be empty - - -
+
-
- -
- - - Cannot be empty - Must be at least 1 - - - - +
+ + + + Cannot be empty + + + {{colorInput.errors.failure}} + Cannot be empty + + + {{batchInput.errors.failure}} + +
+
+
+ + {{commentInput.errors.failure}} + +
Sample references
+
+
+ + Unknown sample number + +
+ + Cannot be empty + +
+
Additional properties
+ + +
+ + {{keyInput.errors.failure}} + +
+ + Cannot be empty + +
+
+
-
-
-

Successfully added samples:

- +
+ + Save sample + +
+ + + Cannot be empty + Must be at least 1 + + + + + + + +
+

Successfully added samples:

+ + - Material{{generatedSamples[0].material.name}} - Type{{generatedSamples[0].type}} - color{{generatedSamples[0].color}} - Batch{{generatedSamples[0].batch}} + Material{{material.name}} + Type{{baseSample.type}} + color{{baseSample.color}} + Batch{{baseSample.batch}} + Comment{{baseSample.notes.comment}} + + Sample reference{{reference[0]}} - {{reference[1]}} + + {{field[0]}}{{field[1]}}
-
-
-

{{gSample.number}}

-
-
- Condition - -
-
- - - + - - - - Cannot be empty +
+ + + +
+
+
+
+

{{sample.number}}

+
+
+ Condition + +
+
+ + - - {{parameterInput.errors.failure}} - Cannot be empty - - -
-
-
-
Measurements
-
- - - - -
- - - Cannot be empty - - - + + + Cannot be empty + [name]="'conditionParameter-' + gIndex + '-' + i" + [label]="parameter.name" appValidate="string" required + [(ngModel)]="sample.condition[parameter.name]" #parameterInput="ngModel"> {{parameterInput.errors.failure}} Cannot be empty - -
- - Delete measurement -
-   +
+
+ Measurements + + Restore measurements + +
+
+ + + -
- - New measurement - +
+ + + Cannot be empty + + + + Cannot be empty + + + {{parameterInput.errors.failure}} + Cannot be empty + + + + +
+ + Delete measurement + +
+
+ + New measurement + +
-
- - Save sample{{generatedSamples.length > 1 ? 's' : ''}} +
+ + Summary + + + Delete sample + + + + Do you really want to delete this sample? + + +
+ +
+ + + +
+ + + + + {{sample.number}} + + {{parameter.name}}{{sample.condition[parameter.name]}} + + + + {{parameter.name}}{{measurement.values[parameter.name]}} + + + + + + Save sample{{samples.length > 1 ? 's' : ''}} - - Delete sample - - - - Do you really want to delete {{sample.number}}? - - - -
+
+ diff --git a/src/app/sample/sample.component.scss b/src/app/sample/sample.component.scss index 8dbbd6a..5ec00c6 100644 --- a/src/app/sample/sample.component.scss +++ b/src/app/sample/sample.component.scss @@ -33,3 +33,17 @@ td:first-child { .delete-sample { float: right; } + +.cm-form { + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 1rem; + + rb-tab-panel, div.buttons { + grid-column: span 2; + } +} + +.restore-measurements { + float: right; +} diff --git a/src/app/sample/sample.component.ts b/src/app/sample/sample.component.ts index 057ca09..888b267 100644 --- a/src/app/sample/sample.component.ts +++ b/src/app/sample/sample.component.ts @@ -1,6 +1,7 @@ import cloneDeep from 'lodash/cloneDeep'; import merge from 'lodash/merge'; import omit from 'lodash/omit'; +import isEqual from 'lodash/isEqual'; import strCompare from 'str-compare'; import { AfterContentChecked, @@ -21,6 +22,7 @@ import {animate, style, transition, trigger} from '@angular/animations'; import {Observable} from 'rxjs'; import {ModalService} from '@inst-iot/bosch-angular-ui-components'; import {DataService} from '../services/data.service'; +import {LoginService} from '../services/login.service'; // TODO: additional property value not validated on edit @@ -48,9 +50,9 @@ export class SampleComponent implements OnInit, AfterContentChecked { @ViewChild('sampleForm') sampleForm: NgForm; @ViewChild('cmForm') cmForm: NgForm; - sample = new SampleModel(); // base sample which is saved + baseSample = new SampleModel(); // base sample which is saved sampleCount = 1; // number of samples to be generated - generatedSamples: SampleModel[] = []; // gets filled with response data after saving the sample + samples: SampleModel[] = []; // gets filled with response data after saving the sample sampleReferences: [string, string, string][] = [['', '', '']]; sampleReferenceFinds: {_id: string, number: string}[] = []; // raw sample reference data from db @@ -66,11 +68,20 @@ export class SampleComponent implements OnInit, AfterContentChecked { material = new MaterialModel(); // object of current selected material defaultDevice = ''; // default device for spectra - new; // true if new sample should be created - editSampleBase = false; // set true to edit sample base values even when generatedSamples .length > 0 + // component mode, either new for generating new samples, editOne or editMulti, editing one or multiple samples + mode = 'new'; + view = { // active views + base: false, // base sample + baseSum: false, // base sample summary + cm: false, // conditions and measurements + cmSum: false // conditions and measurements summary + }; loading = 0; // number of currently loading instances checkFormAfterInit = false; modalText = {list: '', suggestion: ''}; + cmSampleIndex = '0'; + measurementDeleteList = []; // buffer with measurements to delete, if the user confirms and saves the cm changes + measurementRestoreData: MeasurementModel[] = []; // deleted measurements if user is allowed and measurements are available charts = [[]]; // chart data for spectra readonly chartInit = [{ @@ -101,11 +112,12 @@ export class SampleComponent implements OnInit, AfterContentChecked { private validation: ValidationService, public autocomplete: AutocompleteService, private modal: ModalService, - public d: DataService + public d: DataService, + private login: LoginService ) { } ngOnInit(): void { - this.new = this.router.url === '/samples/new'; + this.mode = this.router.url === '/samples/new' ? 'new' : ''; this.loading = 7; this.d.load('materials', () => { this.materialNames = this.d.arr.materials.map(e => e.name); @@ -144,49 +156,88 @@ export class SampleComponent implements OnInit, AfterContentChecked { this.availableCustomFields = this.d.arr.sampleNotesFields.map(e => e.name); this.loading--; }); - if (!this.new) { - this.loading++; - this.api.get('/sample/' + this.route.snapshot.paramMap.get('id'), sData => { - this.sample.deserialize(sData); - this.generatedSamples[0] = this.sample; - this.charts = [[]]; - let spectrumCounter = 0; // generate charts for spectrum measurements - this.generatedSamples[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 ++; - } - }); - this.material = new MaterialModel().deserialize(sData.material); // read material - this.customFields = this.sample.notes.custom_fields && this.sample.notes.custom_fields !== {} ? // read custom fields - Object.keys(this.sample.notes.custom_fields).map(e => [e, this.sample.notes.custom_fields[e]]) : []; - if (this.sample.notes.sample_references.length) { // read sample references - this.sampleReferences = []; - this.sampleReferenceAutocomplete = []; - let loadCounter = this.sample.notes.sample_references.length; // count down instances still loading - this.sample.notes.sample_references.forEach(reference => { - this.api.get('/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 !== 'new') { + const sampleIds = this.route.snapshot.paramMap.get('id').split(','); + if (sampleIds.length === 1) { + this.mode = 'editOne'; + this.view.baseSum = true; + this.view.cm = true; + if (this.login.isLevel.dev) { // load measurement restore data + this.api.get('/measurement/sample/' + sampleIds[0], (data, ignore) => { + if (data) { + this.measurementRestoreData = data.filter(e => e.status === 'deleted').map(e => new MeasurementModel().deserialize(e)); + } + console.log(this.measurementRestoreData); }); } - this.loading--; - this.checkFormAfterInit = true; + } + else { + this.mode = 'editMulti'; + this.view.base = true; + } + this.loading += sampleIds.length; + this.samples = []; + sampleIds.forEach((sampleId, i) => { + this.api.get('/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('/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[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 { + ['type', 'color', 'batch', 'notes'].forEach((key) => { + console.log(isEqual(sData[key], this.baseSample[key])); + if (!isEqual(sData[key], this.baseSample[key])) { + this.baseSample[key] = undefined; + } + }); + if (!isEqual(sData.material.name, this.baseSample.material.name)) { + this.baseSample.material.name = undefined; + } + } + this.loading--; + }); }); } + else { + this.view.base = true; + } } ngAfterContentChecked() { - if (this.generatedSamples.length) { // conditions are displayed - this.generatedSamples.forEach((gSample, gIndex) => { + if (this.samples.length) { // conditions are displayed + 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); @@ -207,7 +258,7 @@ export class SampleComponent implements OnInit, AfterContentChecked { } if (this.checkFormAfterInit) { - if (this.editSampleBase) { // validate sampleForm + if (this.view.base) { // validate sampleForm if (this.sampleForm !== undefined && this.sampleForm.form.get('cf-key0')) { this.checkFormAfterInit = false; Object.keys(this.sampleForm.form.controls).forEach(field => { @@ -218,13 +269,13 @@ export class SampleComponent implements OnInit, AfterContentChecked { else { // validate cmForm // check that all fields are ready for validation let formReady: boolean = this.cmForm !== undefined; // forms exist - if (this.generatedSamples[0].condition.condition_template) { // if condition is set, last condition field exists + if (this.samples[0].condition.condition_template) { // if condition is set, last condition field exists formReady = formReady && this.cmForm.form.get('conditionParameter-0-' + - (this.d.id.conditionTemplates[this.generatedSamples[0].condition.condition_template].parameters.length - 1)) as any; + (this.d.id.conditionTemplates[this.samples[0].condition.condition_template].parameters.length - 1)) as any; } - if (this.generatedSamples[0].measurements.length) { // if there are measurements, last measurement field exists - formReady = formReady && this.cmForm.form.get('measurementParameter-0-' + (this.generatedSamples[0].measurements.length - 1) + - '-' + (this.d.id.measurementTemplates[this.generatedSamples[0].measurements[this.generatedSamples[0].measurements.length - 1] + if (this.samples[0].measurements.length) { // if there are measurements, last measurement field exists + formReady = formReady && this.cmForm.form.get('measurementParameter-0-' + (this.samples[0].measurements.length - 1) + + '-' + (this.d.id.measurementTemplates[this.samples[0].measurements[this.samples[0].measurements.length - 1] .measurement_template].parameters.length - 1)) as any; } if (formReady) { // fields are ready, do validation @@ -262,7 +313,7 @@ export class SampleComponent implements OnInit, AfterContentChecked { // save base sample saveSample() { - if (this.new) { + if (this.samples.length === 0) { this.loading = this.sampleCount; // set up loading spinner } new Promise(resolve => { @@ -271,7 +322,7 @@ export class SampleComponent implements OnInit, AfterContentChecked { this.api.post('/material/new', this.material.sendFormat(), data => { this.d.arr.materials.push(data); // add material to data this.material = data; - this.sample.material_id = data._id; // add new material id to sample data + this.baseSample.material_id = data._id; // add new material id to sample data resolve(); }); } @@ -279,29 +330,36 @@ export class SampleComponent implements OnInit, AfterContentChecked { resolve(); } }).then(() => { // save sample - this.sample.notes.custom_fields = {}; + this.baseSample.notes.custom_fields = {}; this.customFields.forEach(element => { if (element[0] !== '') { - this.sample.notes.custom_fields[element[0]] = element[1]; + this.baseSample.notes.custom_fields[element[0]] = element[1]; } }); - this.sample.notes.sample_references = this.sampleReferences + this.baseSample.notes.sample_references = this.sampleReferences .filter(e => e[0] && e[1] && e[2]) .map(e => ({sample_id: e[2], relation: e[1]})); - if (this.new) { + if (this.samples.length === 0) { // only save new sample for the first time in mode new, otherwise save changes for (let i = 0; i < this.sampleCount; i ++) { - this.api.post('/sample/new', this.sample.sendFormat(), data => { - this.generatedSamples[i] = new SampleModel().deserialize(data); - this.generatedSamples[i].material = this.d.arr.materials.find(e => e._id === this.generatedSamples[i].material_id); + this.api.post('/sample/new', this.baseSample.sendFormat(), data => { + this.samples[i] = new SampleModel().deserialize(data); + this.samples[i].material = this.d.arr.materials.find(e => e._id === this.samples[i].material_id); this.loading --; }); } + this.view.base = false; + this.view.baseSum = true; + this.view.cm = true; } else { - this.api.put('/sample/' + this.sample._id, this.sample.sendFormat(), data => { - merge(this.generatedSamples[0], omit(data, ['condition'])); - this.generatedSamples[0].material = this.d.arr.materials.find(e => e._id === this.generatedSamples[0].material_id); - this.editSampleBase = false; + this.samples.forEach((sample, i) => { + console.log(sample._id); + this.api.put('/sample/' + sample._id, this.baseSample.sendFormat(), data => { + merge(this.samples[i], omit(data, ['condition'])); + this.samples[i].material = this.d.arr.materials.find(e => e._id === this.samples[0].material_id); + this.view.base = false; + this.view.baseSum = true; + }); }); } }); @@ -309,7 +367,7 @@ export class SampleComponent implements OnInit, AfterContentChecked { // save conditions and measurements cmSave() { // save measurements and conditions - this.generatedSamples.forEach(sample => { + this.samples.forEach(sample => { if (sample.condition.condition_template) { // condition was set this.api.put('/sample/' + sample._id, {condition: sample.condition}); } @@ -331,24 +389,45 @@ export class SampleComponent implements OnInit, AfterContentChecked { this.api.delete('/measurement/' + measurement._id); } }); + this.measurementDeleteList.forEach(measurement => { + this.api.delete('/measurement/' + measurement); + }); }); this.router.navigate(['/samples']); } + restoreMeasurements() { + let spectrumCounter = 0; // generate charts for spectrum measurements + const measurementCount = this.samples[0].measurements.length; + this.measurementRestoreData.forEach((measurement, i) => { + this.api.put('/measurement/restore/' + measurement._id, {}, () => { + this.samples[0].measurements.push(measurement); + this.charts[0].push(cloneDeep(this.chartInit)); + if (measurement.values.dpt) { + setTimeout(() => { + this.generateChart(measurement.values.dpt, 0, measurementCount + i); + }, spectrumCounter * 20); // generate charts one after another to avoid freezing the UI + spectrumCounter ++; + } + this.checkFormAfterInit = true; + }); + }); + } + // set material based on found material name findMaterial(name) { const res = this.d.arr.materials.find(e => e.name === name); // search for match if (res) { // material found this.material = cloneDeep(res); - this.sample.material_id = this.material._id; + this.baseSample.material_id = this.material._id; } else { // no matching material found - if (this.sample.material_id !== null) { // reset previous match + if (this.baseSample.material_id !== null) { // reset previous match this.material = new MaterialModel(); this.material.properties.material_template = this.d.latest.materialTemplates.find(e => e.name === 'plastic')._id; } - this.sample.material_id = null; + this.baseSample.material_id = null; } this.setNewMaterial(); } @@ -356,9 +435,9 @@ export class SampleComponent implements OnInit, AfterContentChecked { // set newMaterial, if value === null -> toggle setNewMaterial(value = null) { if (value === null) { // toggle dialog - this.newMaterial = !this.sample.material_id; + this.newMaterial = !this.baseSample.material_id; } - else if (value || (!value && this.sample.material_id !== null )) { // set to false only if material already exists + else if (value || (!value && this.baseSample.material_id !== null )) { // set to false only if material already exists this.newMaterial = value; } if (this.newMaterial) { // set validators if dialog is open @@ -373,7 +452,7 @@ export class SampleComponent implements OnInit, AfterContentChecked { // add a new measurement for generated sample at index addMeasurement(gIndex) { - this.generatedSamples[gIndex].measurements.push( + this.samples[gIndex].measurements.push( new MeasurementModel(this.d.latest.measurementTemplates.find(e => e.name === 'spectrum')._id) ); if (!this.charts[gIndex]) { // add array if there are no charts yet @@ -383,18 +462,18 @@ export class SampleComponent implements OnInit, AfterContentChecked { } // remove the measurement at the specified index - removeMeasurement(gIndex, mIndex) { - if (this.generatedSamples[gIndex].measurements[mIndex]._id !== null) { - this.api.delete('/measurement/' + this.generatedSamples[gIndex].measurements[mIndex]._id); + removeMeasurement(gIndex, mIndex) { // TODO: do not delete directly but only after confirmation + if (this.samples[gIndex].measurements[mIndex]._id !== null) { + this.measurementDeleteList.push(this.samples[gIndex].measurements[mIndex]._id); } - this.generatedSamples[gIndex].measurements.splice(mIndex, 1); + this.samples[gIndex].measurements.splice(mIndex, 1); this.charts[gIndex].splice(mIndex, 1); } - // remove measurement data at the specified index + // clear entered measurement data at the specified index due to template change clearMeasurement(gIndex, mIndex) { this.charts[gIndex][mIndex][0].data = []; - this.generatedSamples[gIndex].measurements[mIndex].values = {}; + this.samples[gIndex].measurements[mIndex].values = {}; } fileToArray(files, gIndex, mIndex, parameter) { @@ -405,14 +484,14 @@ export class SampleComponent implements OnInit, AfterContentChecked { let index: number = mIndex; if (Number(i) > 0) { // append further spectra this.addMeasurement(gIndex); - index = this.generatedSamples[gIndex].measurements.length - 1; + index = this.samples[gIndex].measurements.length - 1; } - this.generatedSamples[gIndex].measurements[index].values.device = - this.generatedSamples[gIndex].measurements[mIndex].values.device; - this.generatedSamples[gIndex].measurements[index].values.filename = files[i].name; - this.generatedSamples[gIndex].measurements[index].values[parameter] = - fileReader.result.toString().split('\r\n').map(e => e.split(',')); - this.generateChart(this.generatedSamples[gIndex].measurements[index].values[parameter], gIndex, index); + this.samples[gIndex].measurements[index].values.device = + this.samples[gIndex].measurements[mIndex].values.device; + this.samples[gIndex].measurements[index].values.filename = files[i].name; + this.samples[gIndex].measurements[index].values[parameter] = + fileReader.result.toString().split('\r\n').map(e => e.split(',')).filter(el => el.length === 2); + this.generateChart(this.samples[gIndex].measurements[index].values[parameter], gIndex, index); }; fileReader.readAsText(files[i]); } @@ -452,7 +531,7 @@ export class SampleComponent implements OnInit, AfterContentChecked { deleteConfirm(modal) { this.modal.open(modal).then(result => { if (result) { - this.api.delete('/sample/' + this.sample._id); + this.api.delete('/sample/' + this.baseSample._id); this.router.navigate(['/samples']); } }); diff --git a/src/app/samples/samples.component.html b/src/app/samples/samples.component.html index ec43e4c..ec9a033 100644 --- a/src/app/samples/samples.component.html +++ b/src/app/samples/samples.component.html @@ -4,14 +4,16 @@ New sample - - + - {{validation ? 'Validate' : 'Validation'}} + {{sampleSelect === 2 ? 'Validate' : 'Validation'}}
+ +   Filter @@ -121,6 +123,8 @@
Loading...
+ +
JSON download link @@ -149,10 +153,12 @@
+ + - - all + + all
@@ -167,13 +173,16 @@
- + + + + - + + [(ngModel)]="sample.selected"> {{sample.number}} @@ -224,6 +233,8 @@
+ + diff --git a/src/app/samples/samples.component.ts b/src/app/samples/samples.component.ts index 8d995ca..08f5b19 100644 --- a/src/app/samples/samples.component.ts +++ b/src/app/samples/samples.component.ts @@ -9,6 +9,7 @@ import {LoginService} from '../services/login.service'; import {ModalService} from '@inst-iot/bosch-angular-ui-components'; import {DataService} from '../services/data.service'; import {LocalStorageService} from 'angular-2-local-storage'; +import {Router} from '@angular/router'; // TODO: turn off sort field // TODO reset sort when field is excluded @@ -63,6 +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: 'added', label: 'Added', active: false, autocomplete: [], mode: 'eq', values: ['']} ] }; @@ -78,6 +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', label: 'Notes', active: false, sortable: false}, {id: 'status', label: 'Status', active: false, sortable: true}, {id: 'added', label: 'Added', active: true, sortable: true} @@ -86,7 +89,7 @@ export class SamplesComponent implements OnInit { activeKeys: KeyInterface[] = []; activeTemplateKeys = {material: [], condition: [], measurements: []}; sampleDetailsSample: any = null; - validation = false; // true to activate validation mode + sampleSelect = 0; // modes: 0 - no selection, 1 - sample edit selection, 2 - validation selection loading = 0; @@ -97,7 +100,8 @@ export class SamplesComponent implements OnInit { private modalService: ModalService, public d: DataService, private storage: LocalStorageService, - private window: Window + private window: Window, + private router: Router ) { } @@ -285,7 +289,7 @@ export class SamplesComponent implements OnInit { } loadPage(delta) { - if (!/[0-9]+/.test(delta) || (this.page <= 1 && delta < 0)) { // invalid delta + if (!/[0-9]+/.test(delta) || this.page + delta < 1 || this.page + delta > this.pages) { // invalid delta return; } this.page += delta; @@ -414,17 +418,27 @@ export class SamplesComponent implements OnInit { } validate() { - if (this.validation) { + if (this.sampleSelect) { this.samples.forEach(sample => { - if (sample.validate) { + if (sample.selected) { this.api.put('/sample/validate/' + sample._id); } }); this.loadSamples(); - this.validation = false; + this.sampleSelect = 0; } else { - this.validation = true; + this.sampleSelect = 2; + } + } + + batchEdit() { + if (this.sampleSelect) { + this.router.navigate(['/samples/edit/' + this.samples.filter(e => e.selected).map(e => e._id).join(',')]); + this.sampleSelect = 0; + } + else { + this.sampleSelect = 1; } } @@ -442,10 +456,10 @@ export class SamplesComponent implements OnInit { selectAll(event) { this.samples.forEach(sample => { if (sample.status !== 'deleted') { - sample.validate = event.target.checked; + sample.selected = event.target.checked; } else { - sample.validate = false; + sample.selected = false; } }); } diff --git a/src/styles.scss b/src/styles.scss index 6ad32c2..fbc3cbc 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -7,9 +7,7 @@ $rb-extended-breakpoints: false; // whether to use extended breakpoints xxl and src: url(/assets/fonts/BoschMono.ttf); } -* { - margin: 0; - padding: 0; +*, ::after, ::before { box-sizing: border-box; }