implemented sample references
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<rb-accordion>
|
||||
<rb-accordion-title [open]="true"><span class="rb-ic rb-ic-filter"></span> Filter</rb-accordion-title>
|
||||
<rb-accordion-title [open]="false"><span class="rb-ic rb-ic-filter"></span> Filter</rb-accordion-title>
|
||||
<rb-accordion-body>
|
||||
<form class="filters">
|
||||
<div class="status-selection">
|
||||
@ -28,36 +28,37 @@
|
||||
<option value="500">500</option>
|
||||
</rb-form-select>
|
||||
|
||||
<rb-form-multi-select name="fieldSelect" idField="id" [items]="keys" [(ngModel)]="activeKeys" label="Fields" class="selection" (ngModelChange)="loadSamples()">
|
||||
<rb-form-multi-select name="fieldSelect" idField="id" [items]="keys" [(ngModel)]="isActiveKey" label="Fields" class="selection" (ngModelChange)="loadSamples({}, $event)">
|
||||
<span *rbFormMultiSelectOption="let item" class="load-first-page">{{item.label}}</span>
|
||||
</rb-form-multi-select>
|
||||
|
||||
<div class="fieldfilters">
|
||||
<div *ngFor="let filter of filters.filters">
|
||||
<rb-form-checkbox [name]="'filteractive-' + filter.field" [(ngModel)]="filter.active" (ngModelChange)="loadSamples({firstPage: true})"></rb-form-checkbox>
|
||||
<rb-form-select [name]="'filtermode-' + filter.field" class="filtermode" [(ngModel)]="filter.mode" (ngModelChange)="updateFilterFields(filter.field)">
|
||||
<option value="eq">=</option>
|
||||
<option value="ne">≠</option>
|
||||
<option value="lt"><</option>
|
||||
<option value="lte">≤</option>
|
||||
<option value="gt">></option>
|
||||
<option value="gte">≥</option>
|
||||
<option value="in">∈</option>
|
||||
<option value="nin">∉</option>
|
||||
</rb-form-select>
|
||||
<div class="filter-inputs">
|
||||
<ng-container *ngFor="let ignore of [].constructor(filter.values.length); index as i">
|
||||
<rb-form-date-input *ngIf="filter.field === 'added'; else noDate" [name]="'filter-' + filter.field + i" [label]="filter.label" [(ngModel)]="filter.values[i]" (ngModelChange)="updateFilterFields(filter.field)"></rb-form-date-input>
|
||||
<ng-template #noDate>
|
||||
<rb-form-input *ngIf="!filter.autocomplete.length" [name]="'filter-' + filter.field + i" [label]="filter.label" [(ngModel)]="filter.values[i]" (ngModelChange)="updateFilterFields(filter.field)"></rb-form-input>
|
||||
<rb-form-input *ngIf="filter.autocomplete.length" [name]="'filter-' + filter.field + i" [label]="filter.label" [rbFormInputAutocomplete]="autocomplete.bind(this, filter.autocomplete)" [rbDebounceTime]="0" (keydown)="preventDefault($event, 'Enter')" [(ngModel)]="filter.values[i]" (ngModelChange)="updateFilterFields(filter.field)" ngModel></rb-form-input>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-container *ngIf="isActiveKey[filter.field]">
|
||||
<rb-form-checkbox [name]="'filteractive-' + filter.field" [(ngModel)]="filter.active" (ngModelChange)="loadSamples({firstPage: true})"></rb-form-checkbox>
|
||||
<rb-form-select [name]="'filtermode-' + filter.field" class="filtermode" [(ngModel)]="filter.mode" (ngModelChange)="updateFilterFields(filter.field)">
|
||||
<option value="eq" title="field is equal to value">=</option>
|
||||
<option value="ne" title="field is not equal to value">≠</option>
|
||||
<option value="lt" title="field is lower than value"><</option>
|
||||
<option value="lte" title="field is lower than or equal to value">≤</option>
|
||||
<option value="gt" title="field is greater than value">></option>
|
||||
<option value="gte" title="field is greater than or equal to value">≥</option>
|
||||
<option value="stringin" title="field contains value">⊇</option>
|
||||
<option value="in" title="field is one of the values">∈</option>
|
||||
<option value="nin" title="field is not one of the values">∉</option>
|
||||
</rb-form-select>
|
||||
<div class="filter-inputs">
|
||||
<ng-container *ngFor="let ignore of [].constructor(filter.values.length); index as i">
|
||||
<rb-form-date-input *ngIf="filter.field === 'added'; else noDate" [name]="'filter-' + filter.field + i" [label]="filter.label" [(ngModel)]="filter.values[i]" (ngModelChange)="updateFilterFields(filter.field)"></rb-form-date-input>
|
||||
<ng-template #noDate>
|
||||
<rb-form-input *ngIf="!filter.autocomplete.length" [name]="'filter-' + filter.field + i" [label]="filter.label" [(ngModel)]="filter.values[i]" (ngModelChange)="updateFilterFields(filter.field)"></rb-form-input>
|
||||
<rb-form-input *ngIf="filter.autocomplete.length" [name]="'filter-' + filter.field + i" [label]="filter.label" [rbFormInputAutocomplete]="autocomplete.bind(this, filter.autocomplete)" [rbDebounceTime]="0" (keydown)="preventDefault($event, 'Enter')" [(ngModel)]="filter.values[i]" (ngModelChange)="updateFilterFields(filter.field)" ngModel></rb-form-input>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <button class="rb-btn rb-secondary" (click)="loadSamples()">Apply filters</button>-->
|
||||
</form>
|
||||
</rb-accordion-body>
|
||||
</rb-accordion>
|
||||
@ -83,7 +84,7 @@
|
||||
|
||||
<rb-table>
|
||||
<tr>
|
||||
<th *ngFor="let key of activeKeysArray()">
|
||||
<th *ngFor="let key of activeKeys">
|
||||
<div class="sort-header">
|
||||
<span>{{key.label}}</span>
|
||||
<span class="rb-ic rb-ic-up sort-arr-up" (click)="setSort(key.id + '-' + 'desc')"><span *ngIf="filters.sort === key.id + '-' + 'desc'"></span></span>
|
||||
@ -94,19 +95,18 @@
|
||||
</tr>
|
||||
|
||||
<tr *ngFor="let sample of samples">
|
||||
<td *ngIf="activeKeys['number']">{{sample.number}}</td>
|
||||
<td *ngIf="activeKeys['material.number']">{{sample.material_number}}</td>
|
||||
<td *ngIf="activeKeys['material.name']">{{materials[sample.material_id].name}}</td>
|
||||
<td *ngIf="activeKeys['material.supplier']">{{materials[sample.material_id].supplier}}</td>
|
||||
<td *ngIf="activeKeys['material.group']">{{materials[sample.material_id].group}}</td>
|
||||
<td *ngIf="activeKeys['material.glass_fiber']">{{materials[sample.material_id].glass_fiber}}</td>
|
||||
<td *ngIf="activeKeys['material.carbon_fiber']">{{materials[sample.material_id].carbon_fiber}}</td>
|
||||
<td *ngIf="activeKeys['material.mineral']">{{materials[sample.material_id].mineral}}</td>
|
||||
<td *ngIf="activeKeys['type']">{{sample.type}}</td>
|
||||
<td *ngIf="activeKeys['color']">{{sample.color}}</td>
|
||||
<td *ngIf="activeKeys['batch']">{{sample.batch}}</td>
|
||||
<td *ngIf="activeKeys['added']">{{sample.added | date:'dd/MM/yy'}}</td>
|
||||
<td *ngFor="let key of activeMeasurementKeys()">{{sample[key[1]] ? sample[key[1]][key[2]] : ''}}</td>
|
||||
<td *ngIf="isActiveKey['number']">{{sample.number}}</td>
|
||||
<td *ngIf="isActiveKey['material.numbers']">{{materials[sample.material_id].numbers}}</td>
|
||||
<td *ngIf="isActiveKey['material.name']">{{materials[sample.material_id].name}}</td>
|
||||
<td *ngIf="isActiveKey['material.supplier']">{{materials[sample.material_id].supplier}}</td>
|
||||
<td *ngIf="isActiveKey['material.group']">{{materials[sample.material_id].group}}</td>
|
||||
<td *ngFor="let key of activeTemplateKeys.material">{{materials[sample.material_id].properties[key[2]] | exists}}</td>
|
||||
<td *ngIf="isActiveKey['type']">{{sample.type}}</td>
|
||||
<td *ngIf="isActiveKey['color']">{{sample.color}}</td>
|
||||
<td *ngIf="isActiveKey['batch']">{{sample.batch}}</td>
|
||||
<td *ngIf="isActiveKey['notes']">{{sample.notes | object}}</td>
|
||||
<td *ngFor="let key of activeTemplateKeys.measurements">{{sample[key[1]] | exists: key[2]}}</td>
|
||||
<td *ngIf="isActiveKey['added']">{{sample.added | date:'dd/MM/yy'}}</td>
|
||||
<td><a [routerLink]="'/samples/edit/' + sample._id"><span class="rb-ic rb-ic-edit"></span></a></td>
|
||||
</tr>
|
||||
</rb-table>
|
||||
@ -120,9 +120,9 @@
|
||||
</button>
|
||||
<rb-form-input label="page" (change)="loadPage({toPage: $event.target.value - page})" [ngModel]="page"></rb-form-input>
|
||||
<span>
|
||||
of {{pages()}} ({{totalSamples}} samples)
|
||||
of {{pages}} ({{totalSamples}} samples)
|
||||
</span>
|
||||
<button class="rb-btn rb-link" type="button" (click)="loadPage(1)" [disabled]="page >= pages()">
|
||||
<button class="rb-btn rb-link" type="button" (click)="loadPage(1)" [disabled]="page >= pages">
|
||||
<span class="rb-ic rb-ic-forward-right"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -13,6 +13,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
rb-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rb-ic.rb-ic-edit {
|
||||
font-size: 1.1rem;
|
||||
color: $color-gray-silver-sand;
|
||||
@ -156,11 +160,13 @@
|
||||
& > div {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto 1fr;
|
||||
float: left;
|
||||
margin-right: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.filtermode {
|
||||
max-width: 100px;
|
||||
max-width: 80px;
|
||||
}
|
||||
|
||||
textarea.linkmodal {
|
||||
|
@ -4,12 +4,16 @@ import {AutocompleteService} from '../services/autocomplete.service';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
||||
|
||||
interface LoadSamplesOptions {
|
||||
toPage?: number;
|
||||
event?: Event;
|
||||
firstPage?: boolean;
|
||||
}
|
||||
interface KeyInterface {
|
||||
id: string;
|
||||
label: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-samples',
|
||||
@ -17,6 +21,8 @@ interface LoadSamplesOptions {
|
||||
styleUrls: ['./samples.component.scss']
|
||||
})
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
export class SamplesComponent implements OnInit {
|
||||
@ -24,7 +30,6 @@ export class SamplesComponent implements OnInit {
|
||||
@ViewChild('pageSizeSelection') pageSizeSelection: ElementRef<HTMLElement>;
|
||||
@ViewChild('linkarea') linkarea: ElementRef<HTMLTextAreaElement>;
|
||||
|
||||
customFields = [''];
|
||||
downloadCsv = false;
|
||||
materials = {};
|
||||
samples = [];
|
||||
@ -47,40 +52,29 @@ 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', label: 'Notes', active: false, autocomplete: [], mode: 'eq', values: ['']},
|
||||
{field: 'added', label: 'Added', active: false, autocomplete: [], mode: 'eq', values: [new Date()]}
|
||||
]
|
||||
};
|
||||
page = 1;
|
||||
pages = 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'}
|
||||
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}
|
||||
];
|
||||
isActiveKey: {[key: string]: boolean} = {};
|
||||
activeKeys: KeyInterface[] = [];
|
||||
activeTemplateKeys = {material: [], measurements: []};
|
||||
|
||||
|
||||
constructor(
|
||||
@ -90,6 +84,7 @@ export class SamplesComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.calcFieldSelectKeys();
|
||||
this.api.get('/materials?status=all', (mData: any) => {
|
||||
this.materials = {};
|
||||
mData.forEach(material => {
|
||||
@ -108,21 +103,35 @@ export class SamplesComponent implements OnInit {
|
||||
this.api.get<string[]>('/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 = [];
|
||||
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 = [];
|
||||
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: ['']});
|
||||
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: ['']});
|
||||
});
|
||||
});
|
||||
console.log(this.filters.filters);
|
||||
this.keys = [...this.keys, ...measurementKeys];
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
loadSamples(options: LoadSamplesOptions = {}) { // set toPage to null to reload first page, queues calls
|
||||
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();
|
||||
}
|
||||
this.loadSamplesQueue.push(options);
|
||||
if (this.loadSamplesQueue.length <= 1) { // nothing queued up
|
||||
this.sampleLoader(this.loadSamplesQueue[0]);
|
||||
@ -131,13 +140,11 @@ export class SamplesComponent implements OnInit {
|
||||
|
||||
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) {
|
||||
if (!options.toPage && headers['x-total-items']) {
|
||||
this.totalSamples = headers['x-total-items'];
|
||||
}
|
||||
this.pages = Math.ceil(this.totalSamples / this.filters.pageSize);
|
||||
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]);
|
||||
@ -146,7 +153,7 @@ export class SamplesComponent implements OnInit {
|
||||
}
|
||||
|
||||
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 additionalTableKeys = ['material_id', '_id']; // 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) {
|
||||
@ -170,23 +177,22 @@ export class SamplesComponent implements OnInit {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
});
|
||||
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.filter(el => el !== ''); // do not include empty values
|
||||
if (e.field === 'added') { // correct timezone
|
||||
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] !== '')
|
||||
.filter(e => e.active && e.values.length > 0)
|
||||
.map(e => 'filters[]=' + encodeURIComponent(JSON.stringify(_.pick(e, ['mode', 'field', 'values']))))
|
||||
);
|
||||
console.log(this.filters);
|
||||
@ -230,21 +236,23 @@ export class SamplesComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
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
|
||||
}
|
||||
|
||||
activeMeasurementKeys() {
|
||||
return Object.keys(this.activeKeys).filter(e => e.indexOf('measurements.') >= 0 && this.activeKeys[e]).map(e => e.split('.').map(el => decodeURIComponent(el)));
|
||||
calcFieldSelectKeys() {
|
||||
this.keys.forEach(key => {
|
||||
this.isActiveKey[key.id] = key.active;
|
||||
});
|
||||
}
|
||||
|
||||
preventDefault(event, key = 'all') {
|
||||
|
Reference in New Issue
Block a user