sorting implemented
This commit is contained in:
		@@ -13,7 +13,7 @@ export class SampleModel extends BaseModel {
 | 
				
			|||||||
  condition: {condition_template: string, [prop: string]: string} | {} = {};
 | 
					  condition: {condition_template: string, [prop: string]: string} | {} = {};
 | 
				
			||||||
  material_id: IdModel = null;
 | 
					  material_id: IdModel = null;
 | 
				
			||||||
  material: MaterialModel;
 | 
					  material: MaterialModel;
 | 
				
			||||||
  measurements: MeasurementModel[];
 | 
					  measurements: MeasurementModel[] = [];
 | 
				
			||||||
  note_id: IdModel = null;
 | 
					  note_id: IdModel = null;
 | 
				
			||||||
  user_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: {}};
 | 
					  notes: {comment: string, sample_references: {sample_id: IdModel, relation: string}[], custom_fields: {[prop: string]: string}} = {comment: '', sample_references: [], custom_fields: {}};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,9 @@ import {MeasurementModel} from '../models/measurement.model';
 | 
				
			|||||||
// TODO: confirmation for new group/supplier
 | 
					// TODO: confirmation for new group/supplier
 | 
				
			||||||
// TODO: DPT preview
 | 
					// TODO: DPT preview
 | 
				
			||||||
// TODO: work on better recognition for file input
 | 
					// 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,48 +6,77 @@
 | 
				
			|||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<rb-accordion>
 | 
					<rb-accordion>
 | 
				
			||||||
  <rb-accordion-title><span class="rb-ic rb-ic-filter"></span>  Filter</rb-accordion-title>
 | 
					  <rb-accordion-title [open]="true"><span class="rb-ic rb-ic-filter"></span>  Filter</rb-accordion-title>
 | 
				
			||||||
  <rb-accordion-body>
 | 
					  <rb-accordion-body>
 | 
				
			||||||
    <form>
 | 
					    <form (change)="loadSamples({event: $event})">
 | 
				
			||||||
      <rb-form-select name="statusSelect" label="Status" [(ngModel)]="filters.status">
 | 
					      <div class="status-selection">
 | 
				
			||||||
        <option value="validated">validated</option>
 | 
					        <label class="label">Status</label>
 | 
				
			||||||
        <option value="new">new</option>
 | 
					        <rb-form-checkbox name="status-validated" [(ngModel)]="filters.status.validated" [disabled]="!filters.status.new">
 | 
				
			||||||
        <option value="all">all</option>
 | 
					          validated
 | 
				
			||||||
 | 
					        </rb-form-checkbox>
 | 
				
			||||||
 | 
					        <rb-form-checkbox name="status-new" [(ngModel)]="filters.status.new" [disabled]="!filters.status.validated">
 | 
				
			||||||
 | 
					          new
 | 
				
			||||||
 | 
					        </rb-form-checkbox>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <rb-form-select name="pageSizeSelection" label="page size" [(ngModel)]="filters.pageSize" class="page-size-selection" #pageSizeSelection>
 | 
				
			||||||
 | 
					        <option value="10">10</option>
 | 
				
			||||||
 | 
					        <option value="25">25</option>
 | 
				
			||||||
 | 
					        <option value="50">50</option>
 | 
				
			||||||
 | 
					        <option value="100">100</option>
 | 
				
			||||||
 | 
					        <option value="250">250</option>
 | 
				
			||||||
 | 
					        <option value="500">500</option>
 | 
				
			||||||
      </rb-form-select>
 | 
					      </rb-form-select>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <button class="rb-btn rb-secondary" (click)="loadSamples()">Apply filters</button>
 | 
					<!--      <button class="rb-btn rb-secondary" (click)="loadSamples()">Apply filters</button>-->
 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
  </rb-accordion-body>
 | 
					  </rb-accordion-body>
 | 
				
			||||||
