2020-07-13 10:52:10 +02:00
|
|
|
import {Component, ElementRef, isDevMode, OnInit, ViewChild} from '@angular/core';
|
2020-06-19 08:43:22 +02:00
|
|
|
import {ApiService} from '../services/api.service';
|
2020-07-13 10:52:10 +02:00
|
|
|
import {AutocompleteService} from '../services/autocomplete.service';
|
|
|
|
import _ from 'lodash';
|
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-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-07-22 10:45:34 +02:00
|
|
|
// TODO: manage branches, introduce versioning, only upload ui from master
|
|
|
|
// TODO: check if custom-header.conf works, add headers from helmet https://docs.cloudfoundry.org/buildpacks/staticfile/index.html
|
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>;
|
|
|
|
|
|
|
|
downloadCsv = false;
|
2020-05-22 12:52:17 +02:00
|
|
|
materials = {};
|
|
|
|
samples = [];
|
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},
|
|
|
|
pageSize: 25,
|
|
|
|
toPage: 0,
|
|
|
|
sort: 'added-asc',
|
|
|
|
filters: [
|
|
|
|
{field: 'number', label: 'Number', active: false, autocomplete: [], mode: 'eq', values: ['']},
|
|
|
|
{field: 'material.number', label: 'Material 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: ['']},
|
2020-07-13 10:52:10 +02:00
|
|
|
{field: 'added', label: 'Added', active: false, autocomplete: [], mode: 'eq', values: [new Date()]}
|
|
|
|
]
|
|
|
|
};
|
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-13 10:52:10 +02:00
|
|
|
apiKey = '';
|
2020-07-22 10:45:34 +02:00
|
|
|
keys: KeyInterface[] = [
|
|
|
|
{id: 'number', label: 'Number', active: true},
|
|
|
|
{id: 'material.numbers', label: 'Material numbers', active: true},
|
|
|
|
{id: 'material.name', label: 'Material name', active: true},
|
|
|
|
{id: 'material.supplier', label: 'Supplier', active: true},
|
|
|
|
{id: 'material.group', label: 'Material', active: false},
|
|
|
|
{id: 'type', label: 'Type', active: true},
|
|
|
|
{id: 'color', label: 'Color', active: true},
|
|
|
|
{id: 'batch', label: 'Batch', active: true},
|
|
|
|
{id: 'notes', label: 'Notes', active: false},
|
|
|
|
{id: 'added', label: 'Added', active: 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: []};
|
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
|
|
|
|
) {
|
|
|
|
}
|
2020-05-22 09:36:50 +02:00
|
|
|
|
|
|
|
ngOnInit(): void {
|
2020-07-22 10:45:34 +02:00
|
|
|
this.calcFieldSelectKeys();
|
2020-06-19 08:43:22 +02:00
|
|
|
this.api.get('/materials?status=all', (mData: any) => {
|
2020-05-22 12:52:17 +02:00
|
|
|
this.materials = {};
|
|
|
|
mData.forEach(material => {
|
|
|
|
this.materials[material._id] = material;
|
|
|
|
});
|
2020-07-13 10:52:10 +02:00
|
|
|
this.filters.filters.find(e => e.field === 'material.name').autocomplete = mData.map(e => e.name);
|
|
|
|
this.filters.filters.find(e => e.field === 'color').autocomplete = [...new Set(mData.reduce((s, e) => {s.push(...e.numbers.map(el => el.color)); return s; }, []))];
|
2020-06-19 08:43:22 +02:00
|
|
|
this.loadSamples();
|
|
|
|
});
|
2020-07-13 10:52:10 +02:00
|
|
|
this.api.get('/user/key', (data: {key: string}) => {
|
|
|
|
this.apiKey = data.key;
|
|
|
|
});
|
|
|
|
this.api.get<string[]>('/material/suppliers', (data: any) => {
|
|
|
|
this.filters.filters.find(e => e.field === 'material.supplier').autocomplete = data;
|
|
|
|
});
|
|
|
|
this.api.get<string[]>('/material/groups', (data: any) => {
|
|
|
|
this.filters.filters.find(e => e.field === 'material.group').autocomplete = data;
|
|
|
|
});
|
2020-07-22 10:45:34 +02:00
|
|
|
this.loadTemplateKeys('materials', 'type');
|
|
|
|
this.loadTemplateKeys('measurements', 'added');
|
|
|
|
}
|
|
|
|
|
|
|
|
loadTemplateKeys(collection, insertBefore) {
|
|
|
|
this.api.get('/template/' + collection, (data: {name: string, parameters: {name: string, range: object}[]}[]) => {
|
|
|
|
const templateKeys = [];
|
2020-07-13 10:52:10 +02:00
|
|
|
data.forEach(item => {
|
|
|
|
item.parameters.forEach(parameter => {
|
2020-07-22 10:45:34 +02:00
|
|
|
templateKeys.push({id: `${collection === 'materials' ? 'material' : collection}.${collection === 'materials' ? 'properties' : item.name}.${encodeURIComponent(parameter.name)}`, label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`, active: false});
|
|
|
|
this.filters.filters.push({field: `${collection === 'materials' ? 'material' : collection}.${collection === 'materials' ? 'properties' : item.name}.${parameter.name}`, label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`, active: false, autocomplete: [], mode: 'eq', values: ['']});
|
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-06-19 08:43:22 +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);
|
2020-06-19 08:43:22 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-07-13 10:52:10 +02:00
|
|
|
sampleUrl(options: {paging?: boolean, pagingOptions?: {firstPage?: boolean, toPage?: number, event?: Event}, csv?: boolean, export?: boolean, host?: boolean}) { // return url to fetch samples
|
2020-07-22 10:45:34 +02:00
|
|
|
const additionalTableKeys = ['material_id', '_id']; // keys which should always be added if export = false
|
2020-07-13 10:52:10 +02:00
|
|
|
const query: string[] = [];
|
|
|
|
query.push('status=' + (this.filters.status.new && this.filters.status.validated ? 'all' : (this.filters.status.new ? 'new' : 'validated')));
|
|
|
|
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) {
|
|
|
|
query.push('csv=true');
|
|
|
|
}
|
|
|
|
if (options.export) {
|
|
|
|
query.push('key=' + this.apiKey);
|
|
|
|
}
|
2020-07-22 10:45:34 +02:00
|
|
|
this.keys.forEach(key => {
|
|
|
|
if (key.active && (options.export || (!options.export && key.id.indexOf('material') < 0))) { // do not load material properties for table
|
|
|
|
query.push('fields[]=' + key.id);
|
2020-07-13 10:52:10 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
console.log(this.filters.filters);
|
|
|
|
|
|
|
|
query.push(..._.cloneDeep(this.filters.filters)
|
|
|
|
.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-13 10:52:10 +02:00
|
|
|
.map(e => 'filters[]=' + encodeURIComponent(JSON.stringify(_.pick(e, ['mode', 'field', 'values']))))
|
|
|
|
);
|
|
|
|
console.log(this.filters);
|
|
|
|
if (!options.export) {
|
|
|
|
additionalTableKeys.forEach(key => {
|
|
|
|
if (query.indexOf('fields[]=' + key) < 0) { // add key if not already added
|
|
|
|
query.push('fields[]=' + key);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else if (this.downloadCsv) {
|
|
|
|
query.push('fields[]=measurements.spectrum.dpt');
|
|
|
|
}
|
|
|
|
console.log('/samples?' + query.join('&'));
|
|
|
|
return (options.host && isDevMode() ? window.location.host : '') + (options.export ? this.api.hostName : '') + '/samples?' + query.join('&');
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
if (filter.mode === 'in' || filter.mode === 'nin') {
|
|
|
|
if (filter.values[filter.values.length - 1] === '' && filter.values[filter.values.length - 2] === '') {
|
|
|
|
filter.values.pop();
|
|
|
|
}
|
|
|
|
else if (filter.values[filter.values.length - 1] !== '') {
|
|
|
|
filter.values.push((filter.field === 'added' ? new Date() : '') as string & Date);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
filter.values = [filter.values[0] as string & Date];
|
|
|
|
}
|
|
|
|
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('.').map(el => decodeURIComponent(el)));
|
|
|
|
console.log(this.activeTemplateKeys);
|
|
|
|
console.log(this.keys); // TODO: glass fiber filter not working
|
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
|
|
|
}
|
|
|
|
|
|
|
|
preventDefault(event, key = 'all') {
|
|
|
|
if (key === 'all' || event.key === key) {
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|