pwa, save sample view preferences
@@ -2,12 +2,7 @@ import { Component, isDevMode} from '@angular/core';
 | 
			
		||||
import {LoginService} from './services/login.service';
 | 
			
		||||
import {NavigationStart, Router} from '@angular/router';
 | 
			
		||||
 | 
			
		||||
// TODO: add multiple samples at once
 | 
			
		||||
// TODO: validation: DPT: filename
 | 
			
		||||
// TODO: filter by not completely filled/no measurements
 | 
			
		||||
// TODO: validation of samples
 | 
			
		||||
// TODO: centralize fetching of materials / templates, etc.
 | 
			
		||||
// TODO: PWA
 | 
			
		||||
 | 
			
		||||
// TODO: get rid of chart.js (+moment.js)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,8 @@ import { UsersComponent } from './users/users.component';
 | 
			
		||||
import { ChangelogComponent } from './changelog/changelog.component';
 | 
			
		||||
import { DocumentationDatabaseComponent } from './documentation-database/documentation-database.component';
 | 
			
		||||
import { PredictionComponent } from './prediction/prediction.component';
 | 
			
		||||
import { ServiceWorkerModule } from '@angular/service-worker';
 | 
			
		||||
import { environment } from '../environments/environment';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
@@ -65,7 +67,8 @@ import { PredictionComponent } from './prediction/prediction.component';
 | 
			
		||||
    ReactiveFormsModule,
 | 
			
		||||
    FormFieldsModule,
 | 
			
		||||
    CommonModule,
 | 
			
		||||
    ChartsModule
 | 
			
		||||
    ChartsModule,
 | 
			
		||||
    ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
 | 
			
		||||
  ],
 | 
			
		||||
  providers: [
 | 
			
		||||
    ModalService,
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,6 @@
 | 
			
		||||
    <td>Automatically generated unique id</td>
 | 
			
		||||
    <td>'5f2e63c98d1c020f8cda6e06'</td>
 | 
			
		||||
  </tr>
 | 
			
		||||
<!--  TODO: new names-->
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td>type</td>
 | 
			
		||||
    <td>
 | 
			
		||||
 
 | 
			
		||||
@@ -22,10 +22,6 @@ import {Observable} from 'rxjs';
 | 
			
		||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
			
		||||
import {DataService} from '../services/data.service';
 | 
			
		||||
 | 
			
		||||
// TODO: clean up this mess !!!
 | 
			
		||||
 | 
			
		||||
// TODO: only show condition (if not set) and measurements in edit sample dialog at first
 | 
			
		||||
// TODO: multiple samples for base data, extend multiple measurements, conditions
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-sample',
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ 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';
 | 
			
		||||
import {LocalStorageService} from 'angular-2-local-storage';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface LoadSamplesOptions {
 | 
			
		||||
@@ -28,8 +29,6 @@ interface KeyInterface {
 | 
			
		||||
  styleUrls: ['./samples.component.scss']
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// TODO: save last settings
 | 
			
		||||
 | 
			
		||||
export class SamplesComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  @ViewChild('pageSizeSelection') pageSizeSelection: ElementRef<HTMLElement>;
 | 
			
		||||
@@ -89,29 +88,39 @@ export class SamplesComponent implements OnInit {
 | 
			
		||||
    public autocomplete: AutocompleteService,
 | 
			
		||||
    public login: LoginService,
 | 
			
		||||
    private modalService: ModalService,
 | 
			
		||||
    public d: DataService
 | 
			
		||||
    public d: DataService,
 | 
			
		||||
    private storage: LocalStorageService
 | 
			
		||||
  ) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    let loading = 7;
 | 
			
		||||
    const onLoad = () => {
 | 
			
		||||
      if ((--loading) <= 0) {
 | 
			
		||||
        this.loadSamples();
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    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();
 | 
			
		||||
      onLoad();
 | 
			
		||||
    });
 | 
			
		||||
    this.d.load('materialSuppliers', () => {
 | 
			
		||||
      this.filters.filters.find(e => e.field === 'material.supplier').autocomplete = this.d.arr.materialSuppliers;
 | 
			
		||||
      onLoad();
 | 
			
		||||
    });
 | 
			
		||||
    this.d.load('materialGroups', () => {
 | 
			
		||||
      this.filters.filters.find(e => e.field === 'material.group').autocomplete = this.d.arr.materialGroups;
 | 
			
		||||
      onLoad();
 | 
			
		||||
    });
 | 
			
		||||
    this.d.load('userKey');
 | 
			
		||||
    this.d.load('conditionTemplates');
 | 
			
		||||
    this.loadTemplateKeys('material', 'type');
 | 
			
		||||
    this.loadTemplateKeys('measurement', 'status');
 | 
			
		||||
    this.d.load('userKey', onLoad);
 | 
			
		||||
    this.d.load('conditionTemplates', onLoad);
 | 
			
		||||
    this.loadTemplateKeys('material', 'type', onLoad);
 | 
			
		||||
    this.loadTemplateKeys('measurement', 'status', onLoad);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loadTemplateKeys(collection, insertBefore) {
 | 
			
		||||
  loadTemplateKeys(collection, insertBefore, f) {
 | 
			
		||||
    this.d.load(collection + 'Templates', () => {
 | 
			
		||||
      const templateKeys = [];
 | 
			
		||||
      this.d.arr[collection + 'Templates'].forEach(item => {
 | 
			
		||||
@@ -138,8 +147,8 @@ export class SamplesComponent implements OnInit {
 | 
			
		||||
      });
 | 
			
		||||
      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();
 | 
			
		||||
      this.loadPreferences();
 | 
			
		||||
      f();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -156,6 +165,7 @@ export class SamplesComponent implements OnInit {
 | 
			
		||||
    if (this.loadSamplesQueue.length <= 1) {  // nothing queued up
 | 
			
		||||
      this.sampleLoader(this.loadSamplesQueue[0]);
 | 
			
		||||
    }
 | 
			
		||||
    this.storePreferences();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private sampleLoader(options: LoadSamplesOptions) {  // actual loading of the sample, do not call directly
 | 
			
		||||
@@ -256,6 +266,38 @@ export class SamplesComponent implements OnInit {
 | 
			
		||||
    this.loadSamples({toPage: delta});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  storePreferences() {
 | 
			
		||||
    const store = {
 | 
			
		||||
      filters: {
 | 
			
		||||
        ...pick(this.filters, ['status', 'pageSize', 'toPage', 'sort']),
 | 
			
		||||
        filters: this.filters.filters.map(e => pick(e, ['field', 'active', 'mode', 'values']))
 | 
			
		||||
      },
 | 
			
		||||
      keys: this.keys.map(e => pick(e, ['id', 'active']))
 | 
			
		||||
    };
 | 
			
		||||
    this.storage.set('samplesPreferences', store);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loadPreferences() {
 | 
			
		||||
    const store: any = this.storage.get('samplesPreferences');
 | 
			
		||||
    if (store) {
 | 
			
		||||
      this.filters = {...this.filters, ...pick(store.filters, ['status', 'pageSize', 'toPage', 'sort'])};
 | 
			
		||||
      store.filters.filters.forEach(filter => {
 | 
			
		||||
        const filterIndex = this.filters.filters.findIndex(e => e.field === filter.field);
 | 
			
		||||
        if (filterIndex >= 0) {
 | 
			
		||||
          this.filters.filters[filterIndex] = {...this.filters.filters[filterIndex], ...filter};
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      store.keys.forEach(key => {
 | 
			
		||||
        const keyIndex = this.keys.findIndex(e => e.id === key.id);
 | 
			
		||||
        if (keyIndex >= 0) {
 | 
			
		||||
          this.keys[keyIndex].active = key.active;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      this.calcFieldSelectKeys();
 | 
			
		||||
      this.updateActiveKeys();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateFilterFields(field) {
 | 
			
		||||
    const filter = this.filters.filters.find(e => e.field === field);
 | 
			
		||||
    filter.active = true;
 | 
			
		||||
@@ -390,4 +432,6 @@ export class SamplesComponent implements OnInit {
 | 
			
		||||
  ucFirst(string) {
 | 
			
		||||
    return string[0].toUpperCase() + string.slice(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								src/assets/icons/icon-128x128.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 26 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/icons/icon-144x144.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 30 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/icons/icon-152x152.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 30 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/icons/icon-192x192.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 39 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/icons/icon-384x384.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 136 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/icons/icon-512x512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 235 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/icons/icon-72x72.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 10 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/icons/icon-96x96.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 16 KiB  | 
@@ -6,8 +6,11 @@
 | 
			
		||||
  <base href="/">
 | 
			
		||||
  <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
  <link rel="icon" type="image/x-icon" href="favicon.ico">
 | 
			
		||||
  <link rel="manifest" href="manifest.webmanifest">
 | 
			
		||||
  <meta name="theme-color" content="#1976d2">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
  <app-root></app-root>
 | 
			
		||||
  <noscript>Please enable JavaScript to continue using this application.</noscript>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								src/manifest.webmanifest
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,59 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "definma",
 | 
			
		||||
  "short_name": "definma",
 | 
			
		||||
  "theme_color": "#1976d2",
 | 
			
		||||
  "background_color": "#fafafa",
 | 
			
		||||
  "display": "standalone",
 | 
			
		||||
  "scope": "./",
 | 
			
		||||
  "start_url": "./",
 | 
			
		||||
  "icons": [
 | 
			
		||||
    {
 | 
			
		||||
      "src": "assets/icons/icon-72x72.png",
 | 
			
		||||
      "sizes": "72x72",
 | 
			
		||||
      "type": "image/png",
 | 
			
		||||
      "purpose": "maskable any"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "src": "assets/icons/icon-96x96.png",
 | 
			
		||||
      "sizes": "96x96",
 | 
			
		||||
      "type": "image/png",
 | 
			
		||||
      "purpose": "maskable any"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "src": "assets/icons/icon-128x128.png",
 | 
			
		||||
      "sizes": "128x128",
 | 
			
		||||
      "type": "image/png",
 | 
			
		||||
      "purpose": "maskable any"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "src": "assets/icons/icon-144x144.png",
 | 
			
		||||
      "sizes": "144x144",
 | 
			
		||||
      "type": "image/png",
 | 
			
		||||
      "purpose": "maskable any"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "src": "assets/icons/icon-152x152.png",
 | 
			
		||||
      "sizes": "152x152",
 | 
			
		||||
      "type": "image/png",
 | 
			
		||||
      "purpose": "maskable any"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "src": "assets/icons/icon-192x192.png",
 | 
			
		||||
      "sizes": "192x192",
 | 
			
		||||
      "type": "image/png",
 | 
			
		||||
      "purpose": "maskable any"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "src": "assets/icons/icon-384x384.png",
 | 
			
		||||
      "sizes": "384x384",
 | 
			
		||||
      "type": "image/png",
 | 
			
		||||
      "purpose": "maskable any"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "src": "assets/icons/icon-512x512.png",
 | 
			
		||||
      "sizes": "512x512",
 | 
			
		||||
      "type": "image/png",
 | 
			
		||||
      "purpose": "maskable any"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||