</rb-accordion>
 | 
					</rb-accordion>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<ng-container *ngTemplateOutlet="paging"></ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<rb-table>
 | 
					<rb-table>
 | 
				
			||||||
  <tr>
 | 
					  <tr>
 | 
				
			||||||
    <th>Number</th>
 | 
					    <th *ngFor="let key of activeKeys">
 | 
				
			||||||
    <th>Material number</th>
 | 
					      <div class="sort-header">
 | 
				
			||||||
    <th>Material name</th>
 | 
					        <span>{{key.name}}</span>
 | 
				
			||||||
    <th>Supplier</th>
 | 
					        <span class="rb-ic rb-ic-up" [ngClass]="{'sort-active-desc': filters.sort === key.key + '-' + 'desc'}" (click)="setSort(key.key + '-' + 'desc')"></span>
 | 
				
			||||||
    <th>Material</th>
 | 
					        <span class="rb-ic rb-ic-down" [ngClass]="{'sort-active-asc': filters.sort === key.key + '-' + 'asc'}" (click)="setSort(key.key + '-' + 'asc')"></span>
 | 
				
			||||||
    <th>GF</th>
 | 
					      </div>
 | 
				
			||||||
    <th>CF</th>
 | 
					    </th>
 | 
				
			||||||
    <th>M</th>
 | 
					 | 
				
			||||||
    <th>type</th>
 | 
					 | 
				
			||||||
    <th>Color</th>
 | 
					 | 
				
			||||||
    <th>Batch</th>
 | 
					 | 
				
			||||||
    <th></th>
 | 
					    <th></th>
 | 
				
			||||||
  </tr>
 | 
					  </tr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <tr *ngFor="let sample of samples">
 | 
					  <tr *ngFor="let sample of samples">
 | 
				
			||||||
    <td>{{sample.number}}</td>
 | 
					    <td>{{sample.number}}</td>
 | 
				
			||||||
    <td>{{sample.material_number}}</td>
 | 
					<!--    <td>{{sample.material_number}}</td>-->
 | 
				
			||||||
    <td>{{materials[sample.material_id].name}}</td>
 | 
					    <td>{{materials[sample.material_id].name}}</td>
 | 
				
			||||||
    <td>{{materials[sample.material_id].supplier}}</td>
 | 
					    <td>{{materials[sample.material_id].supplier}}</td>
 | 
				
			||||||
    <td>{{materials[sample.material_id].group}}</td>
 | 
					<!--    <td>{{materials[sample.material_id].group}}</td>-->
 | 
				
			||||||
    <td>{{materials[sample.material_id].glass_fiber}}</td>
 | 
					<!--    <td>{{materials[sample.material_id].glass_fiber}}</td>-->
 | 
				
			||||||
    <td>{{materials[sample.material_id].carbon_fiber}}</td>
 | 
					<!--    <td>{{materials[sample.material_id].carbon_fiber}}</td>-->
 | 
				
			||||||
    <td>{{materials[sample.material_id].mineral}}</td>
 | 
					<!--    <td>{{materials[sample.material_id].mineral}}</td>-->
 | 
				
			||||||
    <td>{{sample.type}}</td>
 | 
					    <td>{{sample.type}}</td>
 | 
				
			||||||
    <td>{{sample.color}}</td>
 | 
					    <td>{{sample.color}}</td>
 | 
				
			||||||
    <td>{{sample.batch}}</td>
 | 
					    <td>{{sample.batch}}</td>
 | 
				
			||||||
 | 
					    <td>{{sample.added | date}}</td>
 | 
				
			||||||
    <td><a [routerLink]="'/samples/edit/' + sample._id"><span class="rb-ic rb-ic-edit"></span></a></td>
 | 
					    <td><a [routerLink]="'/samples/edit/' + sample._id"><span class="rb-ic rb-ic-edit"></span></a></td>
 | 
				
			||||||
  </tr>
 | 
					  </tr>
 | 
				
			||||||
</rb-table>
 | 
					</rb-table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<ng-container *ngTemplateOutlet="paging"></ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<ng-template #paging>
 | 
				
			||||||
 | 
					  <div class="paging">
 | 
				
			||||||
 | 
					    <button class="rb-btn rb-link" type="button" (click)="loadPage(-1)" [disabled]="page === 1">
 | 
				
			||||||
 | 
					      <span class="rb-ic  rb-ic-back-left"></span>
 | 
				
			||||||
 | 
					    </button>
 | 
				
			||||||
 | 
					    <rb-form-input label="page" (change)="loadPage({toPage: $event.target.value - page})" [ngModel]="page"></rb-form-input>
 | 
				
			||||||
 | 
					    <span>
 | 
				
			||||||
 | 
					      of {{pages()}}
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					    <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>
 | 
				
			||||||
 | 
					</ng-template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,3 +22,83 @@
 | 
				
			|||||||
    color: #000;
 | 
					    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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,16 +1,46 @@
 | 
				
			|||||||
import { Component, OnInit } from '@angular/core';
 | 
					import {Component, OnInit, ViewChild} from '@angular/core';
 | 
				
			||||||
import {ApiService} from '../services/api.service';
 | 
					import {ApiService} from '../services/api.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface LoadSamplesOptions {
 | 
				
			||||||
 | 
					  toPage?: number;
 | 
				
			||||||
 | 
					  event?: Event;
 | 
				
			||||||
 | 
					  firstPage?: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-samples',
 | 
					  selector: 'app-samples',
 | 
				
			||||||
  templateUrl: './samples.component.html',
 | 
					  templateUrl: './samples.component.html',
 | 
				
			||||||
  styleUrls: ['./samples.component.scss']
 | 
					  styleUrls: ['./samples.component.scss']
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: always show first page on sort change
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class SamplesComponent implements OnInit {  // TODO: implement paging
 | 
					export class SamplesComponent implements OnInit {  // TODO: implement paging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ViewChild('pageSizeSelection') pageSizeSelection: HTMLElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  materials = {};
 | 
					  materials = {};
 | 
				
			||||||
  samples = [];
 | 
					  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(
 | 
					  constructor(
 | 
				
			||||||
    private api: ApiService
 | 
					    private api: ApiService
 | 
				
			||||||
@@ -24,15 +54,61 @@ export class SamplesComponent implements OnInit {  // TODO: implement paging
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
      this.loadSamples();
 | 
					      this.loadSamples();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    this.api.get('/samples/count', (data: {count: number}) => {
 | 
				
			||||||
 | 
					      this.totalSamples = data.count;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  loadSamples() {
 | 
					  loadSamples(options: LoadSamplesOptions = {}) {  // set toPage to null to reload first page, queues calls
 | 
				
			||||||
    this.api.get(`/samples?status=${this.filters.status}`, sData => {
 | 
					    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 = sData as any;
 | 
				
			||||||
      this.samples.forEach(sample => {
 | 
					      this.samples.forEach(sample => {
 | 
				
			||||||
        sample.material_number = this.materials[sample.material_id].numbers.find(e => sample.color === e.color).number;
 | 
					        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});
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user