definma-ui/src/app/samples/samples.component.ts

379 lines
14 KiB
TypeScript
Raw Normal View History

import {Component, ElementRef, isDevMode, OnInit, TemplateRef, ViewChild} from '@angular/core';
import {ApiService} from '../services/api.service';
2020-07-13 10:52:10 +02:00
import {AutocompleteService} from '../services/autocomplete.service';
2020-07-30 14:23:51 +02:00
import cloneDeep from 'lodash/cloneDeep';
import pick from 'lodash/pick';
2020-07-27 17:52:03 +02:00
import {SampleModel} from '../models/sample.model';
import {LoginService} from '../services/login.service';
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
import {DataService} from '../services/data.service';
2020-05-22 09:36:50 +02:00
2020-06-26 11:09:59 +02:00
interface LoadSamplesOptions {
toPage?: number;
event?: Event;
firstPage?: boolean;
}
2020-07-22 10:45:34 +02:00
interface KeyInterface {
id: string;
label: string;
active: boolean;
2020-07-27 17:52:03 +02:00
sortable: boolean;
2020-07-22 10:45:34 +02:00
}
2020-06-26 11:09:59 +02:00
2020-05-22 09:36:50 +02:00
@Component({
selector: 'app-samples',
templateUrl: './samples.component.html',
styleUrls: ['./samples.component.scss']
})
2020-06-26 11:09:59 +02:00
2020-05-22 09:36:50 +02:00
2020-07-13 10:52:10 +02:00
export class SamplesComponent implements OnInit {
2020-06-26 11:09:59 +02:00
2020-07-13 10:52:10 +02:00
@ViewChild('pageSizeSelection') pageSizeSelection: ElementRef<HTMLElement>;
@ViewChild('linkarea') linkarea: ElementRef<HTMLTextAreaElement>;
2020-08-10 12:34:14 +02:00
downloadSpectra = false;
downloadFlatten = true;
2020-07-27 17:52:03 +02:00
samples: SampleModel[] = [];
2020-06-26 11:09:59 +02:00
totalSamples = 0; // total number of samples
2020-07-13 10:52:10 +02:00
csvUrl = ''; // store url separate so it only has to be generated when clicking the download button
filters = {
status: {new: true, validated: true, deleted: false},
2020-07-13 10:52:10 +02:00
pageSize: 25,
toPage: 0,
sort: 'added-asc',
filters: [
{field: 'number', label: 'Number', active: false, autocomplete: [], mode: 'eq', values: ['']},
{field: 'material.name', label: 'Material name', active: false, autocomplete: [], mode: 'eq', values: ['']},
{field: 'material.supplier', label: 'Supplier', active: false, autocomplete: [], mode: 'eq', values: ['']},
{field: 'material.group', label: 'Material', active: false, autocomplete: [], mode: 'eq', values: ['']},
{field: 'material.glass_fiber', label: 'GF', active: false, autocomplete: [], mode: 'eq', values: ['']},
{field: 'material.carbon_fiber', label: 'CF', active: false, autocomplete: [], mode: 'eq', values: ['']},
{field: 'material.mineral', label: 'M', active: false, autocomplete: [], mode: 'eq', values: ['']},
{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: ['']},
2020-07-22 10:45:34 +02:00
{field: 'notes', label: 'Notes', active: false, autocomplete: [], mode: 'eq', values: ['']},
{field: 'added', label: 'Added', active: false, autocomplete: [], mode: 'eq', values: ['']}
2020-07-13 10:52:10 +02:00
]
};
2020-06-26 11:09:59 +02:00
page = 1;
2020-07-22 10:45:34 +02:00
pages = 1;
2020-06-26 11:09:59 +02:00
loadSamplesQueue = []; // arguments of queued up loadSamples() calls
2020-07-22 10:45:34 +02:00
keys: KeyInterface[] = [
2020-07-27 17:52:03 +02:00
{id: 'number', label: 'Number', active: true, sortable: true},
{id: 'material.numbers', label: 'Material numbers', active: true, sortable: false},
{id: 'material.name', label: 'Material name', active: true, sortable: true},
{id: 'material.supplier', label: 'Supplier', active: true, sortable: true},
{id: 'material.group', label: 'Material', active: false, sortable: true},
{id: 'type', label: 'Type', active: true, sortable: true},
{id: 'color', label: 'Color', active: true, sortable: true},
{id: 'batch', label: 'Batch', active: true, sortable: true},
{id: 'notes', label: 'Notes', active: false, sortable: false},
{id: 'status', label: 'Status', active: true, sortable: true},
{id: 'added', label: 'Added', active: true, sortable: true}
2020-06-26 11:09:59 +02:00
];
2020-07-22 10:45:34 +02:00
isActiveKey: {[key: string]: boolean} = {};
activeKeys: KeyInterface[] = [];
activeTemplateKeys = {material: [], measurements: []};
sampleDetailsSample: any = null;
validation = false; // true to activate validation mode
2020-05-22 12:52:17 +02:00
2020-07-13 10:52:10 +02:00
2020-05-22 12:52:17 +02:00
constructor(
2020-07-13 10:52:10 +02:00
private api: ApiService,
public autocomplete: AutocompleteService,
public login: LoginService,
private modalService: ModalService,
public d: DataService
2020-07-13 10:52:10 +02:00
) {
}
2020-05-22 09:36:50 +02:00
ngOnInit(): void {
2020-07-22 10:45:34 +02:00
this.calcFieldSelectKeys();
this.d.load('materials', () => {
this.filters.filters.find(e => e.field === 'material.name').autocomplete = this.d.arr.materials.map(e => e.name);
this.loadSamples();
});
this.d.load('materialSuppliers', () => {
this.filters.filters.find(e => e.field === 'material.supplier').autocomplete = this.d.arr.materialSuppliers;
2020-07-13 10:52:10 +02:00
});
this.d.load('materialGroups', () => {
this.filters.filters.find(e => e.field === 'material.group').autocomplete = this.d.arr.materialGroups;
2020-07-13 10:52:10 +02:00
});
this.d.load('userKey');
this.loadTemplateKeys('material', 'type');
this.loadTemplateKeys('measurement', 'status');
2020-07-22 10:45:34 +02:00
}
loadTemplateKeys(collection, insertBefore) {
this.d.load(collection + 'Templates', () => {
2020-07-22 10:45:34 +02:00
const templateKeys = [];
this.d.arr[collection + 'Templates'].forEach(item => {
2020-07-13 10:52:10 +02:00
item.parameters.forEach(parameter => {
2020-07-27 17:52:03 +02:00
const parameterName = encodeURIComponent(parameter.name);
// exclude spectrum and duplicates
if (parameter.name !== 'dpt' && !templateKeys.find(e => new RegExp('.' + parameterName + '$').test(e.id))) {
templateKeys.push({
id: `${collection === 'material' ? 'material.properties' : collection + 's.' + item.name}.${parameterName}`,
label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`,
active: false,
sortable: true
});
this.filters.filters.push({
field: `${collection === 'material' ? 'material.properties' : collection + 's.' + item.name}.${parameterName}`,
label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`,
active: false,
autocomplete: [],
mode: 'eq',
values: ['']
});
2020-07-27 17:52:03 +02:00
}
2020-07-13 10:52:10 +02:00
});
});
2020-07-22 10:45:34 +02:00
this.keys.splice(this.keys.findIndex(e => e.id === insertBefore), 0, ...templateKeys);
this.keys = [...this.keys]; // complete overwrite array to invoke update in rb-multiselect
this.updateActiveKeys();
this.calcFieldSelectKeys();
2020-06-26 11:09:59 +02:00
});
}
2020-07-22 10:45:34 +02:00
loadSamples(options: LoadSamplesOptions = {}, event = null) { // set toPage to null to reload first page, queues calls
if (event) { // adjust active keys
this.keys.forEach(key => {
if (event.hasOwnProperty(key.id)) {
key.active = event[key.id];
}
});
this.updateActiveKeys();
}
2020-06-26 11:09:59 +02:00
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
2020-07-13 10:52:10 +02:00
this.api.get(this.sampleUrl({paging: true, pagingOptions: options}), (sData, ignore, headers) => {
2020-07-22 10:45:34 +02:00
if (!options.toPage && headers['x-total-items']) {
2020-07-13 10:52:10 +02:00
this.totalSamples = headers['x-total-items'];
2020-06-26 11:09:59 +02:00
}
2020-07-22 10:45:34 +02:00
this.pages = Math.ceil(this.totalSamples / this.filters.pageSize);
this.samples = sData as any;
2020-06-26 11:09:59 +02:00
this.loadSamplesQueue.shift();
if (this.loadSamplesQueue.length > 0) { // execute next queue item
this.sampleLoader(this.loadSamplesQueue[0]);
}
2020-05-22 12:52:17 +02:00
});
2020-05-22 09:36:50 +02:00
}
sampleUrl(options: {
paging?: boolean,
pagingOptions?: {
firstPage?: boolean,
toPage?: number,
event?: Event
},
csv?: boolean,
export?: boolean,
host?: boolean
}) { // return url to fetch samples
const additionalTableKeys = ['material_id', '_id', 'user_id']; // keys which should always be added if export = false
2020-07-13 10:52:10 +02:00
const query: string[] = [];
query.push(...Object.keys(this.filters.status).filter(e => this.filters.status[e]).map(e => 'status[]=' + e));
2020-07-13 10:52:10 +02:00
if (options.paging) {
if (this.samples[0]) { // do not include from-id when page size was changed
if (!options.pagingOptions.firstPage) {
query.push('from-id=' + this.samples[0]._id);
}
else {
this.page = 1;
}
}
if (options.pagingOptions.toPage) {
query.push('to-page=' + options.pagingOptions.toPage);
}
query.push('page-size=' + this.filters.pageSize);
}
query.push('sort=' + this.filters.sort);
if (options.csv) {
2020-08-10 12:34:14 +02:00
query.push('output=csv');
2020-07-13 10:52:10 +02:00
}
if (options.export) {
query.push('key=' + this.d.d.userKey.key);
2020-07-13 10:52:10 +02:00
}
2020-07-22 10:45:34 +02:00
this.keys.forEach(key => {
// do not load material properties for table
if (key.active && (options.export || (!options.export && key.id.indexOf('material') < 0))) {
2020-07-22 10:45:34 +02:00
query.push('fields[]=' + key.id);
2020-07-13 10:52:10 +02:00
}
});
2020-07-30 14:23:51 +02:00
query.push(...cloneDeep(this.filters.filters)
2020-07-13 10:52:10 +02:00
.map(e => {
2020-07-22 10:45:34 +02:00
e.values = e.values.filter(el => el !== ''); // do not include empty values
if (e.field === 'added') { // correct timezone
2020-07-13 10:52:10 +02:00
e.values = e.values.map(el => new Date(new Date(el).getTime() - new Date(el).getTimezoneOffset() * 60000).toISOString());
}
return e;
})
2020-07-22 10:45:34 +02:00
.filter(e => e.active && e.values.length > 0)
2020-07-30 14:23:51 +02:00
.map(e => 'filters[]=' + encodeURIComponent(JSON.stringify(pick(e, ['mode', 'field', 'values']))))
2020-07-13 10:52:10 +02:00
);
if (!options.export) {
additionalTableKeys.forEach(key => {
if (query.indexOf('fields[]=' + key) < 0) { // add key if not already added
query.push('fields[]=' + key);
}
});
}
2020-08-10 12:34:14 +02:00
else {
if (this.downloadFlatten) {
query.push('output=flatten');
}
if (this.downloadSpectra) {
query.push('fields[]=measurements.spectrum.dpt');
}
2020-07-13 10:52:10 +02:00
}
return (options.host && isDevMode() ? window.location.host : '') +
(options.export ? this.api.hostName : '') +
'/samples?' + query.join('&');
2020-07-13 10:52:10 +02:00
}
2020-06-26 11:09:59 +02:00
loadPage(delta) {
if (!/[0-9]+/.test(delta) || (this.page <= 1 && delta < 0)) { // invalid delta
return;
}
this.page += delta;
this.loadSamples({toPage: delta});
}
2020-07-13 10:52:10 +02:00
updateFilterFields(field) {
const filter = this.filters.filters.find(e => e.field === field);
2020-07-27 17:52:03 +02:00
filter.active = true;
2020-07-13 10:52:10 +02:00
if (filter.active) {
this.loadSamples({firstPage: true});
}
}
2020-06-26 11:09:59 +02:00
setSort(string) {
this.filters.sort = string;
this.loadSamples({firstPage: true});
}
2020-07-13 10:52:10 +02:00
2020-07-22 10:45:34 +02:00
updateActiveKeys() { // array with all activeKeys
this.activeKeys = this.keys.filter(e => e.active);
this.activeTemplateKeys.material = this.keys
.filter(e => e.id.indexOf('material.properties.') >= 0 && e.active)
.map(e => e.id.split('.')
.map(el => decodeURIComponent(el)));
this.activeTemplateKeys.measurements = this.keys.filter(e => e.id.indexOf('measurements.') >= 0 && e.active)
.map(e => e.id.split('.')
2020-07-30 14:23:51 +02:00
.map(el => decodeURIComponent(el)));
2020-07-13 10:52:10 +02:00
}
2020-07-22 10:45:34 +02:00
calcFieldSelectKeys() {
this.keys.forEach(key => {
this.isActiveKey[key.id] = key.active;
});
2020-07-13 10:52:10 +02:00
}
// TODO: add measurements when ressource service is done
sampleDetails(id: string, modal: TemplateRef<any>) {
this.sampleDetailsSample = null;
this.api.get<SampleModel>('/sample/' + id, data => {
this.sampleDetailsSample = new SampleModel().deserialize(data);
if (data.notes.custom_fields) { // convert custom_fields for more optimized display
this.sampleDetailsSample.notes.custom_fields_entries = Object.entries(this.sampleDetailsSample.notes.custom_fields);
}
else {
this.sampleDetailsSample.custom_fields_entries = [];
}
this.sampleDetailsSample.measurement_entries = [];
this.sampleDetailsSample.measurements.forEach(measurement => { // convert measurements for more optimized display without dpt
const name = this.d.id.measurementTemplates[measurement.measurement_template].name;
this.sampleDetailsSample.measurement_entries.push(...Object.entries(measurement.values).filter(e => e[0] !== 'dpt')
.map(e => ({name: this.ucFirst(name) + ' ' + this.ucFirst(e[0]), value: e[1]})));
});
new Promise(resolve => {
if (data.notes.sample_references.length) { // load referenced samples if available
let loadingCounter = data.notes.sample_references.length;
this.sampleDetailsSample.notes.sample_references.forEach(reference => {
this.api.get<SampleModel>('/sample/' + reference.sample_id, rData => {
reference.number = rData.number;
loadingCounter --;
if (!loadingCounter) {
resolve();
}
});
});
}
else {
resolve();
}
}).then(() => {
this.modalService.open(modal).then(() => {});
});
});
}
validate() {
if (this.validation) {
this.samples.forEach(sample => {
if (sample.validate) {
this.api.put('/sample/validate/' + sample._id);
}
});
this.loadSamples();
this.validation = false;
}
else {
this.validation = true;
}
}
restoreSample(id, modal, event) {
this.stopPropagation(event);
this.modalService.open(modal).then(res => {
if (res) {
this.api.put('/sample/restore/' + id, {}, ignore => {
this.samples.find(e => e._id === id).status = 'new';
});
}
});
}
selectAll(event) {
this.samples.forEach(sample => {
if (sample.status !== 'deleted') {
sample.validate = event.target.checked;
}
else {
sample.validate = false;
}
});
}
2020-07-13 10:52:10 +02:00
preventDefault(event, key = 'all') {
if (key === 'all' || event.key === key) {
event.preventDefault();
}
}
stopPropagation(event) {
event.stopPropagation();
}
2020-07-13 10:52:10 +02:00
clipboard() {
this.linkarea.nativeElement.select();
this.linkarea.nativeElement.setSelectionRange(0, 99999);
document.execCommand('copy');
}
ucFirst(string) {
return string[0].toUpperCase() + string.slice(1);
}
2020-05-22 09:36:50 +02:00
}