added templates component
This commit is contained in:
		@@ -26,8 +26,7 @@
 | 
				
			|||||||
            "assets": [
 | 
					            "assets": [
 | 
				
			||||||
              "src/favicon.ico",
 | 
					              "src/favicon.ico",
 | 
				
			||||||
              "src/assets",
 | 
					              "src/assets",
 | 
				
			||||||
              { "glob": "**/*", "input": "./node_modules/@inst-iot/bosch-angular-ui-components/assets", "output": "./assets" },
 | 
					              { "glob": "**/*", "input": "./node_modules/@inst-iot/bosch-angular-ui-components/assets", "output": "./assets" }
 | 
				
			||||||
              "src/Staticfile"
 | 
					 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "styles": [
 | 
					            "styles": [
 | 
				
			||||||
              "src/styles.scss"
 | 
					              "src/styles.scss"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
pushstate: enabled
 | 
					pushstate: enabled
 | 
				
			||||||
force_https: true
 | 
					force_https: true
 | 
				
			||||||
root: UI
 | 
					root: UI
 | 
				
			||||||
location_include: custom-header.conf
 | 
					location_include: ../../headers.conf
 | 
				
			||||||
							
								
								
									
										1
									
								
								cf_config/headers.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cf_config/headers.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					add_header Content-Security-Policy "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; font-src 'self'; connect-src https://definma-api.apps.de1.bosch-iot-cloud.com; form-action 'none'; frame-ancestors 'none'; base-uri 'self'";
 | 
				
			||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
applications:
 | 
					applications:
 | 
				
			||||||
  - name: definma
 | 
					  - name: definma
 | 
				
			||||||
    path: dist/UI
 | 
					    path: dist
 | 
				
			||||||
    buildpacks:
 | 
					    buildpacks:
 | 
				
			||||||
      - staticfile_buildpack
 | 
					      - staticfile_buildpack
 | 
				
			||||||
    memory: 128M
 | 
					    memory: 64M
 | 
				
			||||||
    instances: 1
 | 
					    instances: 1
 | 
				
			||||||
    stack: cflinuxfs3
 | 
					    stack: cflinuxfs3
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										99
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										99
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -2316,6 +2316,12 @@
 | 
				
			|||||||
      "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
 | 
					      "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "acorn-walk": {
 | 
				
			||||||
 | 
					      "version": "7.2.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "adm-zip": {
 | 
					    "adm-zip": {
 | 
				
			||||||
      "version": "0.4.13",
 | 
					      "version": "0.4.13",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz",
 | 
				
			||||||
@@ -2892,6 +2898,18 @@
 | 
				
			|||||||
        "callsite": "1.0.0"
 | 
					        "callsite": "1.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "bfj": {
 | 
				
			||||||
 | 
					      "version": "6.1.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "bluebird": "^3.5.5",
 | 
				
			||||||
 | 
					        "check-types": "^8.0.3",
 | 
				
			||||||
 | 
					        "hoopy": "^0.1.4",
 | 
				
			||||||
 | 
					        "tryer": "^1.0.1"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "big.js": {
 | 
					    "big.js": {
 | 
				
			||||||
      "version": "5.2.2",
 | 
					      "version": "5.2.2",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
 | 
				
			||||||
@@ -3393,6 +3411,12 @@
 | 
				
			|||||||
      "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-0.7.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-0.7.0.tgz",
 | 
				
			||||||
      "integrity": "sha512-PKVUX14nYhH0wcdCpgOoC39Gbzvn6cZ7O9n+bwc02yKD9FTnJ7/TSrBcfebmolFZp1Rcicr9xbT0a5HUbigS7g=="
 | 
					      "integrity": "sha512-PKVUX14nYhH0wcdCpgOoC39Gbzvn6cZ7O9n+bwc02yKD9FTnJ7/TSrBcfebmolFZp1Rcicr9xbT0a5HUbigS7g=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "check-types": {
 | 
				
			||||||
 | 
					      "version": "8.0.3",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "chokidar": {
 | 
					    "chokidar": {
 | 
				
			||||||
      "version": "3.4.0",
 | 
					      "version": "3.4.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
 | 
				
			||||||
@@ -4782,6 +4806,12 @@
 | 
				
			|||||||
        "is-obj": "^2.0.0"
 | 
					        "is-obj": "^2.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "duplexer": {
 | 
				
			||||||
 | 
					      "version": "0.1.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "duplexify": {
 | 
					    "duplexify": {
 | 
				
			||||||
      "version": "3.7.1",
 | 
					      "version": "3.7.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
 | 
				
			||||||
@@ -4810,6 +4840,12 @@
 | 
				
			|||||||
      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
 | 
					      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "ejs": {
 | 
				
			||||||
 | 
					      "version": "2.7.4",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "electron-to-chromium": {
 | 
					    "electron-to-chromium": {
 | 
				
			||||||
      "version": "1.3.446",
 | 
					      "version": "1.3.446",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.446.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.446.tgz",
 | 
				
			||||||
@@ -5483,6 +5519,12 @@
 | 
				
			|||||||
        "minimatch": "^3.0.3"
 | 
					        "minimatch": "^3.0.3"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "filesize": {
 | 
				
			||||||
 | 
					      "version": "3.6.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "fill-range": {
 | 
					    "fill-range": {
 | 
				
			||||||
      "version": "7.0.1",
 | 
					      "version": "7.0.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
 | 
				
			||||||
@@ -5879,6 +5921,16 @@
 | 
				
			|||||||
      "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
 | 
					      "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "gzip-size": {
 | 
				
			||||||
 | 
					      "version": "5.1.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "duplexer": "^0.1.1",
 | 
				
			||||||
 | 
					        "pify": "^4.0.1"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "handle-thing": {
 | 
					    "handle-thing": {
 | 
				
			||||||
      "version": "2.0.1",
 | 
					      "version": "2.0.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
 | 
				
			||||||
@@ -6063,6 +6115,12 @@
 | 
				
			|||||||
        "minimalistic-crypto-utils": "^1.0.1"
 | 
					        "minimalistic-crypto-utils": "^1.0.1"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "hoopy": {
 | 
				
			||||||
 | 
					      "version": "0.1.4",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "hosted-git-info": {
 | 
					    "hosted-git-info": {
 | 
				
			||||||
      "version": "3.0.4",
 | 
					      "version": "3.0.4",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.4.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.4.tgz",
 | 
				
			||||||
@@ -9242,6 +9300,12 @@
 | 
				
			|||||||
        "is-wsl": "^2.1.1"
 | 
					        "is-wsl": "^2.1.1"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "opener": {
 | 
				
			||||||
 | 
					      "version": "1.5.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "opn": {
 | 
					    "opn": {
 | 
				
			||||||
      "version": "5.5.0",
 | 
					      "version": "5.5.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
 | 
				
			||||||
@@ -12914,6 +12978,12 @@
 | 
				
			|||||||
      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
 | 
					      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "tryer": {
 | 
				
			||||||
 | 
					      "version": "1.0.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "ts-node": {
 | 
					    "ts-node": {
 | 
				
			||||||
      "version": "7.0.1",
 | 
					      "version": "7.0.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz",
 | 
				
			||||||
@@ -13726,6 +13796,35 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "webpack-bundle-analyzer": {
 | 
				
			||||||
 | 
					      "version": "3.8.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.8.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-PODQhAYVEourCcOuU+NiYI7WdR8QyELZGgPvB1y2tjbUpbmcQOt5Q7jEK+ttd5se0KSBKD9SXHCEozS++Wllmw==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "acorn": "^7.1.1",
 | 
				
			||||||
 | 
					        "acorn-walk": "^7.1.1",
 | 
				
			||||||
 | 
					        "bfj": "^6.1.1",
 | 
				
			||||||
 | 
					        "chalk": "^2.4.1",
 | 
				
			||||||
 | 
					        "commander": "^2.18.0",
 | 
				
			||||||
 | 
					        "ejs": "^2.6.1",
 | 
				
			||||||
 | 
					        "express": "^4.16.3",
 | 
				
			||||||
 | 
					        "filesize": "^3.6.1",
 | 
				
			||||||
 | 
					        "gzip-size": "^5.0.0",
 | 
				
			||||||
 | 
					        "lodash": "^4.17.15",
 | 
				
			||||||
 | 
					        "mkdirp": "^0.5.1",
 | 
				
			||||||
 | 
					        "opener": "^1.5.1",
 | 
				
			||||||
 | 
					        "ws": "^6.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "acorn": {
 | 
				
			||||||
 | 
					          "version": "7.3.1",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==",
 | 
				
			||||||
 | 
					          "dev": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "webpack-dev-middleware": {
 | 
					    "webpack-dev-middleware": {
 | 
				
			||||||
      "version": "3.7.2",
 | 
					      "version": "3.7.2",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,12 +5,13 @@
 | 
				
			|||||||
    "ng": "ng",
 | 
					    "ng": "ng",
 | 
				
			||||||
    "start": "ng serve",
 | 
					    "start": "ng serve",
 | 
				
			||||||
    "build": "ng build --prod --aot",
 | 
					    "build": "ng build --prod --aot",
 | 
				
			||||||
    "build-push": "ng build --prod --aot && cf push",
 | 
					    "build-push": "ng build --prod --aot && copy /Y cf_config\\ dist && cf push",
 | 
				
			||||||
    "test": "ng test",
 | 
					    "test": "ng test",
 | 
				
			||||||
    "lint": "ng lint",
 | 
					    "lint": "ng lint",
 | 
				
			||||||
    "e2e": "ng e2e",
 | 
					    "e2e": "ng e2e",
 | 
				
			||||||
    "coverage": "ng test --no-watch --code-coverage",
 | 
					    "coverage": "ng test --no-watch --code-coverage",
 | 
				
			||||||
    "api": "cd C:\\Users\\vle2fe\\Documents\\Code\\API && node dist\\index.js"
 | 
					    "api": "cd C:\\Users\\vle2fe\\Documents\\Code\\API && node dist\\index.js",
 | 
				
			||||||
 | 
					    "bundle-report": "ng build --prod --aot --stats-json && webpack-bundle-analyzer dist/UI/stats-es2015.json"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "private": true,
 | 
					  "private": true,
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
@@ -54,6 +55,7 @@
 | 
				
			|||||||
    "protractor": "~5.4.0",
 | 
					    "protractor": "~5.4.0",
 | 
				
			||||||
    "ts-node": "~7.0.0",
 | 
					    "ts-node": "~7.0.0",
 | 
				
			||||||
    "tslint": "~5.15.0",
 | 
					    "tslint": "~5.15.0",
 | 
				
			||||||
    "typescript": "~3.8.3"
 | 
					    "typescript": "~3.8.3",
 | 
				
			||||||
 | 
					    "webpack-bundle-analyzer": "^3.8.0"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ import {LoginService} from './services/login.service';
 | 
				
			|||||||
import {SampleComponent} from './sample/sample.component';
 | 
					import {SampleComponent} from './sample/sample.component';
 | 
				
			||||||
import {SamplesComponent} from './samples/samples.component';
 | 
					import {SamplesComponent} from './samples/samples.component';
 | 
				
			||||||
import {DocumentationComponent} from './documentation/documentation.component';
 | 
					import {DocumentationComponent} from './documentation/documentation.component';
 | 
				
			||||||
 | 
					import {TemplatesComponent} from './templates/templates.component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const routes: Routes = [
 | 
					const routes: Routes = [
 | 
				
			||||||
@@ -13,6 +14,8 @@ const routes: Routes = [
 | 
				
			|||||||
  {path: 'samples', component: SamplesComponent, canActivate: [LoginService]},
 | 
					  {path: 'samples', component: SamplesComponent, canActivate: [LoginService]},
 | 
				
			||||||
  {path: 'samples/new', component: SampleComponent, canActivate: [LoginService]},
 | 
					  {path: 'samples/new', component: SampleComponent, canActivate: [LoginService]},
 | 
				
			||||||
  {path: 'samples/edit/:id', component: SampleComponent, canActivate: [LoginService]},
 | 
					  {path: 'samples/edit/:id', component: SampleComponent, canActivate: [LoginService]},
 | 
				
			||||||
 | 
					  {path: 'templates', component: TemplatesComponent},  // TODO: change after development
 | 
				
			||||||
 | 
					  // {path: 'templates', component: TemplatesComponent, canActivate: [LoginService]},
 | 
				
			||||||
  {path: 'documentation', component: DocumentationComponent},
 | 
					  {path: 'documentation', component: DocumentationComponent},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // if not authenticated
 | 
					  // if not authenticated
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
  <nav *rbMainNavItems>
 | 
					  <nav *rbMainNavItems>
 | 
				
			||||||
    <a routerLink="/home" routerLinkActive="active" rbLoadingLink>Home</a>
 | 
					    <a routerLink="/home" routerLinkActive="active" rbLoadingLink>Home</a>
 | 
				
			||||||
    <a routerLink="/samples" routerLinkActive="active" rbLoadingLink *ngIf="loginService.isLoggedIn">Samples</a>
 | 
					    <a routerLink="/samples" routerLinkActive="active" rbLoadingLink *ngIf="loginService.isLoggedIn">Samples</a>
 | 
				
			||||||
 | 
					    <a routerLink="/templates" routerLinkActive="active" rbLoadingLink *ngIf="loginService.isMaintain">Templates</a>
 | 
				
			||||||
    <a routerLink="/documentation" routerLinkActive="active" rbLoadingLink>Documentation</a>
 | 
					    <a routerLink="/documentation" routerLinkActive="active" rbLoadingLink>Documentation</a>
 | 
				
			||||||
  </nav>
 | 
					  </nav>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,13 @@ import {Router} from '@angular/router';
 | 
				
			|||||||
// TODO: filter by not completely filled/no measurements
 | 
					// TODO: filter by not completely filled/no measurements
 | 
				
			||||||
// TODO: account
 | 
					// TODO: account
 | 
				
			||||||
// TODO: admin user handling, template pages, validation of samples
 | 
					// TODO: admin user handling, template pages, validation of samples
 | 
				
			||||||
 | 
					// TODO: activate filter on start typing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Build IconComponent free lib version because of CSP
 | 
				
			||||||
 | 
					// TODO: more helmet headers, UI presentatin plan
 | 
				
			||||||
 | 
					// TODO: sort material numbers, filter field measurements
 | 
				
			||||||
 | 
					// TODO: get rid of chart.js (+moment.js) and lodash
 | 
				
			||||||
 | 
					// TODO: look into CSS/XHR/Anfragen tab of console
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-root',
 | 
					  selector: 'app-root',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms';
 | 
				
			|||||||
import {LocalStorageModule} from 'angular-2-local-storage';
 | 
					import {LocalStorageModule} from 'angular-2-local-storage';
 | 
				
			||||||
import {HttpClientModule} from '@angular/common/http';
 | 
					import {HttpClientModule} from '@angular/common/http';
 | 
				
			||||||
import { SamplesComponent } from './samples/samples.component';
 | 
					import { SamplesComponent } from './samples/samples.component';
 | 
				
			||||||
import {RbTableModule} from './rb-table/rb-table.module';
 | 
					import {RbCustomInputsModule} from './rb-custom-inputs/rb-custom-inputs.module';
 | 
				
			||||||
import { SampleComponent } from './sample/sample.component';
 | 
					import { SampleComponent } from './sample/sample.component';
 | 
				
			||||||
import { ValidateDirective } from './validate.directive';
 | 
					import { ValidateDirective } from './validate.directive';
 | 
				
			||||||
import {CommonModule} from '@angular/common';
 | 
					import {CommonModule} from '@angular/common';
 | 
				
			||||||
@@ -21,6 +21,8 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
 | 
				
			|||||||
import { DocumentationComponent } from './documentation/documentation.component';
 | 
					import { DocumentationComponent } from './documentation/documentation.component';
 | 
				
			||||||
import { ImgMagnifierComponent } from './img-magnifier/img-magnifier.component';
 | 
					import { ImgMagnifierComponent } from './img-magnifier/img-magnifier.component';
 | 
				
			||||||
import { ExistsPipe } from './exists.pipe';
 | 
					import { ExistsPipe } from './exists.pipe';
 | 
				
			||||||
 | 
					import { TemplatesComponent } from './templates/templates.component';
 | 
				
			||||||
 | 
					import { ParametersPipe } from './parameters.pipe';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@NgModule({
 | 
					@NgModule({
 | 
				
			||||||
  declarations: [
 | 
					  declarations: [
 | 
				
			||||||
@@ -34,7 +36,9 @@ import { ExistsPipe } from './exists.pipe';
 | 
				
			|||||||
    ObjectPipe,
 | 
					    ObjectPipe,
 | 
				
			||||||
    DocumentationComponent,
 | 
					    DocumentationComponent,
 | 
				
			||||||
    ImgMagnifierComponent,
 | 
					    ImgMagnifierComponent,
 | 
				
			||||||
    ExistsPipe
 | 
					    ExistsPipe,
 | 
				
			||||||
 | 
					    TemplatesComponent,
 | 
				
			||||||
 | 
					    ParametersPipe
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  imports: [
 | 
					  imports: [
 | 
				
			||||||
    LocalStorageModule.forRoot({
 | 
					    LocalStorageModule.forRoot({
 | 
				
			||||||
@@ -47,7 +51,7 @@ import { ExistsPipe } from './exists.pipe';
 | 
				
			|||||||
    RbUiComponentsModule,
 | 
					    RbUiComponentsModule,
 | 
				
			||||||
    FormsModule,
 | 
					    FormsModule,
 | 
				
			||||||
    HttpClientModule,
 | 
					    HttpClientModule,
 | 
				
			||||||
    RbTableModule,
 | 
					    RbCustomInputsModule,
 | 
				
			||||||
    ReactiveFormsModule,
 | 
					    ReactiveFormsModule,
 | 
				
			||||||
    FormFieldsModule,
 | 
					    FormFieldsModule,
 | 
				
			||||||
    CommonModule,
 | 
					    CommonModule,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ export class SampleModel extends BaseModel {
 | 
				
			|||||||
  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: {}};
 | 
				
			||||||
 | 
					  added: Date = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  deserialize(input: any): this {
 | 
					  deserialize(input: any): this {
 | 
				
			||||||
    Object.assign(this, input);
 | 
					    Object.assign(this, input);
 | 
				
			||||||
@@ -27,6 +28,9 @@ export class SampleModel extends BaseModel {
 | 
				
			|||||||
    if (input.hasOwnProperty('measurements')) {
 | 
					    if (input.hasOwnProperty('measurements')) {
 | 
				
			||||||
      this.measurements = input.measurements.map(e => new MeasurementModel().deserialize(e));
 | 
					      this.measurements = input.measurements.map(e => new MeasurementModel().deserialize(e));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (input.hasOwnProperty('added')) {
 | 
				
			||||||
 | 
					      this.added = new Date(input.added);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return this;
 | 
					    return this;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import {BaseModel} from './base.model';
 | 
				
			|||||||
export class TemplateModel extends BaseModel {
 | 
					export class TemplateModel extends BaseModel {
 | 
				
			||||||
  _id: IdModel = null;
 | 
					  _id: IdModel = null;
 | 
				
			||||||
  name = '';
 | 
					  name = '';
 | 
				
			||||||
  version = 1;
 | 
					  version = 0;
 | 
				
			||||||
  parameters: {name: string, range: {[prop: string]: any}}[] = [];
 | 
					  first_id: IdModel = null;
 | 
				
			||||||
 | 
					  parameters: {name: string, range: {[prop: string]: any}, rangeString?: string}[] = [];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { Pipe, PipeTransform } from '@angular/core';
 | 
					import { Pipe, PipeTransform } from '@angular/core';
 | 
				
			||||||
 | 
					import _ from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Pipe({
 | 
					@Pipe({
 | 
				
			||||||
  name: 'object',
 | 
					  name: 'object',
 | 
				
			||||||
@@ -6,8 +7,9 @@ import { Pipe, PipeTransform } from '@angular/core';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class ObjectPipe implements PipeTransform {
 | 
					export class ObjectPipe implements PipeTransform {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  transform(value: object): string {
 | 
					  transform(value: object, omit: string[] = []): string {
 | 
				
			||||||
    return value ? JSON.stringify(value) : '';
 | 
					    const res = _.omit(value, omit);
 | 
				
			||||||
 | 
					    return res && Object.keys(res).length ? JSON.stringify(res) : '';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8
									
								
								src/app/parameters.pipe.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/app/parameters.pipe.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					import { ParametersPipe } from './parameters.pipe';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('ParametersPipe', () => {
 | 
				
			||||||
 | 
					  it('create an instance', () => {
 | 
				
			||||||
 | 
					    const pipe = new ParametersPipe();
 | 
				
			||||||
 | 
					    expect(pipe).toBeTruthy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/app/parameters.pipe.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/app/parameters.pipe.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					import { Pipe, PipeTransform } from '@angular/core';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Pipe({
 | 
				
			||||||
 | 
					  name: 'parameters'
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class ParametersPipe implements PipeTransform {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  transform(value: {name: string, range: object}[]): string {
 | 
				
			||||||
 | 
					    return `{${value.map(e => `${e.name}: <${JSON.stringify(e.range).replace('{}', 'any').replace(/["{}]/g, '')}>`).join(', ')}}`;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					import { TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { ArrayInputHelperService } from './array-input-helper.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('ArrayInputHelperService', () => {
 | 
				
			||||||
 | 
					  let service: ArrayInputHelperService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(() => {
 | 
				
			||||||
 | 
					    TestBed.configureTestingModule({});
 | 
				
			||||||
 | 
					    service = TestBed.inject(ArrayInputHelperService);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should be created', () => {
 | 
				
			||||||
 | 
					    expect(service).toBeTruthy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					import { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import {Observable, Subject} from 'rxjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Injectable({
 | 
				
			||||||
 | 
					  providedIn: 'root'
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class ArrayInputHelperService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  com: Subject<{ id: string, index: number, value: any }> = new Subject();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  values(id: string) {
 | 
				
			||||||
 | 
					    return new Observable<{index: number, value: any}>(observer => {
 | 
				
			||||||
 | 
					      this.com.subscribe(data => {
 | 
				
			||||||
 | 
					        if (data.id === id) {
 | 
				
			||||||
 | 
					          observer.next({index: data.index, value: data.value});
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  newValue(id: string, index: number, value: any) {
 | 
				
			||||||
 | 
					    this.com.next({id, index, value});
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					<ng-container *ngFor="let ignore of [].constructor(values.length); index as i">
 | 
				
			||||||
 | 
					  <ng-container *ngTemplateOutlet="item.templateRef; context: {$implicit: {i: i, value: values[i]}}"></ng-container>
 | 
				
			||||||
 | 
					</ng-container>
 | 
				
			||||||
@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { RbArrayInputComponent } from './rb-array-input.component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('RbArrayInputComponent', () => {
 | 
				
			||||||
 | 
					  let component: RbArrayInputComponent;
 | 
				
			||||||
 | 
					  let fixture: ComponentFixture<RbArrayInputComponent>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(async(() => {
 | 
				
			||||||
 | 
					    TestBed.configureTestingModule({
 | 
				
			||||||
 | 
					      declarations: [ RbArrayInputComponent ]
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .compileComponents();
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(() => {
 | 
				
			||||||
 | 
					    fixture = TestBed.createComponent(RbArrayInputComponent);
 | 
				
			||||||
 | 
					    component = fixture.componentInstance;
 | 
				
			||||||
 | 
					    fixture.detectChanges();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should create', () => {
 | 
				
			||||||
 | 
					    expect(component).toBeTruthy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					import {
 | 
				
			||||||
 | 
					  AfterViewInit,
 | 
				
			||||||
 | 
					  Component,
 | 
				
			||||||
 | 
					  ContentChild,
 | 
				
			||||||
 | 
					  Directive,
 | 
				
			||||||
 | 
					  forwardRef,
 | 
				
			||||||
 | 
					  HostListener,
 | 
				
			||||||
 | 
					  Input,
 | 
				
			||||||
 | 
					  OnInit,
 | 
				
			||||||
 | 
					  TemplateRef
 | 
				
			||||||
 | 
					} from '@angular/core';
 | 
				
			||||||
 | 
					import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
 | 
				
			||||||
 | 
					import _ from 'lodash';
 | 
				
			||||||
 | 
					import {ArrayInputHelperService} from './array-input-helper.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: implement everywhere
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Directive({  // directive for template and input values
 | 
				
			||||||
 | 
					  // tslint:disable-next-line:directive-selector
 | 
				
			||||||
 | 
					  selector: '[rbArrayInputItem]'
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class RbArrayInputItemDirective {
 | 
				
			||||||
 | 
					  constructor(public templateRef: TemplateRef<any>) {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Directive({  // directive for change detection
 | 
				
			||||||
 | 
					  // tslint:disable-next-line:directive-selector
 | 
				
			||||||
 | 
					  selector: '[rbArrayInputListener]'
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class RbArrayInputListenerDirective {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Input() rbArrayInputListener: string;
 | 
				
			||||||
 | 
					  @Input() index: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private helperService: ArrayInputHelperService
 | 
				
			||||||
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @HostListener('ngModelChange', ['$event'])
 | 
				
			||||||
 | 
					  onChange(event) {
 | 
				
			||||||
 | 
					    console.log(event);
 | 
				
			||||||
 | 
					    this.helperService.newValue(this.rbArrayInputListener, this.index, event);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					  // tslint:disable-next-line:component-selector
 | 
				
			||||||
 | 
					  selector: 'rb-array-input',
 | 
				
			||||||
 | 
					  templateUrl: './rb-array-input.component.html',
 | 
				
			||||||
 | 
					  styleUrls: ['./rb-array-input.component.scss'],
 | 
				
			||||||
 | 
					  providers: [{provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RbArrayInputComponent), multi: true}]
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class RbArrayInputComponent implements ControlValueAccessor, OnInit, AfterViewInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Input() pushTemplate: any;
 | 
				
			||||||
 | 
					  @Input() pushPath: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ContentChild(RbArrayInputItemDirective) item: RbArrayInputItemDirective;
 | 
				
			||||||
 | 
					  @ContentChild(RbArrayInputListenerDirective) item2: RbArrayInputListenerDirective;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  values = [];  // main array to display
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onChange = (ignore?: any): void => {};
 | 
				
			||||||
 | 
					  onTouched = (ignore?: any): void => {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private helperService: ArrayInputHelperService
 | 
				
			||||||
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ngOnInit(): void {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ngAfterViewInit() {
 | 
				
			||||||
 | 
					    setTimeout(() => {  // needed to find reference
 | 
				
			||||||
 | 
					      this.helperService.values(this.item2.rbArrayInputListener).subscribe(data => {  // action on value change
 | 
				
			||||||
 | 
					        this.values[data.index][this.pushPath] = data.value;
 | 
				
			||||||
 | 
					        console.log(this.values);
 | 
				
			||||||
 | 
					        if (this.values[this.values.length - 1][this.pushPath] === '' && this.values[this.values.length - 2][this.pushPath] === '') {  // remove last element if last two are empty
 | 
				
			||||||
 | 
					          this.values.pop();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (this.values[this.values.length - 1][this.pushPath] !== '') {  // add element if last one is filled
 | 
				
			||||||
 | 
					          this.values.push(_.cloneDeep(this.pushTemplate));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.onChange(this.values.filter(e => e !== ''));  // trigger ngModel with filled elements
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }, 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  writeValue(obj: any) {  // add empty value on init
 | 
				
			||||||
 | 
					    this.values = obj ? obj : [];
 | 
				
			||||||
 | 
					    if (this.values.length === 0 || this.values[0] !== '') {
 | 
				
			||||||
 | 
					      console.log(this.values);
 | 
				
			||||||
 | 
					      this.values.push(_.cloneDeep(this.pushTemplate));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  registerOnChange(fn: any) {
 | 
				
			||||||
 | 
					    this.onChange = fn;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  registerOnTouched(fn: any) {
 | 
				
			||||||
 | 
					    this.onTouched = fn;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/app/rb-custom-inputs/rb-custom-inputs.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/app/rb-custom-inputs/rb-custom-inputs.module.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					import { NgModule } from '@angular/core';
 | 
				
			||||||
 | 
					import { CommonModule } from '@angular/common';
 | 
				
			||||||
 | 
					import { RbTableComponent } from './rb-table/rb-table.component';
 | 
				
			||||||
 | 
					import {RbArrayInputComponent, RbArrayInputListenerDirective, RbArrayInputItemDirective} from './rb-array-input/rb-array-input.component';
 | 
				
			||||||
 | 
					import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
				
			||||||
 | 
					import {FormsModule} from '@angular/forms';
 | 
				
			||||||
 | 
					import { RbIconButtonComponent } from './rb-icon-button/rb-icon-button.component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@NgModule({
 | 
				
			||||||
 | 
					  declarations: [
 | 
				
			||||||
 | 
					    RbTableComponent,
 | 
				
			||||||
 | 
					    RbArrayInputComponent,
 | 
				
			||||||
 | 
					    RbArrayInputListenerDirective,
 | 
				
			||||||
 | 
					    RbArrayInputItemDirective,
 | 
				
			||||||
 | 
					    RbIconButtonComponent
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  imports: [
 | 
				
			||||||
 | 
					    CommonModule,
 | 
				
			||||||
 | 
					    FormsModule,
 | 
				
			||||||
 | 
					    RbUiComponentsModule
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  exports: [
 | 
				
			||||||
 | 
					    RbTableComponent,
 | 
				
			||||||
 | 
					    RbArrayInputComponent,
 | 
				
			||||||
 | 
					    RbArrayInputListenerDirective,
 | 
				
			||||||
 | 
					    RbArrayInputItemDirective,
 | 
				
			||||||
 | 
					    RbIconButtonComponent
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class RbCustomInputsModule { }
 | 
				
			||||||
@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					<button class="rb-btn rb" [ngClass]="'rb-' + mode" type="button" [disabled]="disabled">
 | 
				
			||||||
 | 
					  <span class="rb-ic" [ngClass]="'rb-ic-' + icon"></span>  
 | 
				
			||||||
 | 
					  <ng-content></ng-content>
 | 
				
			||||||
 | 
					</button>
 | 
				
			||||||
@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { RbIconButtonComponent } from './rb-icon-button.component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('RbIconButtonComponent', () => {
 | 
				
			||||||
 | 
					  let component: RbIconButtonComponent;
 | 
				
			||||||
 | 
					  let fixture: ComponentFixture<RbIconButtonComponent>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(async(() => {
 | 
				
			||||||
 | 
					    TestBed.configureTestingModule({
 | 
				
			||||||
 | 
					      declarations: [ RbIconButtonComponent ]
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .compileComponents();
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(() => {
 | 
				
			||||||
 | 
					    fixture = TestBed.createComponent(RbIconButtonComponent);
 | 
				
			||||||
 | 
					    component = fixture.componentInstance;
 | 
				
			||||||
 | 
					    fixture.detectChanges();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should create', () => {
 | 
				
			||||||
 | 
					    expect(component).toBeTruthy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					import {Component, Input, OnInit} from '@angular/core';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: apply everywhere
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					  // tslint:disable-next-line:component-selector
 | 
				
			||||||
 | 
					  selector: 'rb-icon-button',
 | 
				
			||||||
 | 
					  templateUrl: './rb-icon-button.component.html',
 | 
				
			||||||
 | 
					  styleUrls: ['./rb-icon-button.component.scss']
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class RbIconButtonComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Input() icon: string;
 | 
				
			||||||
 | 
					  @Input() mode: string;
 | 
				
			||||||
 | 
					  @Input() disabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ngOnInit(): void {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import { Component, OnInit } from '@angular/core';
 | 
					import { Component, OnInit } from '@angular/core';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
 | 
					  // tslint:disable-next-line:component-selector
 | 
				
			||||||
  selector: 'rb-table',
 | 
					  selector: 'rb-table',
 | 
				
			||||||
  templateUrl: './rb-table.component.html',
 | 
					  templateUrl: './rb-table.component.html',
 | 
				
			||||||
  styleUrls: ['./rb-table.component.scss']
 | 
					  styleUrls: ['./rb-table.component.scss']
 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
import { NgModule } from '@angular/core';
 | 
					 | 
				
			||||||
import { CommonModule } from '@angular/common';
 | 
					 | 
				
			||||||
import { RbTableComponent } from './rb-table/rb-table.component';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@NgModule({
 | 
					 | 
				
			||||||
  declarations: [
 | 
					 | 
				
			||||||
    RbTableComponent
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
  imports: [
 | 
					 | 
				
			||||||
    CommonModule
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
  exports: [
 | 
					 | 
				
			||||||
    RbTableComponent
 | 
					 | 
				
			||||||
  ]
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
export class RbTableModule { }
 | 
					 | 
				
			||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					<script src="samples.component.ts"></script>
 | 
				
			||||||
<div class="header-addnew">
 | 
					<div class="header-addnew">
 | 
				
			||||||
  <h2>Samples</h2>
 | 
					  <h2>Samples</h2>
 | 
				
			||||||
  <a routerLink="/samples/new">
 | 
					  <a routerLink="/samples/new">
 | 
				
			||||||
@@ -87,8 +88,10 @@
 | 
				
			|||||||
    <th *ngFor="let key of activeKeys">
 | 
					    <th *ngFor="let key of activeKeys">
 | 
				
			||||||
      <div class="sort-header">
 | 
					      <div class="sort-header">
 | 
				
			||||||
        <span>{{key.label}}</span>
 | 
					        <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>
 | 
					        <ng-container *ngIf="key.sortable">
 | 
				
			||||||
        <span class="rb-ic rb-ic-down sort-arr-down" (click)="setSort(key.id + '-' + 'asc')"><span *ngIf="filters.sort === key.id + '-' + 'asc'"></span></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>
 | 
				
			||||||
 | 
					          <span class="rb-ic rb-ic-down sort-arr-down" (click)="setSort(key.id + '-' + 'asc')"><span *ngIf="filters.sort === key.id + '-' + 'asc'"></span></span>
 | 
				
			||||||
 | 
					        </ng-container>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </th>
 | 
					    </th>
 | 
				
			||||||
    <th></th>
 | 
					    <th></th>
 | 
				
			||||||
@@ -104,7 +107,7 @@
 | 
				
			|||||||
    <td *ngIf="isActiveKey['type']">{{sample.type}}</td>
 | 
					    <td *ngIf="isActiveKey['type']">{{sample.type}}</td>
 | 
				
			||||||
    <td *ngIf="isActiveKey['color']">{{sample.color}}</td>
 | 
					    <td *ngIf="isActiveKey['color']">{{sample.color}}</td>
 | 
				
			||||||
    <td *ngIf="isActiveKey['batch']">{{sample.batch}}</td>
 | 
					    <td *ngIf="isActiveKey['batch']">{{sample.batch}}</td>
 | 
				
			||||||
    <td *ngIf="isActiveKey['notes']">{{sample.notes | object}}</td>
 | 
					    <td *ngIf="isActiveKey['notes']">{{sample.notes | object: ['_id', 'sample_references']}}</td>
 | 
				
			||||||
    <td *ngFor="let key of activeTemplateKeys.measurements">{{sample[key[1]] | exists: key[2]}}</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 *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>
 | 
					    <td><a [routerLink]="'/samples/edit/' + sample._id"><span class="rb-ic rb-ic-edit"></span></a></td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -178,5 +178,5 @@ textarea.linkmodal {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.filter-inputs > * {
 | 
					.filter-inputs > * {
 | 
				
			||||||
  display: inline-block;
 | 
					  display: inline-block;
 | 
				
			||||||
  max-width: 250px;
 | 
					  width: 220px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ import {Component, ElementRef, isDevMode, OnInit, ViewChild} from '@angular/core
 | 
				
			|||||||
import {ApiService} from '../services/api.service';
 | 
					import {ApiService} from '../services/api.service';
 | 
				
			||||||
import {AutocompleteService} from '../services/autocomplete.service';
 | 
					import {AutocompleteService} from '../services/autocomplete.service';
 | 
				
			||||||
import _ from 'lodash';
 | 
					import _ from 'lodash';
 | 
				
			||||||
 | 
					import {SampleModel} from '../models/sample.model';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface LoadSamplesOptions {
 | 
					interface LoadSamplesOptions {
 | 
				
			||||||
@@ -13,6 +14,7 @@ interface KeyInterface {
 | 
				
			|||||||
  id: string;
 | 
					  id: string;
 | 
				
			||||||
  label: string;
 | 
					  label: string;
 | 
				
			||||||
  active: boolean;
 | 
					  active: boolean;
 | 
				
			||||||
 | 
					  sortable: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
@@ -21,9 +23,8 @@ interface KeyInterface {
 | 
				
			|||||||
  styleUrls: ['./samples.component.scss']
 | 
					  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
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: check if custom-header.conf works, add headers from helmet https://docs.cloudfoundry.org/buildpacks/staticfile/index.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class SamplesComponent implements OnInit {
 | 
					export class SamplesComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,7 +33,7 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  downloadCsv = false;
 | 
					  downloadCsv = false;
 | 
				
			||||||
  materials = {};
 | 
					  materials = {};
 | 
				
			||||||
  samples = [];
 | 
					  samples: SampleModel[] = [];
 | 
				
			||||||
  totalSamples = 0;  // total number of samples
 | 
					  totalSamples = 0;  // total number of samples
 | 
				
			||||||
  csvUrl = '';  // store url separate so it only has to be generated when clicking the download button
 | 
					  csvUrl = '';  // store url separate so it only has to be generated when clicking the download button
 | 
				
			||||||
  filters = {
 | 
					  filters = {
 | 
				
			||||||
@@ -61,16 +62,16 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
  loadSamplesQueue = [];  // arguments of queued up loadSamples() calls
 | 
					  loadSamplesQueue = [];  // arguments of queued up loadSamples() calls
 | 
				
			||||||
  apiKey = '';
 | 
					  apiKey = '';
 | 
				
			||||||
  keys: KeyInterface[] = [
 | 
					  keys: KeyInterface[] = [
 | 
				
			||||||
    {id: 'number', label: 'Number', active: true},
 | 
					    {id: 'number', label: 'Number', active: true, sortable: true},
 | 
				
			||||||
    {id: 'material.numbers', label: 'Material numbers', active: true},
 | 
					    {id: 'material.numbers', label: 'Material numbers', active: true, sortable: false},
 | 
				
			||||||
    {id: 'material.name', label: 'Material name', active: true},
 | 
					    {id: 'material.name', label: 'Material name', active: true, sortable: true},
 | 
				
			||||||
    {id: 'material.supplier', label: 'Supplier', active: true},
 | 
					    {id: 'material.supplier', label: 'Supplier', active: true, sortable: true},
 | 
				
			||||||
    {id: 'material.group', label: 'Material', active: false},
 | 
					    {id: 'material.group', label: 'Material', active: false, sortable: true},
 | 
				
			||||||
    {id: 'type', label: 'Type', active: true},
 | 
					    {id: 'type', label: 'Type', active: true, sortable: true},
 | 
				
			||||||
    {id: 'color', label: 'Color', active: true},
 | 
					    {id: 'color', label: 'Color', active: true, sortable: true},
 | 
				
			||||||
    {id: 'batch', label: 'Batch', active: true},
 | 
					    {id: 'batch', label: 'Batch', active: true, sortable: true},
 | 
				
			||||||
    {id: 'notes', label: 'Notes', active: false},
 | 
					    {id: 'notes', label: 'Notes', active: false, sortable: false},
 | 
				
			||||||
    {id: 'added', label: 'Added', active: true}
 | 
					    {id: 'added', label: 'Added', active: true, sortable: true},
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
  isActiveKey: {[key: string]: boolean} = {};
 | 
					  isActiveKey: {[key: string]: boolean} = {};
 | 
				
			||||||
  activeKeys: KeyInterface[] = [];
 | 
					  activeKeys: KeyInterface[] = [];
 | 
				
			||||||
@@ -112,8 +113,11 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
      const templateKeys = [];
 | 
					      const templateKeys = [];
 | 
				
			||||||
      data.forEach(item => {
 | 
					      data.forEach(item => {
 | 
				
			||||||
        item.parameters.forEach(parameter => {
 | 
					        item.parameters.forEach(parameter => {
 | 
				
			||||||
          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});
 | 
					          const parameterName = encodeURIComponent(parameter.name);
 | 
				
			||||||
          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: ['']});
 | 
					          if (parameter.name !== 'dpt' && !templateKeys.find(e => new RegExp('.' + parameterName + '$').test(e.id))) {  // exclude spectrum
 | 
				
			||||||
 | 
					            templateKeys.push({id: `${collection === 'materials' ? 'material.properties' : collection + '.' + item.name}.${parameterName}`, label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`, active: false, sortable: true});
 | 
				
			||||||
 | 
					            this.filters.filters.push({field: `${collection === 'materials' ? 'material.properties' : collection + '.' + item.name}.${parameterName}`, label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`, active: false, autocomplete: [], mode: 'eq', values: ['']});
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      this.keys.splice(this.keys.findIndex(e => e.id === insertBefore), 0, ...templateKeys);
 | 
					      this.keys.splice(this.keys.findIndex(e => e.id === insertBefore), 0, ...templateKeys);
 | 
				
			||||||
@@ -220,6 +224,7 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  updateFilterFields(field) {
 | 
					  updateFilterFields(field) {
 | 
				
			||||||
    const filter = this.filters.filters.find(e => e.field === field);
 | 
					    const filter = this.filters.filters.find(e => e.field === field);
 | 
				
			||||||
 | 
					    filter.active = true;
 | 
				
			||||||
    if (filter.mode === 'in' || filter.mode === 'nin') {
 | 
					    if (filter.mode === 'in' || filter.mode === 'nin') {
 | 
				
			||||||
      if (filter.values[filter.values.length - 1] === '' && filter.values[filter.values.length - 2] === '') {
 | 
					      if (filter.values[filter.values.length - 1] === '' && filter.values[filter.values.length - 2] === '') {
 | 
				
			||||||
        filter.values.pop();
 | 
					        filter.values.pop();
 | 
				
			||||||
@@ -250,6 +255,8 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  calcFieldSelectKeys() {
 | 
					  calcFieldSelectKeys() {
 | 
				
			||||||
 | 
					    console.log('CALC');
 | 
				
			||||||
 | 
					    console.log(this.keys);
 | 
				
			||||||
    this.keys.forEach(key => {
 | 
					    this.keys.forEach(key => {
 | 
				
			||||||
      this.isActiveKey[key.id] = key.active;
 | 
					      this.isActiveKey[key.id] = key.active;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,10 @@ import {Observable} from 'rxjs';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class LoginService implements CanActivate {
 | 
					export class LoginService implements CanActivate {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private maintainPaths = ['templates'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private loggedIn;
 | 
					  private loggedIn;
 | 
				
			||||||
 | 
					  private level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private api: ApiService,
 | 
					    private api: ApiService,
 | 
				
			||||||
@@ -27,6 +30,7 @@ export class LoginService implements CanActivate {
 | 
				
			|||||||
        if (!error) {
 | 
					        if (!error) {
 | 
				
			||||||
          if (data.status === 'Authorization successful') {
 | 
					          if (data.status === 'Authorization successful') {
 | 
				
			||||||
            this.loggedIn = true;
 | 
					            this.loggedIn = true;
 | 
				
			||||||
 | 
					            this.level = data.level;
 | 
				
			||||||
            resolve(true);
 | 
					            resolve(true);
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            this.loggedIn = false;
 | 
					            this.loggedIn = false;
 | 
				
			||||||
@@ -49,14 +53,21 @@ export class LoginService implements CanActivate {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  canActivate(route: ActivatedRouteSnapshot = null, state: RouterStateSnapshot = null): Observable<boolean> {
 | 
					  canActivate(route: ActivatedRouteSnapshot = null, state: RouterStateSnapshot = null): Observable<boolean> {
 | 
				
			||||||
    return new Observable<boolean>(observer => {
 | 
					    return new Observable<boolean>(observer => {
 | 
				
			||||||
      if (this.loggedIn === undefined) {
 | 
					      const isMaintainPath = this.maintainPaths.indexOf(route.url[0].path) >= 0;
 | 
				
			||||||
        this.login().then(res => {
 | 
					      if (!isMaintainPath || (isMaintainPath && this.isMaintain)) {
 | 
				
			||||||
          observer.next(res as any);
 | 
					        if (this.loggedIn === undefined) {
 | 
				
			||||||
 | 
					          this.login().then(res => {
 | 
				
			||||||
 | 
					            observer.next(res as any);
 | 
				
			||||||
 | 
					            observer.complete();
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					          observer.next(this.loggedIn);
 | 
				
			||||||
          observer.complete();
 | 
					          observer.complete();
 | 
				
			||||||
        });
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      else {
 | 
					      else {
 | 
				
			||||||
        observer.next(this.loggedIn);
 | 
					        observer.next(false);
 | 
				
			||||||
        observer.complete();
 | 
					        observer.complete();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -66,6 +77,10 @@ export class LoginService implements CanActivate {
 | 
				
			|||||||
    return this.loggedIn;
 | 
					    return this.loggedIn;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get isMaintain() {
 | 
				
			||||||
 | 
					    return this.level === 'maintain' || this.level === 'admin';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get username() {
 | 
					  get username() {
 | 
				
			||||||
    return atob(this.storage.get('basicAuth')).split(':')[0];
 | 
					    return atob(this.storage.get('basicAuth')).split(':')[0];
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -121,4 +121,53 @@ export class ValidationService {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    return {ok: true, error: ''};
 | 
					    return {ok: true, error: ''};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  parameterName(data) {
 | 
				
			||||||
 | 
					    const {ignore, error} = Joi.string()
 | 
				
			||||||
 | 
					      .max(128)
 | 
				
			||||||
 | 
					      .invalid('condition_template', 'material_template')
 | 
				
			||||||
 | 
					      .pattern(/^[^.]+$/)
 | 
				
			||||||
 | 
					      .required()
 | 
				
			||||||
 | 
					      .messages({'string.pattern.base': 'name must not contain a dot'})
 | 
				
			||||||
 | 
					      .validate(data);
 | 
				
			||||||
 | 
					    if (error) {
 | 
				
			||||||
 | 
					      return {ok: false, error: error.details[0].message};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return {ok: true, error: ''};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  parameterRange(data) {
 | 
				
			||||||
 | 
					    if (data) {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        const {ignore, error} = Joi.object({
 | 
				
			||||||
 | 
					          values: Joi.array()
 | 
				
			||||||
 | 
					            .min(1),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          min: Joi.number(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          max: Joi.number(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          type: Joi.string()
 | 
				
			||||||
 | 
					            .valid('array')
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					          .oxor('values', 'min')
 | 
				
			||||||
 | 
					          .oxor('values', 'max')
 | 
				
			||||||
 | 
					          .oxor('type', 'values')
 | 
				
			||||||
 | 
					          .oxor('type', 'min')
 | 
				
			||||||
 | 
					          .oxor('type', 'max')
 | 
				
			||||||
 | 
					          .required()
 | 
				
			||||||
 | 
					          .validate(JSON.parse(data));
 | 
				
			||||||
 | 
					        if (error) {
 | 
				
			||||||
 | 
					          return {ok: false, error: error.details[0].message};
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      catch (e) {
 | 
				
			||||||
 | 
					        return {ok: false, error: `no valid JSON`};
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return {ok: true, error: ''};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      return {ok: false, error: `no valid value`};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										66
									
								
								src/app/templates/templates.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/app/templates/templates.component.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					<h2>Templates</h2>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<rb-form-select name="collectionSelection" label="collection"
 | 
				
			||||||
 | 
					                [(ngModel)]="collection" (ngModelChange)="loadTemplates()">
 | 
				
			||||||
 | 
					  <option value="material">Materials</option>
 | 
				
			||||||
 | 
					  <option value="measurement">Measurements</option>
 | 
				
			||||||
 | 
					  <option value="condition">Conditions</option>
 | 
				
			||||||
 | 
					</rb-form-select>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<rb-icon-button icon="add" mode="primary" (click)="newTemplate()">New template</rb-icon-button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="list">
 | 
				
			||||||
 | 
					  <div class="row">
 | 
				
			||||||
 | 
					    <div class="header">Name</div>
 | 
				
			||||||
 | 
					    <div class="header">Version</div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ng-container *ngFor="let group of groupsView">
 | 
				
			||||||
 | 
					    <div class="row clickable">
 | 
				
			||||||
 | 
					      <div (click)="group.expanded = !group.expanded">{{group.name}}</div>
 | 
				
			||||||
 | 
					      <div (click)="group.expanded = !group.expanded">{{group.version}}</div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="row" *ngIf="group.expanded" [@inOut]>
 | 
				
			||||||
 | 
					      <div class="details">
 | 
				
			||||||
 | 
					        <ng-container *ngFor="let template of group.entries">
 | 
				
			||||||
 | 
					          <div>{{template.name}}</div>
 | 
				
			||||||
 | 
					          <div>{{template.version}}</div>
 | 
				
			||||||
 | 
					          <div>{{template.parameters | parameters}}</div>
 | 
				
			||||||
 | 
					        </ng-container>
 | 
				
			||||||
 | 
					        <div class="template-actions">
 | 
				
			||||||
 | 
					          <form #templateForm="ngForm">
 | 
				
			||||||
 | 
					            <div *ngIf="group.edit">
 | 
				
			||||||
 | 
					              <rb-form-input [name]="'name-' + group.name" label="name" appValidate="string" required
 | 
				
			||||||
 | 
					                             [(ngModel)]="templateEdit[group.first_id].name" #supplierInput="ngModel">
 | 
				
			||||||
 | 
					                <ng-template rbFormValidationMessage="failure">{{supplierInput.errors.failure}}</ng-template>
 | 
				
			||||||
 | 
					              </rb-form-input>
 | 
				
			||||||
 | 
					              <rb-array-input [(ngModel)]="templateEdit[group.first_id].parameters" [name]="'parameters-' + group.name"
 | 
				
			||||||
 | 
					                              [pushTemplate]="{name: '', range: {}, rangeString: '{}'}" pushPath="name"
 | 
				
			||||||
 | 
					                              class="parameters">
 | 
				
			||||||
 | 
					                <ng-container *rbArrayInputItem="let item">
 | 
				
			||||||
 | 
					                  <rb-form-input [rbArrayInputListener]="'parameter-name-' + group.name" appValidate="parameterName"
 | 
				
			||||||
 | 
					                                 [index]="item.i" [name]="'parameter-name-' + group.name + item.i" label="parameter name"
 | 
				
			||||||
 | 
					                                 [ngModel]="item.value.name" #parameterName="ngModel">
 | 
				
			||||||
 | 
					                    <ng-template rbFormValidationMessage="failure">{{parameterName.errors.failure}}</ng-template>
 | 
				
			||||||
 | 
					                  </rb-form-input>
 | 
				
			||||||
 | 
					                  <rb-form-textarea [name]="'parameter-range-' + group.name + item.i" label="range" appValidate="parameterRange"
 | 
				
			||||||
 | 
					                                    [(ngModel)]="item.value.rangeString" #parameterRange="ngModel">
 | 
				
			||||||
 | 
					                    <ng-template rbFormValidationMessage="failure">{{parameterRange.errors.failure}}</ng-template>
 | 
				
			||||||
 | 
					                  </rb-form-textarea>
 | 
				
			||||||
 | 
					                </ng-container>
 | 
				
			||||||
 | 
					              </rb-array-input>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <rb-icon-button icon="edit" mode="secondary" (click)="group.edit = !group.edit">
 | 
				
			||||||
 | 
					              Edit template
 | 
				
			||||||
 | 
					            </rb-icon-button>
 | 
				
			||||||
 | 
					            <rb-icon-button icon="save" mode="primary" (click)="saveTemplate(group.first_id)" *ngIf="group.edit">
 | 
				
			||||||
 | 
					              Save template
 | 
				
			||||||
 | 
					            </rb-icon-button>
 | 
				
			||||||
 | 
					          </form>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </ng-container>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										44
									
								
								src/app/templates/templates.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/app/templates/templates.component.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					@import "~@inst-iot/bosch-angular-ui-components/styles/variables/colors";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.list {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .row {
 | 
				
			||||||
 | 
					    display: grid;
 | 
				
			||||||
 | 
					    grid-template-columns: 1fr 4fr;
 | 
				
			||||||
 | 
					    border-bottom: 1px solid $color-gray-mercury;
 | 
				
			||||||
 | 
					    overflow: hidden;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    & > div {
 | 
				
			||||||
 | 
					      padding: 8px 5px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      &.header {
 | 
				
			||||||
 | 
					        font-weight: bold;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      &.details {
 | 
				
			||||||
 | 
					        grid-column: span 2;
 | 
				
			||||||
 | 
					        display: grid;
 | 
				
			||||||
 | 
					        grid-template-columns: 1fr 1fr 3fr;
 | 
				
			||||||
 | 
					        background: $color-gray-alabaster;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .template-actions {
 | 
				
			||||||
 | 
					          grid-column: span 3;
 | 
				
			||||||
 | 
					          margin-top: 10px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          .parameters {
 | 
				
			||||||
 | 
					            display: grid;
 | 
				
			||||||
 | 
					            grid-template-columns: 1fr 2fr;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          rb-icon-button[icon="save"] {
 | 
				
			||||||
 | 
					            float: right;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.clickable {
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/app/templates/templates.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/app/templates/templates.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { TemplatesComponent } from './templates.component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('TemplatesComponent', () => {
 | 
				
			||||||
 | 
					  let component: TemplatesComponent;
 | 
				
			||||||
 | 
					  let fixture: ComponentFixture<TemplatesComponent>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(async(() => {
 | 
				
			||||||
 | 
					    TestBed.configureTestingModule({
 | 
				
			||||||
 | 
					      declarations: [ TemplatesComponent ]
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .compileComponents();
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(() => {
 | 
				
			||||||
 | 
					    fixture = TestBed.createComponent(TemplatesComponent);
 | 
				
			||||||
 | 
					    component = fixture.componentInstance;
 | 
				
			||||||
 | 
					    fixture.detectChanges();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should create', () => {
 | 
				
			||||||
 | 
					    expect(component).toBeTruthy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										124
									
								
								src/app/templates/templates.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/app/templates/templates.component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					import { Component, OnInit } from '@angular/core';
 | 
				
			||||||
 | 
					import {ApiService} from '../services/api.service';
 | 
				
			||||||
 | 
					import {TemplateModel} from '../models/template.model';
 | 
				
			||||||
 | 
					import {animate, style, transition, trigger} from '@angular/animations';
 | 
				
			||||||
 | 
					import {ValidationService} from '../services/validation.service';
 | 
				
			||||||
 | 
					import _ from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					  selector: 'app-templates',
 | 
				
			||||||
 | 
					  templateUrl: './templates.component.html',
 | 
				
			||||||
 | 
					  styleUrls: ['./templates.component.scss'],
 | 
				
			||||||
 | 
					  animations: [
 | 
				
			||||||
 | 
					    trigger(
 | 
				
			||||||
 | 
					      'inOut', [
 | 
				
			||||||
 | 
					        transition(':enter', [
 | 
				
			||||||
 | 
					          style({height: 0, opacity: 0}),
 | 
				
			||||||
 | 
					          animate('0.5s ease-out', style({height: '*', opacity: 1}))
 | 
				
			||||||
 | 
					        ]),
 | 
				
			||||||
 | 
					        transition(':leave', [
 | 
				
			||||||
 | 
					          style({height: '*', opacity: 1}),
 | 
				
			||||||
 | 
					          animate('0.5s ease-in', style({height: 0, opacity: 0}))
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class TemplatesComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  collection = 'measurement';
 | 
				
			||||||
 | 
					  templates: TemplateModel[] = [];
 | 
				
			||||||
 | 
					  templateGroups: {[first_id: string]: TemplateModel[]} = {};  // templates grouped by first_id
 | 
				
			||||||
 | 
					  templateEdit: {[first_id: string]: TemplateModel} = {};      // latest template of each first_id for editing
 | 
				
			||||||
 | 
					  groupsView: {first_id: string, name: string, version: number, expanded: boolean, edit: boolean, entries: TemplateModel[]}[] = [];
 | 
				
			||||||
 | 
					  arr = ['testA', 'testB', 'testC'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private api: ApiService,
 | 
				
			||||||
 | 
					    private validate: ValidationService
 | 
				
			||||||
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ngOnInit(): void {
 | 
				
			||||||
 | 
					    this.loadTemplates();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  loadTemplates() {
 | 
				
			||||||
 | 
					    this.api.get<TemplateModel[]>(`/template/${this.collection}s`, data => {
 | 
				
			||||||
 | 
					      this.templates = data;
 | 
				
			||||||
 | 
					      this.templateFormat();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  templateFormat() {
 | 
				
			||||||
 | 
					    this.templateGroups = {};
 | 
				
			||||||
 | 
					    this.templateEdit = {};
 | 
				
			||||||
 | 
					    this.templates.forEach(template => {
 | 
				
			||||||
 | 
					      if (this.templateGroups[template.first_id]) {
 | 
				
			||||||
 | 
					        this.templateGroups[template.first_id].push(template);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else {
 | 
				
			||||||
 | 
					        this.templateGroups[template.first_id] = [template];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    Object.keys(this.templateGroups).forEach(id => {
 | 
				
			||||||
 | 
					      this.templateGroups[id] = this.templateGroups[id].sort((a, b) => a.version - b.version);
 | 
				
			||||||
 | 
					      this.templateEdit[id] = _.cloneDeep(this.templateGroups[id][this.templateGroups[id].length - 1]);
 | 
				
			||||||
 | 
					      this.templateEdit[id].parameters = this.templateEdit[id].parameters.map(e => {e.rangeString = JSON.stringify(e.range, null, 2); return e; });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    this.groupsView = Object.values(this.templateGroups)
 | 
				
			||||||
 | 
					      .map(e => ({
 | 
				
			||||||
 | 
					        first_id: e[e.length - 1].first_id,
 | 
				
			||||||
 | 
					        name: e[e.length - 1].name,
 | 
				
			||||||
 | 
					        version: e[e.length - 1].version,
 | 
				
			||||||
 | 
					        expanded: false,
 | 
				
			||||||
 | 
					        edit: false,
 | 
				
			||||||
 | 
					        entries: e
 | 
				
			||||||
 | 
					      }));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  saveTemplate(first_id) {
 | 
				
			||||||
 | 
					    const template = _.cloneDeep(this.templateEdit[first_id]);
 | 
				
			||||||
 | 
					    template.parameters = template.parameters.filter(e => e.name !== '');
 | 
				
			||||||
 | 
					    let valid = true;
 | 
				
			||||||
 | 
					    valid = valid && this.validate.string(template.name).ok;
 | 
				
			||||||
 | 
					    template.parameters.forEach(parameter => {
 | 
				
			||||||
 | 
					      valid = valid && this.validate.parameterName(parameter.name).ok;
 | 
				
			||||||
 | 
					      valid = valid && this.validate.parameterRange(parameter.rangeString).ok;
 | 
				
			||||||
 | 
					      if (valid) {
 | 
				
			||||||
 | 
					        parameter.range = JSON.parse(parameter.rangeString);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    if (valid) {
 | 
				
			||||||
 | 
					      console.log('valid', template);
 | 
				
			||||||
 | 
					      const sendData = {name: template.name, parameters: template.parameters.map(e => _.omit(e, ['rangeString']))};
 | 
				
			||||||
 | 
					      if (first_id === 'null') {
 | 
				
			||||||
 | 
					        this.api.post<TemplateModel>(`/template/${this.collection}/new`, sendData, data => {
 | 
				
			||||||
 | 
					          if (data.version > template.version) {  // there were actual changes and a new version was created
 | 
				
			||||||
 | 
					            this.templates.push(data);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          this.templateFormat();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else {
 | 
				
			||||||
 | 
					        this.api.put<TemplateModel>(`/template/${this.collection}/${template.first_id}`, sendData, data => {
 | 
				
			||||||
 | 
					          if (data.version > template.version) {  // there were actual changes and a new version was created
 | 
				
			||||||
 | 
					            this.templates.push(data);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          this.templateFormat();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      console.log('not valid');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  newTemplate() {
 | 
				
			||||||
 | 
					    if (!this.templateEdit.null) {
 | 
				
			||||||
 | 
					      const template = new TemplateModel();
 | 
				
			||||||
 | 
					      template.name = 'new template';
 | 
				
			||||||
 | 
					      this.groupsView.push({first_id: 'null', name: 'new template', version: 0, expanded: true, edit: true, entries: [template]});
 | 
				
			||||||
 | 
					      this.templateEdit.null = new TemplateModel();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										82
									
								
								src/assets/imgs/supergraphic.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/assets/imgs/supergraphic.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="300" width="720" version="1.1" y="0" x="0" viewBox="0 0 720 300">
 | 
				
			||||||
 | 
					  <style type="text/css">
 | 
				
			||||||
 | 
					    .st0 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_1_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st1 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_2_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st2 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_3_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st3 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_4_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st4 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_5_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st5 {
 | 
				
			||||||
 | 
					      fill: #AF2024;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st6 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_6_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st7 {
 | 
				
			||||||
 | 
					      fill: #941B1E;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st8 {
 | 
				
			||||||
 | 
					      fill: #B12739;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st9 {
 | 
				
			||||||
 | 
					      fill: #952432;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st10 {
 | 
				
			||||||
 | 
					      fill: #D42027;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st11 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_7_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st12 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_8_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st13 {
 | 
				
			||||||
 | 
					      fill: #1C9A48;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st14 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_9_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st15 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_10_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st16 {
 | 
				
			||||||
 | 
					      fill: #2A3886;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st17 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_11_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st18 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_12_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st19 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_13_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .st20 {
 | 
				
			||||||
 | 
					      fill: url("#SVGID_14_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  </style>
 | 
				
			||||||
 | 
					  <g transform="translate(-1.55,-3.3)">
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_1_" y2="-32.663" gradientUnits="userSpaceOnUse" y1="-32.663" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="842.08" x1="118.98"><stop stop-color="#952331" offset="0"/><stop stop-color="#921C1D" offset=".036094"/><stop stop-color="#B02739" offset=".084649"/><stop stop-color="#AD1F24" offset=".1237"/><stop stop-color="#C72026" offset=".1509"/><stop stop-color="#D42027" offset=".1697"/><stop stop-color="#CC2431" offset=".1758"/><stop stop-color="#B72B4C" offset=".1888"/><stop stop-color="#953371" offset=".2074"/><stop stop-color="#88357F" offset=".2142"/><stop stop-color="#853681" offset=".2436"/><stop stop-color="#6F368B" offset=".2638"/><stop stop-color="#39428F" offset=".2911"/><stop stop-color="#233D7D" offset=".3242"/><stop stop-color="#322C6F" offset=".4181"/><stop stop-color="#2A3885" offset=".494"/><stop stop-color="#1D62A1" offset=".5581"/><stop stop-color="#276CA5" offset=".5702"/><stop stop-color="#438EB3" offset=".6103"/><stop stop-color="#55A5BC" offset=".6399"/><stop stop-color="#5CAFBF" offset=".6556"/><stop stop-color="#56ABBD" offset=".6777"/><stop stop-color="#439FB8" offset=".7058"/><stop stop-color="#188EAF" offset=".7372"/><stop stop-color="#038BAE" offset=".7426"/><stop stop-color="#069292" offset=".7898"/><stop stop-color="#05A14B" offset=".8875"/><stop stop-color="#03927E" offset="1"/></linearGradient><rect width="723.1" y="0" x="0" height="306.4" class="st0" fill="url(#SVGID_1_)"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_2_" y2="-109.26" gradientUnits="userSpaceOnUse" y1="-109.26" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="235.98" x1="325.08"><stop stop-color="#893680" offset="0"/><stop stop-color="#893680" offset=".3354"/><stop stop-color="#8D316D" offset=".5025"/><stop stop-color="#90294D" offset=".8398"/><stop stop-color="#902541" offset="1"/></linearGradient><polygon points="175.1 153.2 117 306.4 206.1 306.4" fill="url(#SVGID_2_)" class="st1"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_3_" y2="-82.284" gradientUnits="userSpaceOnUse" y1="120.24" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="446.55" x1="478.93"><stop stop-color="#322C6F" offset="0"/><stop stop-color="#322C6F" offset=".2427"/><stop stop-color="#302F72" offset=".4599"/><stop stop-color="#2A3A7E" offset=".7155"/><stop stop-color="#154A93" offset=".9896"/><stop stop-color="#134B94" offset="1"/></linearGradient><polygon points="288.4 153.2 310.7 306.4 358.1 306.4 358.1 0 312.9 0" fill="url(#SVGID_3_)" class="st2"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_4_" y2="-32.663" gradientUnits="userSpaceOnUse" y1="-32.663" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="372.88" x1="294.08"><stop stop-color="#6F378D" offset="0"/><stop stop-color="#3A4291" offset="1"/></linearGradient><polygon points="175.1 153.2 206.1 306.4 253.9 153.2 209.4 0 209.4 0" fill="url(#SVGID_4_)" class="st3"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_5_" y2="-32.663" gradientUnits="userSpaceOnUse" y1="-32.663" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="325.08" x1="431.88"><stop stop-color="#233D7D" offset="0"/><stop stop-color="#293D7D" offset=".2495"/><stop stop-color="#3A3C80" offset=".5446"/><stop stop-color="#513B84" offset=".8616"/><stop stop-color="#5D3A86" offset="1"/></linearGradient><polygon points="253.9 153.2 206.1 306.4 310.7 306.4 288.4 153.2 312.9 0 209.4 0" fill="url(#SVGID_5_)" class="st4"/><polygon points="116.1 0 55.7 0 55.7 94.8 89.9 153.2 55.7 211.6 55.7 306.4 117 306.4 95.2 153.2" fill="#af2024" class="st5"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_6_" y2="43.937" gradientUnits="userSpaceOnUse" y1="43.937" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="232.67" x1="329.11"><stop stop-color="#893680" offset="0"/><stop stop-color="#893680" offset=".3354"/><stop stop-color="#8D316D" offset=".5025"/><stop stop-color="#90294D" offset=".8398"/><stop stop-color="#902541" offset="1"/></linearGradient><polygon points="175.1 153.2 209.4 0 116.1 0" fill="url(#SVGID_6_)" class="st6"/><polygon points="55.7 94.8 55.7 0 0 0" fill="#941b1e" class="st7"/><polygon points="55.7 211.6 89.9 153.2 55.7 94.8" fill="#b12739" class="st8"/><polygon points="55.7 211.6 0 306.4 55.7 306.4" fill="#941b1e" class="st7"/><polygon points="55.7 94.8 0 0 0 306.4 55.7 211.6" fill="#952432" class="st9"/><polygon points="116.1 0 95.2 153.2 117 306.4 175.1 153.2" fill="#d42027" class="st10"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_7_" y2="-186.06" gradientUnits="userSpaceOnUse" y1="120.44" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="748.96" x1="748.96"><stop stop-color="#94BE55" offset="0"/><stop stop-color="#93BD58" offset=".044340"/><stop stop-color="#8BBC6A" offset=".3891"/><stop stop-color="#86BC75" offset=".7149"/><stop stop-color="#84BC79" offset="1"/></linearGradient><path d="m641.6 259.6c1.7-25.4 10-54.6 18.8-85.6 1.4-5 2.8-10 4.2-15.1-1.4-5.5-2.8-10.9-4.2-16.2-8.8-33.3-17-64.7-18.8-92-1.4-21.2 1.4-37 8.9-50.6h-45.9c-7.5 18.3-10.3 29.1-8.9 50.3 1.7 27.3 10 58.7 18.8 92 13 49.3 28 106.2 23.2 164.2h12.9c-7.6-12.8-10.4-27.3-9-47z" class="st11" fill="url(#SVGID_7_)"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_8_" y2="-184.45" gradientUnits="userSpaceOnUse" y1="117.29" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="733.49" x1="653.76"><stop stop-color="#08A24B" offset="0"/><stop stop-color="#0AA14E" offset=".1678"/><stop stop-color="#0B9E57" offset=".4047"/><stop stop-color="#099A67" offset=".6827"/><stop stop-color="#04947D" offset=".9898"/><stop stop-color="#04937E" offset="1"/></linearGradient><path d="m614.5 142.3c-8.8-33.3-17-64.7-18.8-92-1.4-21.2 1.4-32 8.9-50.3h-35.4c5.7 53.9-3.8 106.7-13.6 166.8-5.7 35-11.7 71.3-13.2 100.6-1.1 21.1 0.4 32.8 1.8 39h93.5c4.8-57.9-10.3-114.8-23.2-164.1z" class="st12" fill="url(#SVGID_8_)"/><path class="st13" fill="#1c9a48" d="m664.6 158.9c-1.4 5.1-2.8 10.1-4.2 15.1-8.8 31-17 60.2-18.8 85.6-1.4 19.7 1.4 34.2 9 46.9h33c4.2-51.8-7.2-102.3-19-147.6z"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_9_" y2="-185.96" gradientUnits="userSpaceOnUse" y1="120.54" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="812.83" x1="812.83"><stop stop-color="#69A060" offset="0"/><stop stop-color="#639D5C" offset=".039895"/><stop stop-color="#4C944F" offset=".2192"/><stop stop-color="#378E47" offset=".4184"/><stop stop-color="#298B44" offset=".6515"/><stop stop-color="#238A43" offset="1"/></linearGradient><path d="m680.5 0c10.7 55.3-2.5 110.4-15.9 158.9 11.7 45.3 23.2 95.8 18.9 147.6h39.6v-306.5h-42.6z" class="st14" fill="url(#SVGID_9_)"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_10_" y2="-185.86" gradientUnits="userSpaceOnUse" y1="120.54" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="652.45" x1="652.45"><stop stop-color="#05B5DC" offset="0"/><stop stop-color="#04B0D7" offset=".2197"/><stop stop-color="#05A4C9" offset=".5371"/><stop stop-color="#0591B4" offset=".9122"/><stop stop-color="#058CAE" offset="1"/></linearGradient><path d="m542.3 267.4c1.5-29.4 7.5-65.6 13.2-100.6 9.8-60.1 19.3-112.8 13.6-166.8h-70.8c-1.4 11.4-2.9 19.2-1.8 41.8 1.5 31.6 7.5 70.5 13.2 108.2 8.4 55.4 16.6 108.8 15.1 156.4h19.2c-1.3-6.2-2.8-17.9-1.7-39z" class="st15" fill="url(#SVGID_10_)"/><polygon points="375.7 153.2 358.1 0 358.1 306.4" fill="#2a3886" class="st16"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_11_" y2="77.136" gradientUnits="userSpaceOnUse" y1="-4.3281" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="796.71" x1="751.05"><stop stop-color="#62B16E" offset="0"/><stop stop-color="#87B957" offset="1"/></linearGradient><path d="m641.6 50.6c1.7 27.3 10 58.7 18.8 92 1.4 5.3 2.8 10.7 4.2 16.2 13.5-48.4 26.6-103.5 15.9-158.8h-30c-7.5 13.6-10.3 29.4-8.9 50.6z" class="st17" fill="url(#SVGID_11_)"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_12_" y2="-189.28" gradientUnits="userSpaceOnUse" y1="113.71" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="631.59" x1="550.4"><stop stop-color="#069AD4" offset="0"/><stop stop-color="#30A0CE" offset=".3525"/><stop stop-color="#5BB0C0" offset="1"/></linearGradient><path d="m509.8 150c-5.7-37.7-11.7-76.6-13.2-108.2-1.1-22.7 0.4-30.4 1.8-41.8h-41.5c1.5 40.1-1.5 85.3-7 160.8-3.1 43.5-8 110.5-7 145.7h82.1c1.4-47.7-6.8-101.1-15.2-156.5z" class="st18" fill="url(#SVGID_12_)"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_13_" y2="-185.86" gradientUnits="userSpaceOnUse" y1="120.54" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="505.33" x1="505.33"><stop stop-color="#1E458E" offset="0"/><stop stop-color="#1F4F96" offset=".2411"/><stop stop-color="#2B6AAB" offset=".7292"/><stop stop-color="#337BB9" offset="1"/></linearGradient><polygon points="358.1 306.4 414.6 306.4 414.6 0 358.1 0 375.7 153.2" fill="url(#SVGID_13_)" class="st19"/>
 | 
				
			||||||
 | 
					    <linearGradient id="SVGID_14_" y2="120.54" gradientUnits="userSpaceOnUse" y1="-185.86" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="554.92" x1="554.92"><stop stop-color="#3F9AC9" offset="0"/><stop stop-color="#2062A2" offset="1"/></linearGradient><path d="m449.9 160.8c5.5-75.5 8.5-120.6 7-160.8h-42.2l-0.1 306.4h28.3c-1-35.1 3.8-102.1 7-145.6z" class="st20" fill="url(#SVGID_14_)"/></g>
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 9.9 KiB  | 
@@ -15,3 +15,7 @@ a, a:active, a:focus {
 | 
				
			|||||||
button::-moz-focus-inner {
 | 
					button::-moz-focus-inner {
 | 
				
			||||||
  border: 0;
 | 
					  border: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.supergraphic {
 | 
				
			||||||
 | 
					  background-image: url("assets/imgs/supergraphic.svg");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user