import {Component, ElementRef, isDevMode, OnInit, ViewChild} from '@angular/core'; import {ApiService} from '../services/api.service'; import {AutocompleteService} from '../services/autocomplete.service'; import _ from 'lodash'; interface LoadSamplesOptions { toPage?: number; event?: Event; firstPage?: boolean; } @Component({ selector: 'app-samples', templateUrl: './samples.component.html', styleUrls: ['./samples.component.scss'] }) export class SamplesComponent implements OnInit { @ViewChild('pageSizeSelection') pageSizeSelection: ElementRef; @ViewChild('linkarea') linkarea: ElementRef; customFields = ['']; downloadCsv = false; materials = {}; samples = []; totalSamples = 0; // total number of samples 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: ['']}, {field: 'added', label: 'Added', active: false, autocomplete: [], mode: 'eq', values: [new Date()]} ] }; page = 1; loadSamplesQueue = []; // arguments of queued up loadSamples() calls apiKey = ''; activeKeys = { number: true, 'material.number': true, 'material.name': true, 'material.supplier': true, 'material.group': false, 'material.glass_fiber': false, 'material.carbon_fiber': false, 'material.mineral': false, type: true, color: true, batch: true, added: true, }; keys = [ {id: 'number', label: 'Number'}, {id: 'material.number', label: 'Material number'}, {id: 'material.name', label: 'Material name'}, {id: 'material.supplier', label: 'Supplier'}, {id: 'material.group', label: 'Material'}, {id: 'material.glass_fiber', label: 'GF'}, {id: 'material.carbon_fiber', label: 'CF'}, {id: 'material.mineral', label: 'M'}, {id: 'type', label: 'Type'}, {id: 'color', label: 'Color'}, {id: 'batch', label: 'Batch'}, {id: 'added', label: 'Added'} ]; constructor( private api: ApiService, public autocomplete: AutocompleteService ) { } ngOnInit(): void { this.api.get('/materials?status=all', (mData: any) => { this.materials = {}; mData.forEach(material => { this.materials[material._id] = material; }); 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; }, []))]; this.loadSamples(); }); this.api.get('/user/key', (data: {key: string}) => { this.apiKey = data.key; }); this.api.get('/material/suppliers', (data: any) => { this.filters.filters.find(e => e.field === 'material.supplier').autocomplete = data; }); this.api.get('/material/groups', (data: any) => { this.filters.filters.find(e => e.field === 'material.group').autocomplete = data; }); this.api.get('/template/measurements', (data: {name: string, parameters: {name: string, range: object}[]}[]) => { const measurementKeys = []; data.forEach(item => { item.parameters.forEach(parameter => { this.activeKeys[`measurements.${item.name}.${encodeURIComponent(parameter.name)}`] = false; measurementKeys.push({id: `measurements.${item.name}.${encodeURIComponent(parameter.name)}`, label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`}); this.filters.filters.push({field: `measurements.${item.name}.${parameter.name}`, label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`, active: false, autocomplete: [], mode: 'eq', values: ['']}); }); }); console.log(this.filters.filters); this.keys = [...this.keys, ...measurementKeys]; }); } 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 this.api.get(this.sampleUrl({paging: true, pagingOptions: options}), (sData, ignore, headers) => { if (!options.toPage) { this.totalSamples = headers['x-total-items']; } this.samples = sData as any; this.samples.forEach(sample => { sample.material_number = sample.color === '' ? '' : 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]); } }); } 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', 'color']; // keys which should always be added if export = false 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); } Object.keys(this.activeKeys).forEach(key => { if (this.activeKeys[key] && (options.export || (!options.export && key.indexOf('material') < 0))) { // do not load material properties for table query.push('fields[]=' + key); } }); console.log(this.filters.filters); query.push(..._.cloneDeep(this.filters.filters) .map(e => { e.values = e.values.filter(el => el !== ''); if (e.field === 'added') { console.log(e.values); e.values = e.values.map(el => new Date(new Date(el).getTime() - new Date(el).getTimezoneOffset() * 60000).toISOString()); } return e; }) .filter(e => e.active && e.values[0] !== '') .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('&'); } loadPage(delta) { if (!/[0-9]+/.test(delta) || (this.page <= 1 && delta < 0)) { // invalid delta return; } this.page += delta; this.loadSamples({toPage: delta}); } 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}); } } pages() { return Math.ceil(this.totalSamples / this.filters.pageSize); } setSort(string) { this.filters.sort = string; this.loadSamples({firstPage: true}); } activeKeysArray() { // array with all activeKeys names return Object.keys(this.activeKeys).filter(e => this.activeKeys[e] === true).map(e => this.keys.find(el => el.id === e)); } activeMeasurementKeys() { return Object.keys(this.activeKeys).filter(e => e.indexOf('measurements.') >= 0 && this.activeKeys[e]).map(e => e.split('.').map(el => decodeURIComponent(el))); } 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); } }