diff --git a/src/app/models/sample.model.ts b/src/app/models/sample.model.ts index c73acd0..319af04 100644 --- a/src/app/models/sample.model.ts +++ b/src/app/models/sample.model.ts @@ -13,7 +13,7 @@ export class SampleModel extends BaseModel { condition: {condition_template: string, [prop: string]: string} | {} = {}; material_id: IdModel = null; material: MaterialModel; - measurements: MeasurementModel[]; + measurements: MeasurementModel[] = []; note_id: IdModel = null; user_id: IdModel = null; notes: {comment: string, sample_references: {sample_id: IdModel, relation: string}[], custom_fields: {[prop: string]: string}} = {comment: '', sample_references: [], custom_fields: {}}; diff --git a/src/app/sample/sample.component.ts b/src/app/sample/sample.component.ts index 6f2919b..137e986 100644 --- a/src/app/sample/sample.component.ts +++ b/src/app/sample/sample.component.ts @@ -19,6 +19,9 @@ import {MeasurementModel} from '../models/measurement.model'; // TODO: confirmation for new group/supplier // TODO: DPT preview // TODO: work on better recognition for file input +// TODO: only show condition (if not set) and measurements in edit sample dialog at first +// TODO: multiple spectra, drag and drop +// TODO: multiple samples for base data, extend multiple measurements, conditions diff --git a/src/app/samples/samples.component.html b/src/app/samples/samples.component.html index 8e7f199..02f0a6a 100644 --- a/src/app/samples/samples.component.html +++ b/src/app/samples/samples.component.html @@ -6,48 +6,77 @@ -   Filter +   Filter -
- - - - + +
+ + + validated + + + new + +
+ + + + + + + - +
+ + - Number - Material number - Material name - Supplier - Material - GF - CF - M - type - Color - Batch + +
+ {{key.name}} + + +
+ {{sample.number}} - {{sample.material_number}} + {{materials[sample.material_id].name}} {{materials[sample.material_id].supplier}} - {{materials[sample.material_id].group}} - {{materials[sample.material_id].glass_fiber}} - {{materials[sample.material_id].carbon_fiber}} - {{materials[sample.material_id].mineral}} + + + + {{sample.type}} {{sample.color}} {{sample.batch}} + {{sample.added | date}}
+ + + + +
+ + + + of {{pages()}} + + +
+
+ diff --git a/src/app/samples/samples.component.scss b/src/app/samples/samples.component.scss index e2d94b5..c3fb393 100644 --- a/src/app/samples/samples.component.scss +++ b/src/app/samples/samples.component.scss @@ -22,3 +22,83 @@ color: #000; } } + +form { + overflow: hidden; +} + +.status-selection { + overflow: hidden; + margin-bottom: 10px; + float: left; + margin-right: 15px; + + label { + display: block; + font-weight: 700; + font-size: 10px; + } + + rb-form-checkbox { + float: left; + margin-right: 10px; + margin-top: -10px; + } +} + +.page-size-selection { + max-width: 125px; + float: left; +} + +.paging { + rb-form-input { + max-width: 50px; + } + + > * { + float: left; + } + + > button { + margin-top: 18px; + } + + > span { + margin-top: 20px; + margin-left: 5px; + } +} + +.sort-header { + display: inline-grid; + grid-template-columns: 1fr auto; + grid-column-gap: 5px; + width: 100%; + + :first-child { + grid-row: span 2; + } + + :nth-child(2) { + margin-bottom: -3px; + cursor: pointer; + } + + :nth-child(3) { + margin-top: -3px; + cursor: pointer; + } +} + +.sort-active-asc { + color: $color-bosch-dark-blue; + background: linear-gradient(to bottom, #FFF 17%, $color-bosch-light-blue-w50 17%);; + border-radius: 0 0 8px 8px; +} + +.sort-active-desc { + color: $color-bosch-dark-blue; + background: linear-gradient(to top, #FFF 17%, $color-bosch-light-blue-w50 17%);; + border-radius: 8px 8px 0 0; +} diff --git a/src/app/samples/samples.component.ts b/src/app/samples/samples.component.ts index a3bdb39..2384cbc 100644 --- a/src/app/samples/samples.component.ts +++ b/src/app/samples/samples.component.ts @@ -1,16 +1,46 @@ -import { Component, OnInit } from '@angular/core'; +import {Component, OnInit, ViewChild} from '@angular/core'; import {ApiService} from '../services/api.service'; + + +interface LoadSamplesOptions { + toPage?: number; + event?: Event; + firstPage?: boolean; +} + @Component({ selector: 'app-samples', templateUrl: './samples.component.html', styleUrls: ['./samples.component.scss'] }) + +// TODO: always show first page on sort change + export class SamplesComponent implements OnInit { // TODO: implement paging + @ViewChild('pageSizeSelection') pageSizeSelection: HTMLElement; + materials = {}; samples = []; - filters = {status: 'validated'}; + totalSamples = 0; // total number of samples + filters = {status: {new: true, validated: true}, pageSize: 25, toPage: 0, sort: 'added-asc'}; + page = 1; + loadSamplesQueue = []; // arguments of queued up loadSamples() calls + activeKeys = [ + {name: 'Number', key: 'number'}, + // {name: 'Material number', key: ''}, + {name: 'Material name', key: ''}, + {name: 'Supplier', key: ''}, + // {name: 'Material', key: ''}, + // {name: 'GF', key: ''}, + // {name: 'CF', key: ''}, + // {name: 'M', key: ''}, + {name: 'Type', key: 'type'}, + {name: 'Color', key: 'color'}, + {name: 'Batch', key: 'batch'}, + {name: 'Added', key: 'added'}, + ]; constructor( private api: ApiService @@ -24,15 +54,61 @@ export class SamplesComponent implements OnInit { // TODO: implement paging }); this.loadSamples(); }); + this.api.get('/samples/count', (data: {count: number}) => { + this.totalSamples = data.count; + }); } - loadSamples() { - this.api.get(`/samples?status=${this.filters.status}`, sData => { + loadSamples(options: LoadSamplesOptions = {}) { // set toPage to null to reload first page, queues calls + this.loadSamplesQueue.push(options); + if (this.loadSamplesQueue.length <= 1) { // nothing queued up + this.sampleLoader(this.loadSamplesQueue[0]); + } + } + + private sampleLoader(options: LoadSamplesOptions) { // actual loading of the sample, do not call directly + const query: string[] = []; + query.push('status=' + (this.filters.status.new && this.filters.status.validated ? 'all' : (this.filters.status.new ? 'new' : 'validated'))); + if (this.samples[0]) { // do not include from-id when page size was changed + if (!options.firstPage && (!options.event || ((options.event.target as HTMLElement).id.indexOf(this.pageSizeSelection.id) < 0))) { + query.push('from-id=' + this.samples[0]._id); + } + else { + this.page = 1; + } + } + if (options.toPage) { + query.push('to-page=' + options.toPage); + } + query.push('page-size=' + this.filters.pageSize); + query.push('sort=' + this.filters.sort); + + this.api.get('/samples?' + query.join('&'), sData => { this.samples = sData as any; this.samples.forEach(sample => { sample.material_number = this.materials[sample.material_id].numbers.find(e => sample.color === e.color).number; }); + this.loadSamplesQueue.shift(); + if (this.loadSamplesQueue.length > 0) { // execute next queue item + this.sampleLoader(this.loadSamplesQueue[0]); + } }); } + loadPage(delta) { + if (!/[0-9]+/.test(delta) || (this.page <= 1 && delta < 0)) { // invalid delta + return; + } + this.page += delta; + this.loadSamples({toPage: delta}); + } + + pages() { + return Math.ceil(this.totalSamples / this.filters.pageSize); + } + + setSort(string) { + this.filters.sort = string; + this.loadSamples({firstPage: true}); + } }