32 Commits

Author SHA1 Message Date
Willem Dantuma
2192797a35 wip 2026-03-26 17:27:22 +01:00
Willem Dantuma
9d743eddea Add AppConfig parameter 2026-03-26 12:18:12 +01:00
Willem Dantuma
3c99855c31 Set version 2026-03-26 11:08:13 +01:00
Willem Dantuma
8667001c01 Remove oauth 2026-03-25 21:08:18 +01:00
jenkins
73d0a99cd6 [ci skip] Updated packages #2623
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-02-23 15:13:33 +00:00
5991adf0c6 Shared services
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-02-23 16:12:05 +01:00
d7ab7e84ce Shared services
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2026-02-23 15:34:34 +01:00
18092fcc24 Shared services
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2026-02-23 15:22:32 +01:00
jenkins
5e84eb4404 [ci skip] Updated packages #2620
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-02-23 10:43:48 +00:00
a5324c5164 Fix deps
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-02-23 11:41:32 +01:00
jenkins
7c2911afae [ci skip] Updated packages #2619
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-02-17 20:36:15 +00:00
0fe20b6649 trigger build
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-02-17 21:34:18 +01:00
jenkins
c49534050a [ci skip] Updated packages #2618
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-02-17 15:52:00 +00:00
bab870cc43 core js geupdated
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-02-17 16:50:37 +01:00
jenkins
cad022e772 [ci skip] Updated packages #2617
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-02-17 11:11:01 +00:00
jenkins
3b2ea10fdc [ci skip] Updated packages #2616
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-02-03 09:01:03 +00:00
c20754e10a AW-7266 NG0956: The configured tracking expression (track by identity) caused re-creation of the entire collection
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-02-03 09:59:25 +01:00
jenkins
c4b8e9664c [ci skip] Updated packages #2615
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-01-30 12:55:46 +00:00
7822015f8c Revert "NG0956 Tracking expression caused re-creation of the DOM structure."
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
This reverts commit 48261c9657.
2026-01-30 13:54:24 +01:00
48261c9657 NG0956 Tracking expression caused re-creation of the DOM structure.
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2026-01-30 13:40:47 +01:00
jenkins
a6bd28ac45 [ci skip] Updated packages #2613
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-01-21 11:49:36 +00:00
jenkins
674a0f2a9e [ci skip] Updated packages #2612
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-01-21 11:32:36 +00:00
77800fe0f7 feature/Aw6526Angular21
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-01-21 12:30:47 +01:00
3251ba4022 Angular 20, oa. migrating from Karma to Vitest
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-01-21 11:53:59 +01:00
a758a38a0a Angular 20, oa. migrating from Karma to Vitest
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-01-21 11:32:10 +01:00
3c6a8e28c1 Angular 20, oa. migrating from Karma to Vitest
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2026-01-21 11:25:38 +01:00
b72e309fbe npm error ERESOLVE unable to resolve dependency tree
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2026-01-20 18:01:58 +01:00
d7b978c477 ERESOLVE unable to resolve dependency tree
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2026-01-20 17:53:50 +01:00
930d4fe0b7 Merge branch 'develop' into feature/AW6526Angular20
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
# Conflicts:
#	package-lock.json
2026-01-20 17:38:21 +01:00
642ea33c3c Angular v20 2026-01-20 17:36:35 +01:00
429ab24408 Angular v20 2026-01-20 17:11:03 +01:00
jenkins
eed70c449b [ci skip] Updated packages #2601
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-01-20 15:24:54 +00:00
104 changed files with 9354 additions and 13474 deletions

View File

@@ -61,4 +61,4 @@ npm run lint src
npm run lint projects/common/src
npm run lint projects/common-map/src
npm run lint projects/common-map3d/src
```
```

View File

@@ -133,39 +133,10 @@
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"builder": "@angular/build:unit-test",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"src/styles.css"
],
"scripts": [],
"assets": [
"src/favicon.ico",
"src/assets"
]
}
}
}
},
"farmmaps-lib-app-e2e": {
"root": "e2e/",
"projectType": "application",
"prefix": "",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "farmmaps-lib-app:serve"
},
"configurations": {
"production": {
"devServerTarget": "farmmaps-lib-app:serve:production"
}
"browsers": ["chromium"]
}
}
}
@@ -189,11 +160,10 @@
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"builder": "@angular/build:unit-test",
"options": {
"main": "projects/common/src/test.ts",
"tsConfig": "projects/common/tsconfig.spec.json",
"karmaConfig": "projects/common/karma.conf.js"
"browsers": ["chromium"]
}
}
}
@@ -217,11 +187,10 @@
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"builder": "@angular/build:unit-test",
"options": {
"main": "projects/common-map/src/test.ts",
"tsConfig": "projects/common-map/tsconfig.spec.json",
"karmaConfig": "projects/common-map/karma.conf.js"
"browsers": ["chromium"]
}
}
}
@@ -245,11 +214,10 @@
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"builder": "@angular/build:unit-test",
"options": {
"main": "projects/common-map3d/src/test.ts",
"tsConfig": "projects/common-map3d/tsconfig.spec.json",
"karmaConfig": "projects/common-map3d/karma.conf.js"
"browsers": ["chromium"]
}
}
}
@@ -273,11 +241,10 @@
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"builder": "@angular/build:unit-test",
"options": {
"main": "projects/ng-openlayers/src/test.ts",
"tsConfig": "projects/ng-openlayers/tsconfig.spec.json",
"karmaConfig": "projects/ng-openlayers/karma.conf.js"
"browsers": ["chromium"]
}
},
"lint": {
@@ -294,5 +261,31 @@
},
"cli": {
"analytics": false
},
"schematics": {
"@schematics/angular:component": {
"type": "component"
},
"@schematics/angular:directive": {
"type": "directive"
},
"@schematics/angular:service": {
"type": "service"
},
"@schematics/angular:guard": {
"typeSeparator": "."
},
"@schematics/angular:interceptor": {
"typeSeparator": "."
},
"@schematics/angular:module": {
"typeSeparator": "."
},
"@schematics/angular:pipe": {
"typeSeparator": "."
},
"@schematics/angular:resolver": {
"typeSeparator": "."
}
}
}

View File

@@ -1,28 +0,0 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.e2e.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@@ -1,23 +0,0 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to farmmaps-lib-app!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View File

@@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}

View File

@@ -1,13 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

16090
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "farmmaps-lib-app",
"version": "4.20.0",
"scripts": {
"version": "4.22.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
@@ -10,44 +10,43 @@
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular-eslint/eslint-plugin": "^18.2.0",
"@angular/animations": "19.2.18",
"@angular/common": "19.2.18",
"@angular/compiler": "19.2.18",
"@angular/core": "19.2.18",
"@angular/forms": "19.2.18",
"@angular/platform-browser": "19.2.18",
"@angular/platform-browser-dynamic": "19.2.18",
"@angular/router": "19.2.18",
"dependencies": {
"@angular-eslint/eslint-plugin": "20.7.0",
"@angular/animations": "^21.1.0",
"@angular/common": "^21.1.0",
"@angular/compiler": "^21.1.0",
"@angular/core": "^21.1.0",
"@angular/forms": "^21.1.0",
"@angular/platform-browser": "^21.1.0",
"@angular/platform-browser-dynamic": "^21.1.0",
"@angular/router": "^21.1.0",
"@farmmaps/common": "file:dist/common",
"@farmmaps/common-map": "file:dist/common-map",
"@farmmaps/common-map3d": "file:dist/common-map3d",
"@farmmaps/ng-openlayers": "file:dist/ng-openlayers",
"@microsoft/signalr": "^3.1.16",
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
"@ngrx/effects": "19.2.1",
"@ngrx/router-store": "19.2.1",
"@ngrx/store": "19.2.1",
"@popperjs/core": "^2.11.8",
"angular-oauth2-oidc": "^17.0.2",
"assert": "^2.0.0",
"bootstrap": "^5.3.3",
"@microsoft/signalr": "^10.0.0",
"@ng-bootstrap/ng-bootstrap": "^20.0.0",
"@ngrx/effects": "^21.0.1",
"@ngrx/router-store": "^21.0.1",
"@ngrx/store": "^21.0.1",
"@popperjs/core": "2.11.8",
"assert": "^2.1.0",
"bootstrap": "^5.3.8",
"browserify-zlib": "^0.2.0",
"buffer": "^6.0.3",
"cesium": "^1.97.0",
"core-js": "^2.6.12",
"core-js": "^3.48.0",
"https-browserify": "^1.0.0",
"moment": "^2.29.4",
"ngrx-store-localstorage": "19.0.2",
"ngx-avatars": "1.9.3",
"ngrx-store-localstorage": "^20.1.0",
"ngx-avatars": "1.10.1",
"ngx-clipboard": "^16.0.0",
"ngx-image-cropper": "^7.0.0",
"ngx-uploadx": "7.0.1",
"ngx-uploadx": "^7.0.1",
"ol": "^8.2.0",
"olcs": "^2.13.1",
"resumablejs": "^1.1.0",
"rxjs": "^7.8.1",
"rxjs": "^7.8.0",
"stream": "^0.0.2",
"stream-http": "^3.2.0",
"tassign": "^1.0.0",
@@ -56,42 +55,43 @@
"util": "^0.12.4",
"zone.js": "~0.15.1"
},
"optionalDependencies": {
"optionalDependencies": {
"@lmdb/lmdb-linux-x64": "^3.1.0",
"@rollup/rollup-linux-x64-gnu": "^4.21.2"
},
"devDependencies": {
"@angular-builders/custom-webpack": "19.0.1",
"@angular-devkit/build-angular": "19.2.19",
"@angular/cli": "19.2.19",
"@angular/compiler-cli": "19.2.18",
"@angular/language-service": "19.2.18",
"@angular/localize": "19.2.18",
"devDependencies": {
"@angular-builders/custom-webpack": "^21.0.3",
"@angular-devkit/build-angular": "^21.1.0",
"@angular/build": "^21.1.0",
"@angular/cli": "^21.1.0",
"@angular/compiler-cli": "^21.1.0",
"@angular/language-service": "^21.1.0",
"@angular/localize": "^21.1.0",
"@types/arcgis-rest-api": "^10.4.5",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "^2.0.9",
"@types/node": "^22.5.4",
"@typescript-eslint/eslint-plugin": "^6.18.0",
"@typescript-eslint/eslint-plugin-tslint": "^6.18.0",
"@typescript-eslint/parser": "^6.18.0",
"codelyzer": "^6.0.2",
"eslint": "^8.35.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-import": "^2.27.5",
"jasmine-core": "^4.3.0",
"jasmine-spec-reporter": "^7.0.0",
"karma": "^6.3.20",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "^3.0.3",
"karma-jasmine": "^5.1.0",
"karma-jasmine-html-reporter": "^2.0.0",
"ng-packagr": "^19.2.2",
"protractor": "~7.0.0",
"@typescript-eslint/eslint-plugin": "^8.53.1",
"@typescript-eslint/parser": "^8.53.1",
"@vitest/browser": "^4.0.17",
"@vitest/browser-playwright": "^4.0.17",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0",
"jsdom": "^27.4.0",
"ng-packagr": "^21.1.0",
"ts-node": "^8.8.1",
"typescript": "~5.8.3"
"typescript": "~5.9.3",
"vitest": "^4.0.17"
},
"overrides": {
"some-library": {
"overrides": {
"ngrx-store-localstorage": {
"@angular/common": "$@angular/common",
"@angular/core": "$@angular/core"
},
"ngx-avatars": {
"@angular/common": "$@angular/common",
"@angular/core": "$@angular/core"
},
"ngx-uploadx": {
"@angular/common": "$@angular/common",
"@angular/core": "$@angular/core"
}

View File

@@ -11,11 +11,11 @@
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": "^18.2.3",
"@ngrx/effects": "^18.0.2",
"@ngrx/router-store": "^18.0.2",
"@ngrx/store": "^18.0.2",
"ngrx-store-localstorage": "^18",
"@angular/core": "20.3.16",
"@ngrx/effects": "20.1.0",
"@ngrx/router-store": "20.1.0",
"@ngrx/store": "20.1.0",
"ngrx-store-localstorage": "20.1.0",
"tassign": "^1.0.0"
}
},
@@ -36,52 +36,64 @@
"extraneous": true
},
"node_modules/@angular/common": {
"version": "18.2.3",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.3.tgz",
"integrity": "sha512-NFL4yXXImSCH7i1xnHykUjHa9vl9827fGiwSV2mnf7LjSUsyDzFD8/54dNuYN9OY8AUD+PnK0YdNro6cczVyIA==",
"version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.16.tgz",
"integrity": "sha512-GRAziNlntwdnJy3F+8zCOvDdy7id0gITjDnM6P9+n2lXvtDuBLGJKU3DWBbvxcCjtD6JK/g/rEX5fbCxbUHkQQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/core": "18.2.3",
"@angular/core": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/core": {
"version": "18.2.3",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.3.tgz",
"integrity": "sha512-VGhMJxj7d0rYpqVfQrcGRB7EE/BCziotft/I/YPl6bOMPSAvMukG7DXQuJdYpNrr62ks78mlzHlZX/cdmB9Prw==",
"version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.16.tgz",
"integrity": "sha512-KSFPKvOmWWLCJBbEO+CuRUXfecX2FRuO0jNi9c54ptXMOPHlK1lIojUnyXmMNzjdHgRug8ci9qDuftvC2B7MKg==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/compiler": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0",
"zone.js": "~0.14.10"
"zone.js": "~0.15.0"
},
"peerDependenciesMeta": {
"@angular/compiler": {
"optional": true
},
"zone.js": {
"optional": true
}
}
},
"node_modules/@angular/platform-browser": {
"version": "18.2.3",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.3.tgz",
"integrity": "sha512-M2ob4zN7tAcL2mx7U6KnZNqNFPFl9MlPBE0FrjQjIzAjU0wSYPIJXmaPu9aMUp9niyo+He5iX98I+URi2Yc99g==",
"version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.16.tgz",
"integrity": "sha512-YsrLS6vyS77i4pVHg4gdSBW74qvzHjpQRTVQ5Lv/OxIjJdYYYkMmjNalCNgy1ZuyY6CaLIB11ccxhrNnxfKGOQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/animations": "18.2.3",
"@angular/common": "18.2.3",
"@angular/core": "18.2.3"
"@angular/animations": "20.3.16",
"@angular/common": "20.3.16",
"@angular/core": "20.3.16"
},
"peerDependenciesMeta": {
"@angular/animations": {
@@ -90,76 +102,67 @@
}
},
"node_modules/@angular/router": {
"version": "18.2.3",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.3.tgz",
"integrity": "sha512-fvD9eSDIiIbeYoUokoWkXzu7/ZaxlzKPUHFqX1JuKuH5ciQDeT/d7lp4mj31Bxammhohzi3+z12THJYsCkj/iQ==",
"version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.16.tgz",
"integrity": "sha512-e1LiQFZaajKqc00cY5FboIrWJZSMnZ64GDp5R0UejritYrqorQQQNOqP1W85BMuY2owibMmxVfX+dJg/Mc8PuQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/common": "18.2.3",
"@angular/core": "18.2.3",
"@angular/platform-browser": "18.2.3",
"@angular/common": "20.3.16",
"@angular/core": "20.3.16",
"@angular/platform-browser": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@ngrx/effects": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-18.0.2.tgz",
"integrity": "sha512-YojXcOD9Lsq4kl2HCjENccyUM/mOlgBdtddsg9j/ojzSUgu3ZuBVKLN3atrL2TJYkbMX1MN0RzafSkL3TPGFIA==",
"version": "20.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-20.1.0.tgz",
"integrity": "sha512-wkIu0QnTarBd1zUswk643H5SILO9Be1OeswMe7g4tXlkNLFRPkyu0BUzXT80PsUPP5p0VrdFz3akrn3HHjjTjA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@ngrx/operators": "18.0.1",
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": "^18.0.0",
"@ngrx/store": "18.0.2",
"@angular/core": "^20.0.0",
"@ngrx/store": "20.1.0",
"rxjs": "^6.5.3 || ^7.5.0"
}
},
"node_modules/@ngrx/operators": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@ngrx/operators/-/operators-18.0.1.tgz",
"integrity": "sha512-M+QMrHNKgcuiLaRGZxJ4aQi5/OCRfKC4+T/63dsHyLFZ53/FFpF6a/ytSO1Q+tzOplZ5o99S+i8FVaZqNQ3LmQ==",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@ngrx/router-store": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-18.0.2.tgz",
"integrity": "sha512-jUrQ/uJJ53x8O1XbN2YxH2GpRREZlwS5gRxlCoc4fWL4Us/uS1/K6+QfRmKBPtpTKBIixqsOb+dIUV5iwBrivA==",
"version": "20.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-20.1.0.tgz",
"integrity": "sha512-XlpQsgmEc1E9Ogiv6QbJ8g8JzjrJOlr7JX9FngHIDLyXQFCMn5wcXsqhjiBjG9JgJLwlzV+v14i7EM/nqwQCGA==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/common": "^18.0.0",
"@angular/core": "^18.0.0",
"@angular/router": "^18.0.0",
"@ngrx/store": "18.0.2",
"@angular/common": "^20.0.0",
"@angular/core": "^20.0.0",
"@angular/router": "^20.0.0",
"@ngrx/store": "20.1.0",
"rxjs": "^6.5.3 || ^7.5.0"
}
},
"node_modules/@ngrx/store": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-18.0.2.tgz",
"integrity": "sha512-ajwv0+njsO4vzArp9esnFvs1wyUb1U1W8E8LSCKrcW2hWWo9o1Pezj+JRsdQwatxHfrrPFuTDyajsl6GQM/JSA==",
"version": "20.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-20.1.0.tgz",
"integrity": "sha512-o8j3CGAGedm+BIb+QDhNXrVaU//n9uF0wH0HZWtXHmW1mjRBaQiUA+ZPMUkDwAeN8KuOcoIEC+2QUXxXGVI7ow==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": "^18.0.0",
"@angular/core": "^20.0.0",
"rxjs": "^6.5.3 || ^7.5.0"
}
},
@@ -173,18 +176,19 @@
}
},
"node_modules/ngrx-store-localstorage": {
"version": "18.0.0",
"resolved": "https://registry.npmjs.org/ngrx-store-localstorage/-/ngrx-store-localstorage-18.0.0.tgz",
"integrity": "sha512-WoDePvMWiWF9LQHe+dTqbpm8lxoKCPoIvA0/1enIPTmdLQsOpdDKhMSD5YgwuqDusNfEik3QslProTFGyXZwtw==",
"version": "20.1.0",
"resolved": "https://registry.npmjs.org/ngrx-store-localstorage/-/ngrx-store-localstorage-20.1.0.tgz",
"integrity": "sha512-/5+i5qTxZdE8Q5qdSmj7+9JvriAnHwW7RsXzh1rrQ/UHA9vf12q6mJ6wYTTehUO4Qcl2t/K5MRkooN2eG2ZEvw==",
"license": "MIT",
"peer": true,
"dependencies": {
"deepmerge": "^4.2.2",
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "^18.0.1",
"@angular/core": "^18.0.1",
"@ngrx/store": "^18.0.0"
"@angular/common": ">=20.0.0",
"@angular/core": ">=20.0.0",
"@ngrx/store": ">=20.0.0"
}
},
"node_modules/rxjs": {
@@ -206,12 +210,6 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="
},
"node_modules/zone.js": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz",
"integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==",
"peer": true
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@farmmaps/common-map",
"version": "2.0.0",
"version": "4.22.0",
"publishConfig": {
"registry": "https://repository.akkerweb.nl/repository/npm-hosted/"
},
@@ -8,11 +8,16 @@
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": "19.2.18",
"ngrx-store-localstorage": "19.0.2",
"@ngrx/effects": "19.2.1",
"@ngrx/router-store": "19.2.1",
"@ngrx/store": "19.2.1",
"@angular/core": "^21.1.0",
"ngrx-store-localstorage": "^20.1.0",
"@ngrx/effects": "^21.0.1",
"@ngrx/router-store": "^21.0.1",
"@ngrx/store": "^21.0.1",
"tassign": "^1.0.0"
},
"overrides": {
"ngrx-store-localstorage": {
"@angular/core": "$@angular/core"
}
}
}

View File

@@ -1,31 +1,50 @@
<div>
<div class="layerlist" *ngIf="itemLayers.length > 0;else noLayers">
<div class="list-group">
<div *ngFor="let itemLayer of itemLayers" class="list-group-item list-group-item-action p-2 text-truncate" [ngClass]="{'active' : selectedLayer==itemLayer}">
<div (click)="handleSelectLayer($event,itemLayer)" [title]="itemLayer.item.name">{{itemLayer.item.name}}</div>
<div class="mt-1" *ngIf="selectedLayer==itemLayer && !baseLayers">
<span class="btn-group">
<a title="Toggle visibility"href="#" class="btn btn-light btn-sm" (click)="handleToggleVisibility($event,itemLayer)"><i class="fa" aria-hidden="true" [ngClass]="{'fa-eye':!itemLayer.visible,'fa-eye-slash':itemLayer.visible}"></i></a>
<a title="Transparency 25%" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.25)">25%</a>
<a title="Transparency 50%" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.5)">50%</a>
<a title="Transparency 75%" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.75)">75%</a>
<a title="No transparency" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,1)">100%</a>
</span>
<a href="#" title="Zoom to extent" class="btn btn-light btn-sm" (click)="handleZoomToExtent($event,itemLayer)"><i class="far fa-search-plus" aria-hidden="true"></i></a>
<span *ngIf="firstLayer(itemLayer)"><a href="#" title="Toggle legend" class="btn btn-light btn-sm" (click)="itemLayer.legendVisible=toggleLegend($event,itemLayer.legendVisible)"><i class="far fa-chart-bar" aria-hidden="true"></i></a></span>
<span *ngIf="!dataLayers" class="float-end"><a href="#" title="Remove overlay" class="btn btn-light btn-sm" (click)="handleDelete($event,itemLayer)"><i class="fas fa-layer-minus" aria-hidden="true"></i></a></span>
</div>
<div *ngIf="itemLayer.legendVisible">
<div class="card legend">
<fm-map-layer-legend [layer]="firstLayer(itemLayer)" (click)="handleLegendClick($event,itemLayer);" [histogramenabled]="true"></fm-map-layer-legend>
</div>
</div>
</div>
</div>
</div>
</div>
<ng-template #noLayers>
<div class="list-group">
<div class="list-group-item" i18n>No layers</div>
</div>
</ng-template>
<div>
@if (itemLayers.length > 0) {
<div class="layerlist">
<div class="list-group">
@for (itemLayer of itemLayers; track itemLayer.item.code) {
<div class="list-group-item list-group-item-action p-2 text-truncate" [ngClass]="{'active' : selectedLayer==itemLayer}">
<div (click)="handleSelectLayer($event,itemLayer)" [title]="itemLayer.item.name">{{itemLayer.item.name}}</div>
@if (selectedLayer==itemLayer && !baseLayers) {
<div class="mt-1">
<span class="btn-group">
<a title="Toggle visibility"href="#" class="btn btn-light btn-sm" (click)="handleToggleVisibility($event,itemLayer)"><i class="fa" aria-hidden="true" [ngClass]="{'fa-eye':!itemLayer.visible,'fa-eye-slash':itemLayer.visible}"></i></a>
@if (itemLayer.visible) {
<a title="Transparency 25%" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.25)">25%</a>
}
@if (itemLayer.visible) {
<a title="Transparency 50%" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.5)">50%</a>
}
@if (itemLayer.visible) {
<a title="Transparency 75%" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.75)">75%</a>
}
@if (itemLayer.visible) {
<a title="No transparency" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,1)">100%</a>
}
</span>
<a href="#" title="Zoom to extent" class="btn btn-light btn-sm" (click)="handleZoomToExtent($event,itemLayer)"><i class="far fa-search-plus" aria-hidden="true"></i></a>
@if (firstLayer(itemLayer)) {
<span><a href="#" title="Toggle legend" class="btn btn-light btn-sm" (click)="itemLayer.legendVisible=toggleLegend($event,itemLayer.legendVisible)"><i class="far fa-chart-bar" aria-hidden="true"></i></a></span>
}
@if (!dataLayers) {
<span class="float-end"><a href="#" title="Remove overlay" class="btn btn-light btn-sm" (click)="handleDelete($event,itemLayer)"><i class="fas fa-layer-minus" aria-hidden="true"></i></a></span>
}
</div>
}
@if (itemLayer.legendVisible) {
<div>
<div class="card legend">
<fm-map-layer-legend [layer]="firstLayer(itemLayer)" (click)="handleLegendClick($event,itemLayer);" [histogramenabled]="true"></fm-map-layer-legend>
</div>
</div>
}
</div>
}
</div>
</div>
} @else {
<div class="list-group">
<div class="list-group-item" i18n>No layers</div>
</div>
}
</div>

View File

@@ -1,18 +1,27 @@
<div #layerValues class="layer-values">
<div class="cross" *ngIf="enabled$ | async">
<div class="pointer"></div>
<div class="values-container border border-dark rounded p-2" *ngIf="(layerValues$ | async ) as layers">
<div class="lonlat pb-2 "><span class="font-weight-bold">{{lonlat$}}</span><i class="ms-2 fal fa-copy" (click)="copyToClipboard()"></i> </div>
<ul class="value-list p-0 mb-0" *ngIf="layers.length>0 ;else no_data">
<li class="border-top pt-1 pb-1 value" *ngFor="let layerValue of layers">
<div>{{layerValue.layerName}}</div>
<div>{{layerValue.date|date}}</div>
<div><span *ngIf="layerValue.quantity"><span class="me-1">{{layerValue.quantity}}</span> </span><span class="me-1 font-weight-bold">{{getScaledValue(layerValue)}}</span><span>{{layerValue.unit}}</span></div>
</li>
</ul>
<ng-template #no_data>
<div i18n class="border-top pt-1 pb-1">No data at location</div>
</ng-template>
</div>
</div>
</div>
@if (enabled$ | async) {
<div class="cross" >
<div class="pointer"></div>
@if ((layerValues$ | async ); as layers) {
<div class="values-container border border-dark rounded p-2">
<div class="lonlat pb-2 "><span class="font-weight-bold">{{lonlat$}}</span><i class="ms-2 fal fa-copy" (click)="copyToClipboard()"></i> </div>
@if (layers.length>0 ) {
<ul class="value-list p-0 mb-0">
@for (layerValue of layers; track $index) {
<li class="border-top pt-1 pb-1 value">
<div>{{layerValue.layerName}}</div>
<div>{{layerValue.date|date}}</div>
<div>@if (layerValue.quantity) {
<span><span class="me-1">{{layerValue.quantity}}</span> </span>
}<span class="me-1 font-weight-bold">{{getScaledValue(layerValue)}}</span><span>{{layerValue.unit}}</span></div>
</li>
}
</ul>
} @else {
<div i18n class="border-top pt-1 pb-1">No data at location</div>
}
</div>
}
</div>
}
</div>

View File

@@ -1,14 +1,20 @@
<div class="card border-0">
<div class="card-body" *ngIf="(schemeItem|async);let schemeItem">
<fm-back-button></fm-back-button>
<h4 i18n>Farm</h4>
<h3>{{schemeItem.name}}</h3>
<div *ngIf="features;let features">
<div class="cropfields">
<div class="row m-0 ps-3 pe-3" *ngFor="let feature of features" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
</div>
</div>
</div>
</div>
<div class="card border-0">
@if ((schemeItem|async); as schemeItem) {
<div class="card-body">
<fm-back-button></fm-back-button>
<h4 i18n>Farm</h4>
<h3>{{schemeItem.name}}</h3>
@if (features; as features) {
<div>
<div class="cropfields">
@for (feature of features; track $index) {
<div class="row m-0 ps-3 pe-3" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
}
</div>
</div>
}
</div>
}
</div>

View File

@@ -1,13 +1,17 @@
<div class="card border-0">
<div class="card-body">
<fm-back-button></fm-back-button>
<h3><i class="far fa-farm"></i>&nbsp;<span i18n>Farms</span></h3>
<div *ngIf="features;let features">
<div class="farms">
<div class="row m-0 ps-3 pe-3" *ngFor="let feature of features"[ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
</div>
</div>
</div>
</div>
<div class="card border-0">
<div class="card-body">
<fm-back-button></fm-back-button>
<h3><i class="far fa-farm"></i>&nbsp;<span i18n>Farms</span></h3>
@if (features; as features) {
<div>
<div class="farms">
@for (feature of features; track $index) {
<div class="row m-0 ps-3 pe-3"[ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
}
</div>
</div>
}
</div>
</div>

View File

@@ -1,14 +1,16 @@
<div *ngIf="feature;let feature" class="d-flex m-0">
<div class="p-2">
<div class="thumbnail">
<fm-map-feature-thumbnail [feature]="feature"></fm-map-feature-thumbnail>
</div>
</div>
<div class="flex-grow p-2 overflow-hidden">
<h1 class="card-title" title="{{feature.get('name')}}">{{feature.get('name')}}</h1>
<div class="card-text"><span>{{areaInHa(feature)| number:'1.2-2'}}
ha</span>&nbsp;<span>{{feature.get('cropTypeName')}}</span> </div>
<div class="card-text"><span>{{feature.get('datadate')|date}}</span> -
<span>{{feature.get('dataenddate')|date}}</span> </div>
</div>
</div>
@if (feature; as feature) {
<div class="d-flex m-0">
<div class="p-2">
<div class="thumbnail">
<fm-map-feature-thumbnail [feature]="feature"></fm-map-feature-thumbnail>
</div>
</div>
<div class="flex-grow p-2 overflow-hidden">
<h1 class="card-title" title="{{feature.get('name')}}">{{feature.get('name')}}</h1>
<div class="card-text"><span>{{areaInHa(feature)| number:'1.2-2'}}
ha</span>&nbsp;<span>{{feature.get('cropTypeName')}}</span> </div>
<div class="card-text"><span>{{feature.get('datadate')|date}}</span> -
<span>{{feature.get('dataenddate')|date}}</span> </div>
</div>
</div>
}

View File

@@ -1,6 +1,8 @@
<div *ngIf="feature;let feature" class="row m-0">
@if (feature; as feature) {
<div class="row m-0">
<div class="col p-2">
<h1 class="card-title" title="{{feature.get('name')}}">{{feature.get('name')}}</h1>
<div class="card-text">{{feature.get('datadate')|date:'shortDate'}}</div>
</div>
</div>
</div>
}

View File

@@ -1,12 +1,18 @@
<div *ngIf="feature;let feature" class="row m-0">
@if (feature; as feature) {
<div class="row m-0">
<div class="col-3 w-25 m-0 p-2 thumbnail">
<img *ngIf="feature.get('thumbnail')" [src]="config.getConfig('apiEndPoint') + '/api/v1/items/'+feature.get('code')+'/thumbnail'" />
<div *ngIf="!feature.get('thumbnail')" [style.background-color]="itemTypeService.getColor(feature.get('itemType'))">
<i [ngClass]="itemTypeService.getIcon(feature.get('itemType'))"></i>
</div>
@if (feature.get('thumbnail')) {
<img [src]="config.getConfig('apiEndPoint') + '/api/v1/items/'+feature.get('code')+'/thumbnail'" />
}
@if (!feature.get('thumbnail')) {
<div [style.background-color]="itemTypeService.getColor(feature.get('itemType'))">
<i [ngClass]="itemTypeService.getIcon(feature.get('itemType'))"></i>
</div>
}
</div>
<div class="col p-2">
<h1 class="card-title" title="{{feature.get('name')}}"><i [ngClass]="itemTypeService.getIcon(feature.get('itemType'))" [style.color]="itemTypeService.getColor(feature.get('itemType'))"></i>&nbsp;{{feature.get('name')}}</h1>
<div class="card-text">{{feature.get('datadate')|date:'shortDate'}}</div>
</div>
</div>
</div>
}

View File

@@ -1,14 +1,20 @@
<div class="card border-0">
<div class="card-body" *ngIf="(schemeItem|async);let schemeItem">
<fm-back-button></fm-back-button>
<h4 i18n>Farm</h4>
<h3>{{schemeItem.name}}</h3>
<div *ngIf="features;let features">
<div class="cropfields">
<div class="row m-0 ps-3 pe-3" *ngFor="let feature of features" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
</div>
</div>
</div>
</div>
<div class="card border-0">
@if ((schemeItem|async); as schemeItem) {
<div class="card-body">
<fm-back-button></fm-back-button>
<h4 i18n>Farm</h4>
<h3>{{schemeItem.name}}</h3>
@if (features; as features) {
<div>
<div class="cropfields">
@for (feature of features; track $index) {
<div class="row m-0 ps-3 pe-3" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
}
</div>
</div>
}
</div>
}
</div>

View File

@@ -1,6 +1,10 @@
<div *ngIf="features;let features">
<fm-back-button></fm-back-button>
<div class="row m-0 ps-3 pe-3" *ngFor="let feature of features" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
</div>
@if (features; as features) {
<div>
<fm-back-button></fm-back-button>
@for (feature of features; track $index) {
<div class="row m-0 ps-3 pe-3" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
}
</div>
}

View File

@@ -1,4 +1,6 @@
<div *ngIf="item;let item" class="widget" [style.background-color]="itemTypeService.getColor(item.itemType)">
<div class="align-middle icon" [ngClass]="itemTypeService.getIcon(item.itemType)"></div>
<div class="title">{{item.name}}</div>
</div>
@if (item; as item) {
<div class="widget" [style.background-color]="itemTypeService.getColor(item.itemType)">
<div class="align-middle icon" [ngClass]="itemTypeService.getIcon(item.itemType)"></div>
<div class="title">{{item.name}}</div>
</div>
}

View File

@@ -1,7 +1,11 @@
<div *ngIf="items;let items" class="widget-container pt-0">
<div class="widget" *ngFor="let item of items" (click)="handleItemClick(item)">
<div class="content">
<fm-map-item-list-item-container [item]="item" class="item-container"></fm-map-item-list-item-container>
</div>
@if (items; as items) {
<div class="widget-container pt-0">
@for (item of items; track item.code) {
<div class="widget" (click)="handleItemClick(item)">
<div class="content">
<fm-map-item-list-item-container [item]="item" class="item-container"></fm-map-item-list-item-container>
</div>
</div>
}
</div>
</div>
}

View File

@@ -1,7 +1,11 @@
<div *ngIf="widgets;let widgets" class="widget-container pt-0">
<div class="widget" *ngFor="let widget of widgets">
<div class="content">
<ng-template #widgetTemplate></ng-template>
</div>
@if (widgets; as widgets) {
<div class="widget-container pt-0">
@for (widget of widgets; track widget.item.code) {
<div class="widget">
<div class="content">
<ng-template #widgetTemplate></ng-template>
</div>
</div>
}
</div>
</div>
}

View File

@@ -1,37 +1,39 @@
<div class="btn btn-outline-primary layer-switcher" (click)="handleClick($event)">
<i class="fal fa-layer-group"></i>
<div class="layers hidden" [ngClass]="{'hidden':!(showLayerSwitcher|async)}" (click)="$event.stopPropagation();">
<div class="card layers-card m-1">
<div class="card-header">
<span i18n>Layers</span>
<i class="float-end fas fa-times" (click)="close($event)"></i>
</div>
<ul class="navbar-nav">
<li class="nav-item py-0">
<span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Base maps</span></span>
<div class="mb-4">
<fm-map-layer-list [baseLayers]="true" [itemLayers]="baseMaps|async" [selectedLayer]="selectedBaseLayer|async" (onSelectLayer)="handleSelectBaseLayer($event)"></fm-map-layer-list>
</div>
</li>
<li class="nav-item py-0">
<span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Overlays</span></span>
<div class="mb-4">
<fm-map-layer-list [itemLayers]="overlayLayers|async" [selectedLayer]="selectedOverlayLayer|async" (onDelete)="handleOnDelete($event)" (onToggleVisibility)="handleOnToggleVisibility($event)" (onSetOpacity)="handleOnSetOpacity($event)" (onZoomToExtent)="handleZoomToExtent($event)" (onSelectLayer)="handleSelectOverlayLayer($event)"></fm-map-layer-list>
</div>
</li>
<li class="nav-item py-0" *ngIf="selectedItemLayer$ | async as selectedItemLayer">
<span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Overlay slider</span><span class="slideButton"><a href="#" title="Compare" class="btn btn-light btn-sm" (click)="handleToggleShowDatalayerSlide($event)"><i class="fal fa-sliders-h-square"></i></a></span></span>
<div class="mb-4">
<fm-map-layer-list [dataLayers]="true" [itemLayers]="[selectedItemLayer]" [selectedLayer]="selectedItemLayer" (onToggleVisibility)="handleOnToggleVisibility($event)" (onSetOpacity)="handleOnSetOpacity($event)" (onZoomToExtent)="handleZoomToExtent($event)" (onSelectLayer)="handleSelectOverlayLayer($event)"></fm-map-layer-list>
</div>
</li>
<li class="nav-item py-0">
<span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Aerial photos</span></span>
<div class="mb-4">
<fm-map-layer-list [baseLayers]="true" [itemLayers]="aerialMaps|async" [selectedLayer]="selectedBaseLayer|async" (onSelectLayer)="handleSelectBaseLayer($event)"></fm-map-layer-list>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="btn btn-outline-primary layer-switcher" (click)="handleClick($event)">
<i class="fal fa-layer-group"></i>
<div class="layers hidden" [ngClass]="{'hidden':!(showLayerSwitcher|async)}" (click)="$event.stopPropagation();">
<div class="card layers-card m-1">
<div class="card-header">
<span i18n>Layers</span>
<i class="float-end fas fa-times" (click)="close($event)"></i>
</div>
<ul class="navbar-nav">
<li class="nav-item py-0">
<span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Base maps</span></span>
<div class="mb-4">
<fm-map-layer-list [baseLayers]="true" [itemLayers]="baseMaps|async" [selectedLayer]="selectedBaseLayer|async" (onSelectLayer)="handleSelectBaseLayer($event)"></fm-map-layer-list>
</div>
</li>
<li class="nav-item py-0">
<span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Overlays</span></span>
<div class="mb-4">
<fm-map-layer-list [itemLayers]="overlayLayers|async" [selectedLayer]="selectedOverlayLayer|async" (onDelete)="handleOnDelete($event)" (onToggleVisibility)="handleOnToggleVisibility($event)" (onSetOpacity)="handleOnSetOpacity($event)" (onZoomToExtent)="handleZoomToExtent($event)" (onSelectLayer)="handleSelectOverlayLayer($event)"></fm-map-layer-list>
</div>
</li>
@if (selectedItemLayer$ | async; as selectedItemLayer) {
<li class="nav-item py-0">
<span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Overlay slider</span><span class="slideButton"><a href="#" title="Compare" class="btn btn-light btn-sm" (click)="handleToggleShowDatalayerSlide($event)"><i class="fal fa-sliders-h-square"></i></a></span></span>
<div class="mb-4">
<fm-map-layer-list [dataLayers]="true" [itemLayers]="[selectedItemLayer]" [selectedLayer]="selectedItemLayer" (onToggleVisibility)="handleOnToggleVisibility($event)" (onSetOpacity)="handleOnSetOpacity($event)" (onZoomToExtent)="handleZoomToExtent($event)" (onSelectLayer)="handleSelectOverlayLayer($event)"></fm-map-layer-list>
</div>
</li>
}
<li class="nav-item py-0">
<span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Aerial photos</span></span>
<div class="mb-4">
<fm-map-layer-list [baseLayers]="true" [itemLayers]="aerialMaps|async" [selectedLayer]="selectedBaseLayer|async" (onSelectLayer)="handleSelectBaseLayer($event)"></fm-map-layer-list>
</div>
</li>
</ul>
</div>
</div>
</div>

View File

@@ -8,8 +8,8 @@
<div class="col-8 nopadding" ><span i18n>Max</span>:</div>
<div class="col-4 pull-left nopadding">{{ getScaledValue(histogram.max)| number:'1.0-2'}}</div>
<ng-container *ngIf="showConfidenceInterval()">
@if (showConfidenceInterval()) {
<div class="col-8 nopadding"><span i18n>Confidence interval</span>:</div>
<div class="col-4 pull-left nopadding">{{ getScaledValue(histogram.confidence * 100)| number:'1.0-0'}}%</div>
</ng-container>
}
</div>

View File

@@ -1,48 +1,68 @@
<table class="container" *ngIf="showLegend()">
<tr>
<td colspan="3">
<div class="title">
<h4 *ngIf="showTitle">{{layer.name}}</h4>
<b *ngIf="layer.unit">({{layer.unit}})</b>
</div>
</td>
<td>
<a i18n-title title="more info"><i class="fal fa-info-circle text-primary" (click)="hideHistogramDetails = !hideHistogramDetails"></i></a>
</td>
<td colspan="2">
<div class="title" *ngIf="histogramenabled">
<h4>{{histogram}}</h4>
<b *ngIf="histogramunit">({{histogramunit}})</b>
</div>
</td>
</tr>
<tr *ngFor="let entry of layer.renderer.colorMap.entries; let i = index ">
<td class="legend-items"><span [style.background-color]="getAlphaHex(entry.color)" [style.border-color]="getHex(entry.color)"class="color"></span></td>
<td class="legend-items-text"><span *ngIf="!entry.label">{{getScaledValue(entry.value,layer.scale) | number:'1.0-2'}} {{legendunit}}</span><span *ngIf="entry.label">{{entry.label}}</span></td>
<td class="histogram-items">
<div *ngIf="showHistogram()">
<span class="bar" [style.background-color]="getHex(entry.color)" [style.width]="getPart(layer.renderer, i)">
</span>
<span *ngIf="getPercentage(layer.renderer,i) as percentage" class="bar-label">{{percentage | number:'1.0-2'}} %</span>
</div>
</td>
</tr>
<tr *ngIf="showHistogram()">
<td colspan="4" class="pb-1 pt-1">
<div class="info" [ngbCollapse]="hideHistogramDetails">
<ng-container *ngIf="bandContainsStatistics(); else histogram">
<fm-map-statistics-details [statistics]="layer.renderer.band.statistics" [scale]="layer.scale"></fm-map-statistics-details>
</ng-container>
<ng-template #histogram>
<fm-map-histogram-details [histogram]="layer.renderer.band.histogram" [scale]="layer.scale"></fm-map-histogram-details>
</ng-template>
</div>
</td>
</tr>
</table>
@if (showLegend()) {
<table class="container">
<tr>
<td colspan="3">
<div class="title">
@if (showTitle) {
<h4>{{layer.name}}</h4>
}
@if (layer.unit) {
<b>({{layer.unit}})</b>
}
</div>
</td>
<td>
<a i18n-title title="more info"><i class="fal fa-info-circle text-primary" (click)="hideHistogramDetails = !hideHistogramDetails"></i></a>
</td>
<td colspan="2">
@if (histogramenabled) {
<div class="title">
<h4>{{histogram}}</h4>
@if (histogramunit) {
<b>({{histogramunit}})</b>
}
</div>
}
</td>
</tr>
@for (entry of layer.renderer.colorMap.entries; track $index; let i = $index) {
<tr>
<td class="legend-items"><span [style.background-color]="getAlphaHex(entry.color)" [style.border-color]="getHex(entry.color)"class="color"></span></td>
<td class="legend-items-text">@if (!entry.label) {
<span>{{getScaledValue(entry.value,layer.scale) | number:'1.0-2'}} {{legendunit}}</span>
}@if (entry.label) {
<span>{{entry.label}}</span>
}</td>
<td class="histogram-items">
@if (showHistogram()) {
<div>
<span class="bar" [style.background-color]="getHex(entry.color)" [style.width]="getPart(layer.renderer, i)">
</span>
@if (getPercentage(layer.renderer,i); as percentage) {
<span class="bar-label">{{percentage | number:'1.0-2'}} %</span>
}
</div>
}
</td>
</tr>
}
@if (showHistogram()) {
<tr>
<td colspan="4" class="pb-1 pt-1">
<div class="info" [ngbCollapse]="hideHistogramDetails">
@if (bandContainsStatistics()) {
<fm-map-statistics-details [statistics]="layer.renderer.band.statistics" [scale]="layer.scale"></fm-map-statistics-details>
} @else {
<fm-map-histogram-details [histogram]="layer.renderer.band.histogram" [scale]="layer.scale"></fm-map-histogram-details>
}
</div>
</td>
</tr>
}
</table>
}

View File

@@ -29,8 +29,8 @@
<div class="col-4 pull-left nopadding">{{getSquaredScaledValue(statistics.variance)| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Coefficient of variation</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.variationCoefficient | number:'1.0-2'}}</div>
<ng-container *ngIf="statistics.confidenceIntervalLow !== undefined">
@if (statistics.confidenceIntervalLow !== undefined) {
<div class="col-8 nopadding">90% <span i18n>Confidence interval</span>:</div>
<div class="col-4 pull-left nopadding">{{getScaledValue(statistics.confidenceIntervalLow) | number:'1.0-2'}} - {{getScaledValue(statistics.confidenceIntervalHigh) | number:'1.0-2'}}</div>
</ng-container>
}
</div>

View File

@@ -1,95 +1,109 @@
<ng-container *ngIf="{
mapState:mapState$|async,
extent:extent$|async,
baseLayers:baseLayers$|async,
overlayLayers:overlayLayers$|async,
selectedItemLayer:selectedItemLayer$|async,
features:features$|async,
position:position$|async,
parentCode:parentCode$|async,
panelVisible:panelVisible$|async,
openedModalName:openedModalName$|async,
panelCollapsed:panelCollapsed$|async,
panelExtraWide:panelExtraWide$|async,
searchMinified:searchMinified$|async,
selectedItem:selectedItem$|async,
parentItem:parentItem$|async,
queryState:queryState$|async,
searchCollapsed:searchCollapsed$|async,
clearEnabled:clearEnabled$|async,
period:period$|async,
compassHeading:compassHeading$|async,
styles:styles$|async,
selectedFeature:selectedFeature$|async,
fullscreen:fullscreen$|async,
showDataLayerSlide:showDataLayerSlide$|async
} as state">
<aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" (dblClick)="handleShowLayerValues($event)" [ngClass]="{'panel-visible':state.panelVisible,'fullscreen':state.fullscreen }" class="map">
<div>
</div>
<aol-view [zoom]="state.mapState.zoom" [rotation]="state.mapState.rotation">
<aol-coordinate [x]="state.mapState.xCenter" [y]="state.mapState.yCenter" [srid]="'EPSG:4326'"></aol-coordinate>
<fm-map-zoom-to-extent [extent]="state.extent" [animate]="true"></fm-map-zoom-to-extent>
</aol-view>
<aol-interaction-default></aol-interaction-default>
<aol-interaction-dragrotateandzoom></aol-interaction-dragrotateandzoom>
<fm-map-item-layers [itemLayers]="state.baseLayers"></fm-map-item-layers>
<fm-map-item-layers *ngIf="!overrideOverlayLayers" [itemLayers]="state.overlayLayers"></fm-map-item-layers>
<fm-map-item-layers *ngIf="!overrideSelectedItemLayer" [itemLayer]="state.selectedItemLayer" (onPrerender)="handlePrerender($event)"></fm-map-item-layers>
<aol-layer-vector>
<fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeatureSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector>
</aol-layer-vector>
<router-outlet name="map-layers"></router-outlet>
<fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="devicesService.IsMobile()" [showTolerance]="devicesService.IsMobile()" [heading]="state.compassHeading"></fm-map-gps-location>
<fm-map-layer-values></fm-map-layer-values>
<div class="viewport-container" [ngStyle]="{'bottom': bottom(state.panelVisible)}">
<div class="control-container">
<router-outlet name="map-controls"></router-outlet>
<fm-map-layer-switcher></fm-map-layer-switcher>
<fm-map-pan-to-location [position]="state.position" [mapState]="state.mapState" [animate]="true"></fm-map-pan-to-location>
<fm-map-rotation-reset></fm-map-rotation-reset>
</div>
<div class="slide-container" [ngClass]="{'showDataLayerSlide':state.showDataLayerSlide}">
<form>
<input class="dataLayerSlide" (input)="handleSlideChange($event)" type="range" style="width: 100%" value="{{dataLayerSlideValue}}"/>
</form>
</div>
</div>
<fm-map-file-drop-target [parentCode]="state.parentCode" (onFileDropped)="handleFileDropped($event)"></fm-map-file-drop-target>
<div *ngIf="noContent">
<fm-map-map-search #mapSearch [openedModalName]="state.openedModalName" (onOpenModal)="handleOpenModal($event)" (onCloseModal)="handleCloseModal()" [ngClass]="{'menuVisible':state.menuVisible}" (onToggleMenu)="handleToggleMenu($event)" (onSearchCollapse)="handleSearchCollapse($event)" (onSearchExpand)="handleSearchExpand($event)" [collapsed]="state.searchCollapsed" [searchMinified]="state.searchMinified" (onSearch)="handleSearch($event)" (onClear)="handleClearSearch($event)" [filterOptions]="state.queryState" [clearEnabled]="state.clearEnabled" [period]="state.period" (onPeriodChange)="handlePeriodChange($event)" (onCitySearch)="handleCitySearch($event)"></fm-map-map-search>
</div>
<div class="side-panel-container">
<fm-side-panel [resizeable]="true" (onResize)="handlePanelResize($event)" [visible]="state.panelVisible && noContent" [collapsed]="state.panelCollapsed" [collapsable]="false" [extrawide]="state.panelExtraWide">
<div class="panel-wrapper" *ngIf="noContent">
<div class="panel-top bg-secondary" *ngIf="!(state.searchMinified)">
</div>
<div class="panel-bottom">
<div *ngIf="!(state.selectedItem)">
<fm-map-feature-list-container [features]="state.features" [selectedFeature]="state.selectedFeature" [queryState]="state.queryState" [clickedFeature]="clickedFeature"></fm-map-feature-list-container>
</div>
<div *ngIf="state.selectedItem;let item">
<fm-map-selected-item-container [item]="item" [parentItem]="state.parentItem" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container>
</div>
<div *ngIf="state.features.length == 0" class="no-results m-2">
<div *ngIf="state.queryState.query">Cannot find <span>{{state.queryState?.query}}</span></div>
<div *ngIf="state.queryState?.tags">Cannot find tag <span>{{state.queryState?.tags}}</span></div>
</div>
</div>
</div>
</fm-side-panel>
<fm-side-panel [resizeable]="true" [visible]="!noContent" [extrawide]="state.panelExtraWide">
<router-outlet (activate)="handleSidepaneloutletActivate($event)" (deactivate)="handleSidepaneloutletDeactivate($event)"></router-outlet>
</fm-side-panel>
</div>
</aol-map>
</ng-container>
@if ({
mapState:mapState$|async,
extent:extent$|async,
baseLayers:baseLayers$|async,
overlayLayers:overlayLayers$|async,
selectedItemLayer:selectedItemLayer$|async,
features:features$|async,
position:position$|async,
parentCode:parentCode$|async,
panelVisible:panelVisible$|async,
openedModalName:openedModalName$|async,
panelCollapsed:panelCollapsed$|async,
panelExtraWide:panelExtraWide$|async,
searchMinified:searchMinified$|async,
selectedItem:selectedItem$|async,
parentItem:parentItem$|async,
queryState:queryState$|async,
searchCollapsed:searchCollapsed$|async,
clearEnabled:clearEnabled$|async,
period:period$|async,
compassHeading:compassHeading$|async,
styles:styles$|async,
selectedFeature:selectedFeature$|async,
fullscreen:fullscreen$|async,
showDataLayerSlide:showDataLayerSlide$|async,
menuVisible:menuVisible$|async,
}; as state) {
<aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" (dblClick)="handleShowLayerValues($event)" [ngClass]="{'panel-visible':state.panelVisible,'fullscreen':state.fullscreen }" class="map">
<div>
</div>
<aol-view [zoom]="state.mapState.zoom" [rotation]="state.mapState.rotation">
<aol-coordinate [x]="state.mapState.xCenter" [y]="state.mapState.yCenter" [srid]="'EPSG:4326'"></aol-coordinate>
<fm-map-zoom-to-extent [extent]="state.extent" [animate]="true"></fm-map-zoom-to-extent>
</aol-view>
<aol-interaction-default></aol-interaction-default>
<aol-interaction-dragrotateandzoom></aol-interaction-dragrotateandzoom>
<fm-map-item-layers [itemLayers]="state.baseLayers"></fm-map-item-layers>
@if (!overrideOverlayLayers) {
<fm-map-item-layers [itemLayers]="state.overlayLayers"></fm-map-item-layers>
}
@if (!overrideSelectedItemLayer) {
<fm-map-item-layers [itemLayer]="state.selectedItemLayer" (onPrerender)="handlePrerender($event)"></fm-map-item-layers>
}
<aol-layer-vector>
<fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeatureSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector>
</aol-layer-vector>
<router-outlet name="map-layers"></router-outlet>
<fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="devicesService.IsMobile()" [showTolerance]="devicesService.IsMobile()" [heading]="state.compassHeading"></fm-map-gps-location>
<fm-map-layer-values></fm-map-layer-values>
<div class="viewport-container" [ngStyle]="{'bottom': bottom(state.panelVisible)}">
<div class="control-container">
<router-outlet name="map-controls"></router-outlet>
<fm-map-layer-switcher></fm-map-layer-switcher>
<fm-map-pan-to-location [position]="state.position" [mapState]="state.mapState" [animate]="true"></fm-map-pan-to-location>
<fm-map-rotation-reset></fm-map-rotation-reset>
</div>
<div class="slide-container" [ngClass]="{'showDataLayerSlide':state.showDataLayerSlide}">
<form>
<input class="dataLayerSlide" (input)="handleSlideChange($event)" type="range" style="width: 100%" value="{{dataLayerSlideValue}}"/>
</form>
</div>
</div>
<fm-map-file-drop-target [parentCode]="state.parentCode" (onFileDropped)="handleFileDropped($event)"></fm-map-file-drop-target>
@if (noContent) {
<div>
<fm-map-map-search #mapSearch [openedModalName]="state.openedModalName" (onOpenModal)="handleOpenModal($event)" (onCloseModal)="handleCloseModal()" [ngClass]="{'menuVisible':state.menuVisible}" (onToggleMenu)="handleToggleMenu($event)" (onSearchCollapse)="handleSearchCollapse($event)" (onSearchExpand)="handleSearchExpand($event)" [collapsed]="state.searchCollapsed" [searchMinified]="state.searchMinified" (onSearch)="handleSearch($event)" (onClear)="handleClearSearch($event)" [filterOptions]="state.queryState" [clearEnabled]="state.clearEnabled" [period]="state.period" (onPeriodChange)="handlePeriodChange($event)" (onCitySearch)="handleCitySearch($event)"></fm-map-map-search>
</div>
}
<div class="side-panel-container">
<fm-side-panel [resizeable]="true" (onResize)="handlePanelResize($event)" [visible]="state.panelVisible && noContent" [collapsed]="state.panelCollapsed" [collapsable]="false" [extrawide]="state.panelExtraWide">
@if (noContent) {
<div class="panel-wrapper">
@if (!(state.searchMinified)) {
<div class="panel-top bg-secondary">
</div>
}
<div class="panel-bottom">
@if (!(state.selectedItem)) {
<div>
<fm-map-feature-list-container [features]="state.features" [selectedFeature]="state.selectedFeature" [queryState]="state.queryState" [clickedFeature]="clickedFeature"></fm-map-feature-list-container>
</div>
}
@if (state.selectedItem; as item) {
<div>
<fm-map-selected-item-container [item]="item" [parentItem]="state.parentItem" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container>
</div>
}
@if (state.features.length == 0) {
<div class="no-results m-2">
@if (state.queryState.query) {
<div>Cannot find <span>{{state.queryState?.query}}</span></div>
}
@if (state.queryState?.tags) {
<div>Cannot find tag <span>{{state.queryState?.tags}}</span></div>
}
</div>
}
</div>
</div>
}
</fm-side-panel>
<fm-side-panel [resizeable]="true" [visible]="!noContent" [extrawide]="state.panelExtraWide">
<router-outlet (activate)="handleSidepaneloutletActivate($event)" (deactivate)="handleSidepaneloutletDeactivate($event)"></router-outlet>
</fm-side-panel>
</div>
</aol-map>
}

View File

@@ -1,19 +1,23 @@
<div class="spacer"></div>
<div *ngIf="item;let item">
<div class="card border-0">
<div class="card-body">
<fm-back-button></fm-back-button>
<div class="card menu-card">
<h1>Cropfield</h1>
<h4>{{item.name}}</h4>
<div class="card-text"><span>{{areaInHa(item)| number:'1.2-2'}} ha</span>&nbsp;<span>{{item.data.cropTypeName}}</span></div>
<div class="card-text"><span>{{item.data.startDate|date}}</span> - <span>{{item.data.endDate|date}}</span> </div>
<ul class="p-0 mt-2">
<li *ngIf="item.isEditable"><a href="#" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
</ul>
@if (item; as item) {
<div>
<div class="card border-0">
<div class="card-body">
<fm-back-button></fm-back-button>
<div class="card menu-card">
<h1>Cropfield</h1>
<h4>{{item.name}}</h4>
<div class="card-text"><span>{{areaInHa(item)| number:'1.2-2'}} ha</span>&nbsp;<span>{{item.data.cropTypeName}}</span></div>
<div class="card-text"><span>{{item.data.startDate|date}}</span> - <span>{{item.data.endDate|date}}</span> </div>
<ul class="p-0 mt-2">
@if (item.isEditable) {
<li><a href="#" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
}
</ul>
</div>
</div>
<fm-map-item-list [items]="(items|async)"></fm-map-item-list>
<fm-map-item-widget-list [item]="item"></fm-map-item-widget-list>
</div>
<fm-map-item-list [items]="(items|async)"></fm-map-item-list>
<fm-map-item-widget-list [item]="item"></fm-map-item-widget-list>
</div>
</div>
}

View File

@@ -1,37 +1,51 @@
<div class="spacer"></div>
<div *ngIf="item;let item">
<div class="card border-0">
<div class="card-body">
<fm-back-button></fm-back-button>
<div class="card menu-card">
<h2 *ngIf="parentOfItemType('vnd.farmmaps.itemtype.cropfield')">{{parentItem.name}}</h2>
<h1>{{item.name}}</h1>
</div>
<ng-container *ngIf="item?.data.layers;let layers">
<div class="legend-container">
<div class="card menu-card">
<div *ngIf="layers.length>1">
<select (change)="onLayerChanged($event.target.value)">
<option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
</select>
</div>
<fm-map-layer-legend [showTitle]="layers.length == 1"
[layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
</div>
</div>
<div class="card menu-card">
<ul class="p-0 mt-2">
<li *ngIf="item.isEditable"><a href="#" class="ms-1 me-1" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
<ng-container *ngIf="itemTypeService.isLayer(item)">
<li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
<li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
</ng-container>
<li><fm-item-link class="text-primary p-0" [itemCode]="item.code" pathSuffix="data" [showText]="true"></fm-item-link></li>
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
</div>
<fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert>
</ng-container>
</div>
</div>
</div>
<div class="spacer"></div>
@if (item; as item) {
<div>
<div class="card border-0">
<div class="card-body">
<fm-back-button></fm-back-button>
<div class="card menu-card">
@if (parentOfItemType('vnd.farmmaps.itemtype.cropfield')) {
<h2>{{parentItem.name}}</h2>
}
<h1>{{item.name}}</h1>
</div>
@if (item?.data.layers; as layers) {
<div class="legend-container">
<div class="card menu-card">
@if (layers.length>1) {
<div>
<select (change)="onLayerChanged($event.target.value)">
@for (l of layers; track l.index) {
<option [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
}
</select>
</div>
}
<fm-map-layer-legend [showTitle]="layers.length == 1"
[layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
</div>
</div>
<div class="card menu-card">
<ul class="p-0 mt-2">
@if (item.isEditable) {
<li><a href="#" class="ms-1 me-1" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
}
@if (itemTypeService.isLayer(item)) {
@if (!getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
}
@if (getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
}
}
<li><fm-item-link class="text-primary p-0" [itemCode]="item.code" pathSuffix="data" [showText]="true"></fm-item-link></li>
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
</div>
<fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert>
}
</div>
</div>
</div>
}

View File

@@ -1,35 +1,49 @@
<div class="spacer"></div>
<div *ngIf="item;let item">
<div class="card border-0">
<div class="card-body">
<fm-back-button></fm-back-button>
<div class="card menu-card">
<h2 *ngIf="parentOfItemType('vnd.farmmaps.itemtype.cropfield')">{{parentItem.name}}</h2>
<h1>{{item.name}}</h1>
</div>
<ng-container *ngIf="item?.data.layers;let layers">
<div class="legend-container">
<div class="card menu-card">
<div *ngIf="layers.length>1">
<select (change)="onLayerChanged($event.target.value)">
<option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
</select>
</div>
<fm-map-layer-legend [showTitle]="layers.length==1" [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
</div>
</div>
@if (item; as item) {
<div>
<div class="card border-0">
<div class="card-body">
<fm-back-button></fm-back-button>
<div class="card menu-card">
<ul class="p-0 mt-2">
<li *ngIf="item.isEditable"><a href="#" class="mt-1 me-1" (click)="handleOnEdit(item)" ><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
<ng-container *ngIf="itemTypeService.isLayer(item)">
<li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)" ><i class="fas fa-layer-olus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
<li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)" ><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
</ng-container>
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
@if (parentOfItemType('vnd.farmmaps.itemtype.cropfield')) {
<h2>{{parentItem.name}}</h2>
}
<h1>{{item.name}}</h1>
</div>
</ng-container>
<fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert>
@if (item?.data.layers; as layers) {
<div class="legend-container">
<div class="card menu-card">
@if (layers.length>1) {
<div>
<select (change)="onLayerChanged($event.target.value)">
@for (l of layers; track l.index) {
<option [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
}
</select>
</div>
}
<fm-map-layer-legend [showTitle]="layers.length==1" [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
</div>
</div>
<div class="card menu-card">
<ul class="p-0 mt-2">
@if (item.isEditable) {
<li><a href="#" class="mt-1 me-1" (click)="handleOnEdit(item)" ><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
}
@if (itemTypeService.isLayer(item)) {
@if (!getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)" ><i class="fas fa-layer-olus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
}
@if (getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)" ><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
}
}
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
</div>
}
<fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert>
</div>
</div>
</div>
</div>
}

View File

@@ -1,52 +1,70 @@
<div class="spacer"></div>
<div *ngIf="selectedItem();let item">
<div class="card border-0">
<div class="card-body">
<fm-back-button></fm-back-button>
<div class="card menu-card">
<h2 *ngIf="parentOfItemType('vnd.farmmaps.itemtype.cropfield')">{{parentItem.name}}</h2>
<h1>{{item.name}}</h1>
</div>
<ng-container *ngIf="item?.data.layers;let layers">
<div class="legend-container">
<div class="card menu-card">
<h5><span i18n>Date</span>: {{temporalService.selectedDate(itemLayer)}}</h5>
<div class="d-flex justify-content-between">
<div>
<button *ngIf="temporalService.hasPrevious(itemLayer)" type="button" class="btn btn-link p-0" (click)="handlePreviousTemporal($event)">
<i class="fal fa-arrow-circle-left" aria-hidden="true" i18n-title title="Previous"></i>&nbsp;{{ temporalService.previousDate(itemLayer) }}
</button>
</div>
<div>
<button *ngIf="temporalService.hasNext(itemLayer)" type="button" class="btn btn-link p-0" (click)="handleNextTemporal($event)">
{{ temporalService.nextDate(itemLayer)}}&nbsp;<i class="fal fa-arrow-circle-right" aria-hidden="true" i18n-title title="Next"></i>
</button>
</div>
</div>
<fm-map-zoom-to-show-alert [layer]="currentItemLayer()?.layer"></fm-map-zoom-to-show-alert>
</div>
<div class="card menu-card pt-2">
<div *ngIf="layers.length>1">
<select (change)="onLayerChanged($event.target.value)">
<option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
</select>
</div>
<fm-map-layer-legend [showTitle]="layers.length == 1"
[layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
</div>
</div>
<div class="card menu-card">
<ul class="p-0 mt-2">
<li *ngIf="item.isEditable"><a href="#" class="mt-1 me-1" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
<ng-container *ngIf="itemTypeService.isLayer(item)">
<li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
<li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
</ng-container>
<li><a href="#" (click)="handleGoToChart(item)"><i class="fal fa-line-chart" aria-hidden="true" i18m-title title="Show chart"></i>&nbsp;<span i18n>Show chart</span></a></li>
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
</div>
</ng-container>
</div>
</div>
</div>
<div class="spacer"></div>
@if (selectedItem(); as item) {
<div>
<div class="card border-0">
<div class="card-body">
<fm-back-button></fm-back-button>
<div class="card menu-card">
@if (parentOfItemType('vnd.farmmaps.itemtype.cropfield')) {
<h2>{{parentItem.name}}</h2>
}
<h1>{{item.name}}</h1>
</div>
@if (item?.data.layers; as layers) {
<div class="legend-container">
<div class="card menu-card">
<h5><span i18n>Date</span>: {{temporalService.selectedDate(itemLayer)}}</h5>
<div class="d-flex justify-content-between">
<div>
@if (temporalService.hasPrevious(itemLayer)) {
<button type="button" class="btn btn-link p-0" (click)="handlePreviousTemporal($event)">
<i class="fal fa-arrow-circle-left" aria-hidden="true" i18n-title title="Previous"></i>&nbsp;{{ temporalService.previousDate(itemLayer) }}
</button>
}
</div>
<div>
@if (temporalService.hasNext(itemLayer)) {
<button type="button" class="btn btn-link p-0" (click)="handleNextTemporal($event)">
{{ temporalService.nextDate(itemLayer)}}&nbsp;<i class="fal fa-arrow-circle-right" aria-hidden="true" i18n-title title="Next"></i>
</button>
}
</div>
</div>
<fm-map-zoom-to-show-alert [layer]="currentItemLayer()?.layer"></fm-map-zoom-to-show-alert>
</div>
<div class="card menu-card pt-2">
@if (layers.length>1) {
<div>
<select (change)="onLayerChanged($event.target.value)">
@for (l of layers; track l.index) {
<option [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
}
</select>
</div>
}
<fm-map-layer-legend [showTitle]="layers.length == 1"
[layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
</div>
</div>
<div class="card menu-card">
<ul class="p-0 mt-2">
@if (item.isEditable) {
<li><a href="#" class="mt-1 me-1" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
}
@if (itemTypeService.isLayer(item)) {
@if (!getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
}
@if (getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
}
}
<li><a href="#" (click)="handleGoToChart(item)"><i class="fal fa-line-chart" aria-hidden="true" i18m-title title="Show chart"></i>&nbsp;<span i18n>Show chart</span></a></li>
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
</div>
}
</div>
</div>
</div>
}

View File

@@ -1,21 +1,31 @@
<div *ngIf="item;let item">
<div class="card border-0">
<fm-thumbnail [item]="item" [edit]="item.isEditable"></fm-thumbnail>
</div>
<div class="card">
<div class="card-body">
<fm-back-button></fm-back-button>
<h1 class="card-title">{{item.name}}</h1>
<div class="card menu-card">
<ul class="p-0 mt-2">
<li *ngIf="itemTypeService.hasViewer(item)"><a href="#" (click)="handleOnView(item)" class="btn btn-outline-secondary"><i class="fal fa-eye" aria-hidden="true" title="View"></i>&nbsp;<span i18n>View</span></a></li>
<li *ngIf="item.isEditable"><a href="#" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" title="Edit"></i> <span i18n>Edit</span></a></li>
<ng-container *ngIf="itemTypeService.isLayer(item)">
<li *ngIf="!getItemLayer(item)"><a href="#" (click)="handleAddAsLayer(item)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
<li *ngIf="getItemLayer(item)"><a href="#" (click)="handleRemoveLayer(item)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
</ng-container>
</ul>
</div>
</div>
</div>
</div>
@if (item; as item) {
<div>
<div class="card border-0">
<fm-thumbnail [item]="item" [edit]="item.isEditable"></fm-thumbnail>
</div>
<div class="card">
<div class="card-body">
<fm-back-button></fm-back-button>
<h1 class="card-title">{{item.name}}</h1>
<div class="card menu-card">
<ul class="p-0 mt-2">
@if (itemTypeService.hasViewer(item)) {
<li><a href="#" (click)="handleOnView(item)" class="btn btn-outline-secondary"><i class="fal fa-eye" aria-hidden="true" title="View"></i>&nbsp;<span i18n>View</span></a></li>
}
@if (item.isEditable) {
<li><a href="#" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" title="Edit"></i> <span i18n>Edit</span></a></li>
}
@if (itemTypeService.isLayer(item)) {
@if (!getItemLayer(item)) {
<li><a href="#" (click)="handleAddAsLayer(item)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
}
@if (getItemLayer(item)) {
<li ><a href="#" (click)="handleRemoveLayer(item)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
}
}
</ul>
</div>
</div>
</div>
</div>
}

View File

@@ -1,6 +1,16 @@
<div [ngSwitch]="stage">
<h6 *ngSwitchCase="StageEnum.DevelopmentPreAlpha" style="color:darkred" [ngbTooltip]="info"><b><span i18n>Stage:Pre-alpha</span></b></h6>
<h6 *ngSwitchCase="StageEnum.DevelopmentAlpha" style="color:red" [ngbTooltip]="info"><b><span i18n>Stage:Alpha</span></b></h6>
<h6 *ngSwitchCase="StageEnum.DevelopmentBeta" style="color:orange" [ngbTooltip]="info" ><b><span i18n>Stage:Beta</span></b></h6>
<h6 *ngSwitchCase="StageEnum.ReleaseCandidate" style="color:green" [ngbTooltip]="info" ><b><span i18n>Stage:RC</span></b></h6>
</div>
<div>
@switch (stage) {
@case (StageEnum.DevelopmentPreAlpha) {
<h6 style="color:darkred" [ngbTooltip]="info"><b><span i18n>Stage:Pre-alpha</span></b></h6>
}
@case (StageEnum.DevelopmentAlpha) {
<h6 style="color:red" [ngbTooltip]="info"><b><span i18n>Stage:Alpha</span></b></h6>
}
@case (StageEnum.DevelopmentBeta) {
<h6 style="color:orange" [ngbTooltip]="info" ><b><span i18n>Stage:Beta</span></b></h6>
}
@case (StageEnum.ReleaseCandidate) {
<h6 style="color:green" [ngbTooltip]="info" ><b><span i18n>Stage:RC</span></b></h6>
}
}
</div>

View File

@@ -1,18 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false }
}
);

View File

@@ -3,7 +3,7 @@
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"module": "es2015",
"moduleResolution": "node",
"moduleResolution": "bundler",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
@@ -12,15 +12,9 @@
"importHelpers": true,
"esModuleInterop": true,
"types": [],
"lib": [
"dom",
"es2018"
],
"paths": {
"@angular/*": [
"node_modules/@angular/*"
"node_modules/@angular/*"
]
}
},
@@ -33,7 +27,6 @@
"compilationMode": "partial"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -3,7 +3,7 @@
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"module": "es2015",
"moduleResolution": "node",
"moduleResolution": "bundler",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
@@ -12,15 +12,9 @@
"importHelpers": true,
"esModuleInterop": true,
"types": [],
"lib": [
"dom",
"es2018"
],
"paths": {
"@angular/*": [
"node_modules/@angular/*"
"node_modules/@angular/*"
]
}
},
@@ -33,7 +27,6 @@
"compilationMode": "partial"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -3,12 +3,10 @@
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",

View File

@@ -1,32 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/common-map3d'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View File

@@ -11,8 +11,8 @@
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/common": "^18.2.3",
"@angular/core": "^18.2.3",
"@angular/common": "20.3.16",
"@angular/core": "20.3.16",
"cesium": "^1.97.0",
"ol-cesium": ">=2.13.0"
}
@@ -34,35 +34,46 @@
"extraneous": true
},
"node_modules/@angular/common": {
"version": "18.2.3",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.3.tgz",
"integrity": "sha512-NFL4yXXImSCH7i1xnHykUjHa9vl9827fGiwSV2mnf7LjSUsyDzFD8/54dNuYN9OY8AUD+PnK0YdNro6cczVyIA==",
"version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.16.tgz",
"integrity": "sha512-GRAziNlntwdnJy3F+8zCOvDdy7id0gITjDnM6P9+n2lXvtDuBLGJKU3DWBbvxcCjtD6JK/g/rEX5fbCxbUHkQQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/core": "18.2.3",
"@angular/core": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/core": {
"version": "18.2.3",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.3.tgz",
"integrity": "sha512-VGhMJxj7d0rYpqVfQrcGRB7EE/BCziotft/I/YPl6bOMPSAvMukG7DXQuJdYpNrr62ks78mlzHlZX/cdmB9Prw==",
"version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.16.tgz",
"integrity": "sha512-KSFPKvOmWWLCJBbEO+CuRUXfecX2FRuO0jNi9c54ptXMOPHlK1lIojUnyXmMNzjdHgRug8ci9qDuftvC2B7MKg==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/compiler": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0",
"zone.js": "~0.14.10"
"zone.js": "~0.15.0"
},
"peerDependenciesMeta": {
"@angular/compiler": {
"optional": true
},
"zone.js": {
"optional": true
}
}
},
"node_modules/@cesium/engine": {
@@ -498,9 +509,10 @@
}
},
"node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.1.0"
@@ -549,12 +561,6 @@
"integrity": "sha512-Dn6vJ1Z9v1tepSjvnCpwk5QqwIPcEFKdgnjqfYOABv1ngSofuAhtlugcUC3ehS1OHdgDWSG6C5mvj+Qm15udTQ==",
"peer": true
},
"node_modules/zone.js": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz",
"integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==",
"peer": true
},
"node_modules/zstddec": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz",

View File

@@ -8,8 +8,8 @@
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/common": "19.2.18",
"@angular/core": "19.2.18",
"@angular/common": "^21.1.0",
"@angular/core": "^21.1.0",
"cesium": "^1.97.0",
"ol-cesium": ">=2.13.0"
}

View File

@@ -1,17 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false }
}
);

View File

@@ -4,7 +4,7 @@
"outDir": "../../out-tsc/lib",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"moduleResolution": "bundler",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
@@ -13,15 +13,9 @@
"importHelpers": true,
"esModuleInterop": true,
"types": [],
"lib": [
"dom",
"es2018"
],
"paths": {
"@angular/*": [
"node_modules/@angular/*"
"node_modules/@angular/*"
]
}
},
@@ -34,7 +28,6 @@
"compilationMode": "partial"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -4,7 +4,7 @@
"outDir": "../../out-tsc/lib",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"moduleResolution": "bundler",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
@@ -13,15 +13,9 @@
"importHelpers": true,
"esModuleInterop": true,
"types": [],
"lib": [
"dom",
"es2018"
],
"paths": {
"@angular/*": [
"node_modules/@angular/*"
"node_modules/@angular/*"
]
}
},
@@ -34,7 +28,6 @@
"compilationMode": "partial"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -3,12 +3,10 @@
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",

View File

@@ -1,32 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/common'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,24 +1,23 @@
{
"name": "@farmmaps/common",
"version": "2.1.0",
"version": "4.22.0",
"publishConfig": {
"registry": "https://repository.akkerweb.nl/repository/npm-hosted/"
},
"peerDependencies": {
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
"@angular/common": "19.2.18",
"@angular/core": "19.2.18",
"@angular/forms": "19.2.18",
"@ngrx/effects": "19.2.1",
"@ngrx/router-store": "19.2.1",
"@ngrx/store": "19.2.1",
"@ng-bootstrap/ng-bootstrap": "^20.0.0",
"@angular/common": "^21.1.0",
"@angular/core": "^21.1.0",
"@angular/forms": "^21.1.0",
"@ngrx/effects": "^21.0.1",
"@ngrx/router-store": "^21.0.1",
"@ngrx/store": "^21.0.1",
"tassign": "^1.0.0",
"bootstrap": "^5.3.3",
"@microsoft/signalr": "^3.1.16",
"ngx-uploadx": "7.0.1",
"angular-oauth2-oidc": "^17.0.2",
"@microsoft/signalr": "^10.0.0",
"ngx-uploadx": "^7.0.1",
"moment": "^2.29.4",
"ngx-avatars": "1.9.3",
"ngx-avatars": "^1.10.1",
"ngx-image-cropper": "^7.0.0",
"ngx-clipboard": "^16.0.0"
}

View File

@@ -4,7 +4,7 @@ import { IItemTypes } from '../models/item.types';
import { IListItem } from '../models/list.item';
import { IUser } from '../models/user';
import { IItem } from '../models/item';
import { UserInfo } from 'angular-oauth2-oidc';
import { UserInfo } from '../models/user';
export const INITUSER = '[AppCommon] InitUser';
export const INITUSERSUCCESS = '[AppCommon] InitUserSuccess';

View File

@@ -1,8 +1,6 @@
import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
import {AuthCallbackComponent} from './components/auth-callback/auth-callback.component';
import {AuthCallbackGuard} from './components/auth-callback/auth-callback.guard';
import {NavBarGuard} from './services/nav-bar-guard.service';
import {FullScreenGuard} from './services/full-screen-guard.service';
import {SessionClearedComponent} from './components/session-cleared/session-cleared.component';
@@ -11,10 +9,6 @@ import { ProductionGuard } from './services/production-guard.service';
const routes = [
{
path: 'cb',
component: AuthCallbackComponent
},
{
path: 'loggedout',
component: SessionClearedComponent,

View File

@@ -3,9 +3,6 @@ import { NgModule, ModuleWithProviders, Injector, Optional, SkipSelf, inject, pr
import { DatePipe } from '@angular/common';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
// external modules
import { OAuthModule, OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
//components
import { ItemTypeService } from './services/itemtype.service';
@@ -23,22 +20,21 @@ import { DownloadService } from './services/download.service';
import { GeolocatorService } from './services/geolocator.service';
import { WeatherService} from './services/weather.service';
import { AppConfig } from './shared/app.config';
import { AccessTokenInterceptor } from "./shared/accesstoken.interceptor";
import { appConfigFactory } from "./shared/app.config.factory";
import { AuthGuard } from './services/auth-guard.service';
import { NavBarGuard } from './services/nav-bar-guard.service';
import { PackageGuard } from './services/package-guard.service';
import { FullScreenGuard } from './services/full-screen-guard.service';
import { AuthCallbackGuard } from './components/auth-callback/auth-callback.guard';
import { ResumableFileUploadService } from './components/resumable-file-upload/resumable-file-upload.service';
import { NgbDateNativeAdapter } from './services/date-adapter.service'
import { AuthConfigFactory } from './shared/authconfigFactory';
import { StateSerializerService } from './services/state-serializer.service';
import { PackageService } from './services/package.service';
import { PackagePreloadStrategy } from './services/package-preload-strategy.service';
import { SenmlService } from './services/senml-service';
import { DeviceService } from './services/device.service';
import { GradientService} from './services/gradient.service';
import { GradientService } from './services/gradient.service';
import { UserDataService } from './services/user-data.service';
import { CacheService } from './services/cache-service';
import { SharedItemService } from './services/shared.item.service';
export {
FolderService,
@@ -55,12 +51,10 @@ export {
GeolocatorService,
WeatherService,
AppConfig,
AccessTokenInterceptor,
AuthGuard,
NavBarGuard,
PackageGuard,
FullScreenGuard,
AuthCallbackGuard,
ResumableFileUploadService,
NgbDateNativeAdapter,
StateSerializerService,
@@ -69,13 +63,13 @@ export {
SenmlService,
PackagePreloadStrategy,
DeviceService,
GradientService
GradientService,
UserDataService,
CacheService,
SharedItemService
};
@NgModule({
imports: [
OAuthModule.forRoot(),
]
})
export class AppCommonServiceModule {
constructor(@Optional() @SkipSelf() parentModule: AppCommonServiceModule) {
@@ -90,15 +84,6 @@ export class AppCommonServiceModule {
providers: [
AppConfig,
ItemTypeService,
provideAppInitializer(() => {
const initializerFn = (appConfigFactory)(inject(Injector), inject(AppConfig), inject(OAuthService), inject(AuthConfigFactory), inject(OAuthStorage), inject(ItemTypeService));
return initializerFn();
}),
{
provide: HTTP_INTERCEPTORS,
useClass: AccessTokenInterceptor,
multi: true
},
DatePipe
]
};

View File

@@ -8,7 +8,6 @@ import { FormsModule } from '@angular/forms';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { EffectsModule } from '@ngrx/effects';
import { StoreModule } from '@ngrx/store';
import { OAuthModule } from 'angular-oauth2-oidc';
import { ClipboardModule } from 'ngx-clipboard';
import { UploadxModule } from 'ngx-uploadx';
@@ -24,7 +23,6 @@ import { ImageCropperModule } from 'ngx-image-cropper';
import * as commonActions from './actions/app-common.actions';
import { AppMenuComponent } from './components/app-menu/app-menu.component';
import { AppComponent } from './components/app/app.component';
import { AuthCallbackComponent } from './components/auth-callback/auth-callback.component';
import { AvatarComponent } from './components/avatar/avatar.component';
import { BackButtonComponent } from './components/back-button/back-button.component';
import { EditImageModalComponent } from './components/edit-image-modal/edit-image-modal.component';
@@ -61,6 +59,9 @@ import { IItemTask, ItemTask } from './models/itemTask';
import { IJsonline } from './models/json-line';
import { IListItem } from './models/list.item';
import { IPackage, IPackages } from './models/package';
import { IAclRights } from './models/acl.rights';
import { IListItemAclRights } from './models/list.item.acl.rights';
import { ISharedItem } from './models/shared.item';
import { IQueryState } from './models/query.state';
import { ISenMLItem } from './models/senml-item';
import { ITypeaheadItem } from './models/typeahead.item';
@@ -68,24 +69,22 @@ import { IUrlType } from './models/url.type';
import { IUser } from './models/user';
import { WeatherCurrentObservation } from './models/weatherCurrentObservation';
import * as commonReducers from './reducers/app-common.reducer';
import { AuthConfigFactory, IAuthconfigFactory } from './shared/authconfigFactory';
import { SafePipe } from './shared/safe.pipe';
import { SecureOAuthStorage } from './shared/secureOAuthStorage';
export const FM_COMMON_STARTPAGE = new InjectionToken<string>('fm-common-startpage');
export {
Alert, AppComponent, AuthCallbackComponent, AuthConfigFactory, AvatarComponent, BackButtonComponent, commonActions,
Alert, AppComponent, AvatarComponent, BackButtonComponent, commonActions,
commonReducers, EditImageModalComponent,
GradientComponent,
GradientSelectComponent, HasClaimDirective, HasPackageDirective, HasRoleDirective, IAuthconfigFactory, IColor, IDataLayer, IEventMessage, IGradientstop, IItem, IItemLinkType, IItemTask, IItemType, IItemTypes, IJsonline, IListItem, IPackage,
GradientSelectComponent, HasClaimDirective, HasPackageDirective, HasRoleDirective, IColor, IDataLayer, IEventMessage, IGradientstop, IItem, IItemLinkType, IItemTask, IItemType, IItemTypes, IJsonline, IListItem, IPackage,
IAclRights, IListItemAclRights, ISharedItem,
IPackages, IQueryState, ISenMLItem, Item, ItemLinkComponent, ItemTask, ITypeaheadItem, IUrlType, IUser, MenuBackgroundComponent, NotFoundComponent,
NotImplementedComponent, PackageExistsDirective, ResumableFileUploadComponent, SafePipe, SecureOAuthStorage, SessionClearedComponent, SidePanelComponent, TagInputComponent, ThumbnailComponent, TimespanComponent, UserMenuComponent, WeatherCurrentObservation
NotImplementedComponent, PackageExistsDirective, ResumableFileUploadComponent, SafePipe, SessionClearedComponent, SidePanelComponent, TagInputComponent, ThumbnailComponent, TimespanComponent, UserMenuComponent, WeatherCurrentObservation
};
@NgModule({ declarations: [
AppComponent,
AuthCallbackComponent,
SidePanelComponent,
SafePipe,
NotFoundComponent,
@@ -119,7 +118,6 @@ export {
CommonModule,
AppComponent,
ResumableFileUploadComponent,
AuthCallbackComponent,
SidePanelComponent,
SafePipe,
NotFoundComponent,
@@ -145,7 +143,6 @@ export {
AppCommonRoutingModule,
StoreModule.forFeature(MODULE_NAME, commonReducers.reducer),
EffectsModule.forFeature([commonEffects.AppCommonEffects]),
OAuthModule.forRoot(),
NgbModule,
FormsModule,
UploadxModule,

View File

@@ -1,47 +1,51 @@
<div class="app fullscreen" (click)="handleClick($event)" [ngClass]="{'fullscreen' :(fullScreen|async),'pagemode':(isPageMode|async),'appmode':!(isPageMode|async)}">
<nav class="navbar navbar-light navbar-expand bg-light navigation-clean">
<div class="container-fluid p-3 justify-content-start">
<div class="header-logo pageonly"><router-outlet name="header-logo"></router-outlet></div>
<button type="button" class="btn btn-outline-secondary apponly" (click)="handleToggleMenu($event)"><i class="fal fa-bars" aria-hidden="true"></i></button>
<router-outlet name="menu" class="ms-4"></router-outlet>
<div class="collapse navbar-collapse pageonly">
<a class="btn btn-primary ms-auto" role="button" [routerLink]="[ startPage == null?'/map':startPage]">
<span *ngIf="(user|async)==null" i18n>Sign in</span>
<span *ngIf="(user|async)!=null" i18n>To app</span>
</a>
</div>
</div>
</nav>
<div class="body">
<router-outlet></router-outlet>
</div>
<fm-menu-background [visible]="menuVisible|async"></fm-menu-background>
<fm-side-panel [visible]="menuVisible|async" [left]="true" class="menu" (click)="handleStopBubble($event)">
<div class="container-fluid">
<div class="body">
<div class="d-flex flex-row">
<div class="mt-2 mb-2 flex-grow-1 logo" (click)="handleHome($event)"><router-outlet name="side-panel-logo"></router-outlet></div>
<div class="mt-2 mb-2 ms-2"><button type="button" class="btn btn-outline-secondary" (click)="handleToggleMenu($event)"><i class="fal fa-times" aria-hidden="true"></i></button></div>
</div>
<div class="d-flex flex-column cards">
<router-outlet name="side-panel-menu"></router-outlet>
</div>
</div>
</div>
</fm-side-panel>
<ng-container *ngIf="showUploadProgress">
<fm-resumable-file-upload></fm-resumable-file-upload>
</ng-container>
<div class="user-menu apponly">
<fm-setting-menu [user]="user|async" [showMenu]="settingMenuVisible|async" [backgroundColor]="settingMenuBackgroundColor|async"></fm-setting-menu>
<fm-help-menu [user]="user|async" [showMenu]="helpMenuVisible|async"></fm-help-menu>
<fm-notification-menu [user]="user|async" [unread]="unreadNotifications|async" [showMenu]="notificationMenuVisible|async"></fm-notification-menu>
<fm-app-menu [user]="user|async" [showMenu]="appMenuVisible|async"></fm-app-menu>
<fm-user-menu [user]="user|async" [showMenu]="accountMenuVisible|async"></fm-user-menu>
</div>
<div class="healthstatus-container online apponly" [ngClass]="{'online' :(isOnline|async)}">
<div class="healthstatus alert alert-danger m-0" >
<span i18n>Not connected, make sure your device has an active internet connection</span>
</div>
</div>
</div>
<div class="app fullscreen" (click)="handleClick($event)" [ngClass]="{'fullscreen' :(fullScreen|async),'pagemode':(isPageMode|async),'appmode':!(isPageMode|async)}">
<nav class="navbar navbar-light navbar-expand bg-light navigation-clean">
<div class="container-fluid p-3 justify-content-start">
<div class="header-logo pageonly"><router-outlet name="header-logo"></router-outlet></div>
<button type="button" class="btn btn-outline-secondary apponly" (click)="handleToggleMenu($event)"><i class="fal fa-bars" aria-hidden="true"></i></button>
<router-outlet name="menu" class="ms-4"></router-outlet>
<div class="collapse navbar-collapse pageonly">
<a class="btn btn-primary ms-auto" role="button" [routerLink]="[ startPage == null?'/map':startPage]">
@if ((user|async)==null) {
<span i18n>Sign in</span>
}
@if ((user|async)!=null) {
<span i18n>To app</span>
}
</a>
</div>
</div>
</nav>
<div class="body">
<router-outlet></router-outlet>
</div>
<fm-menu-background [visible]="menuVisible|async"></fm-menu-background>
<fm-side-panel [visible]="menuVisible|async" [left]="true" class="menu" (click)="handleStopBubble($event)">
<div class="container-fluid">
<div class="body">
<div class="d-flex flex-row">
<div class="mt-2 mb-2 flex-grow-1 logo" (click)="handleHome($event)"><router-outlet name="side-panel-logo"></router-outlet></div>
<div class="mt-2 mb-2 ms-2"><button type="button" class="btn btn-outline-secondary" (click)="handleToggleMenu($event)"><i class="fal fa-times" aria-hidden="true"></i></button></div>
</div>
<div class="d-flex flex-column cards">
<router-outlet name="side-panel-menu"></router-outlet>
</div>
</div>
</div>
</fm-side-panel>
@if (showUploadProgress) {
<fm-resumable-file-upload></fm-resumable-file-upload>
}
<div class="user-menu apponly">
<fm-setting-menu [user]="user|async" [showMenu]="settingMenuVisible|async" [backgroundColor]="settingMenuBackgroundColor|async"></fm-setting-menu>
<fm-help-menu [user]="user|async" [showMenu]="helpMenuVisible|async"></fm-help-menu>
<fm-notification-menu [user]="user|async" [unread]="unreadNotifications|async" [showMenu]="notificationMenuVisible|async"></fm-notification-menu>
<fm-app-menu [user]="user|async" [showMenu]="appMenuVisible|async"></fm-app-menu>
<fm-user-menu [user]="user|async" [showMenu]="accountMenuVisible|async"></fm-user-menu>
</div>
<div class="healthstatus-container online apponly" [ngClass]="{'online' :(isOnline|async)}">
<div class="healthstatus alert alert-danger m-0" >
<span i18n>Not connected, make sure your device has an active internet connection</span>
</div>
</div>
</div>

View File

@@ -1,11 +1,10 @@
import { Component, OnInit, OnDestroy, Inject, Optional, ViewEncapsulation, RendererFactory2, PLATFORM_ID, ChangeDetectionStrategy, HostListener, Input } from '@angular/core';
import { Component, OnInit, OnDestroy, Inject, Optional, ViewEncapsulation, RendererFactory2, PLATFORM_ID, ChangeDetectionStrategy, HostListener, Input, DOCUMENT } from '@angular/core';
import { Router, NavigationStart, NavigationEnd, RouteConfigLoadStart, RouteConfigLoadEnd, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router';
import { Meta, Title, MetaDefinition } from '@angular/platform-browser'; import { DOCUMENT } from "@angular/common";
import { Meta, Title, MetaDefinition } from '@angular/platform-browser';
import { Subscription, Observable } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';
import { Store, Action } from '@ngrx/store';
import { IUser } from '../../models/user';
import { OAuthService, OAuthErrorEvent } from 'angular-oauth2-oidc';
import { FM_COMMON_STARTPAGE } from '../../common.module';
//AppCommon
@@ -19,13 +18,13 @@ import { AppConfig } from '../../shared/app.config';
import * as appReducers from '../../reducers/app-common.reducer';
@Component({
selector: 'fm-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false
@Component({
selector: 'fm-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false
})
export class AppComponent implements OnInit, OnDestroy {
@@ -64,7 +63,6 @@ export class AppComponent implements OnInit, OnDestroy {
private eventService$: EventService,
private healthCheckService$: HealthCheckService,
private itemTypeService$: ItemTypeService,
private oauthService$: OAuthService,
private appConfig$: AppConfig
) {
}
@@ -121,7 +119,6 @@ export class AppComponent implements OnInit, OnDestroy {
ngOnInit() {
this.InstallRouteEventHandler();
this.InstallEventServiceEventHandler();
this.InstallAuthenticationEventHandler();
this.InstallHealthCheck();
}
@@ -139,32 +136,6 @@ export class AppComponent implements OnInit, OnDestroy {
if (this.eventSub$) this.eventSub$.unsubscribe();
}
private InstallAuthenticationEventHandler() {
// auth event handler
this.oauthService$.events.subscribe((event) => {
//console.debug(event.type);
if (event.type == 'token_error' || event.type == 'silent_refresh_timeout' || event.type == 'logout') {
const e = event as OAuthErrorEvent;
const p = e.params as any;
if (event.type == 'silent_refresh_timeout' || event.type == 'logout' || (p.error && p.error == 'login_required')) {
//console.debug("Session expired");
this.router.navigate(['loggedout'], { queryParams: { redirectTo: this.router.url } });
}
}
if (event.type == 'token_received') {
this.oauthService$.loadUserProfile();
this.store$.dispatch(new commonActions.InitUser());
}
});
if (this.oauthService$.hasValidAccessToken()) {
this.store$.dispatch(new commonActions.InitUser());
} else {
if (this.oauthService$.getRefreshToken() != null) {
this.oauthService$.refreshToken();
}
}
}
private InstallRouteEventHandler() {
const other = this;
this.routerSub$ = this.router.events.subscribe(event => {

View File

@@ -1,22 +0,0 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc';
import { Location} from '@angular/common';
@Component({
selector: 'fm-auth-callback',
template: '<div></div>',
standalone: false
})
export class AuthCallbackComponent {
constructor(private router$: Router,private oauthService$:OAuthService) {
oauthService$.loadDiscoveryDocument().then(() => {
oauthService$.tryLoginCodeFlow().then(() => {
router$.navigateByUrl((oauthService$.state && oauthService$.state!="")?decodeURIComponent(oauthService$.state):"");
}).catch(() => {
router$.navigateByUrl("/");
});
})
}
}

View File

@@ -1,17 +0,0 @@
import { Router } from '@angular/router';
import { Injectable } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { } from '@angular/router';
@Injectable({
providedIn: 'root',
})
export class AuthCallbackGuard {
constructor(private router$: Router,private oauthService$:OAuthService) {}
canActivate() {
this.router$.navigateByUrl(this.oauthService$.state);
return false;
}
}

View File

@@ -1,4 +1,5 @@
<div *ngIf="show()" class="back-button mb-2">
@if (show()) {
<div class="back-button mb-2">
<i class="fal fa-arrow-left"></i>&nbsp;<span i18n="@FmBackButton">Back</span>
</div>
</div>
}

View File

@@ -5,32 +5,34 @@
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="modal-body">
<div class="cropper">
<div *ngIf="!isImageLoaded" class="no-image" (click)="fileInput.click()">
<i class="fal fa-image"></i>
<div i18n>No image selected</div>
</div>
<image-cropper #imageCropper output="base64"
[imageChangedEvent]="imageChangedEvent"
[maintainAspectRatio]="true"
[format]="imageType"
[aspectRatio]="aspectRatio"
[autoCrop]="true"
[resizeToWidth]="maxWidth"
[roundCropper]="roundImage"
(imageCropped)="imageCropped($event)"
(imageLoaded)="imageLoaded($event)"
(cropperReady)="cropperReady()"
(loadImageFailed)="loadImageFailed()"
[imageURL]="imageUrl"
></image-cropper>
@if (!isImageLoaded) {
<div class="no-image" (click)="fileInput.click()">
<i class="fal fa-image"></i>
<div i18n>No image selected</div>
</div>
}
<image-cropper #imageCropper output="base64"
[imageChangedEvent]="imageChangedEvent"
[maintainAspectRatio]="true"
[format]="imageType"
[aspectRatio]="aspectRatio"
[autoCrop]="true"
[resizeToWidth]="maxWidth"
[roundCropper]="roundImage"
(imageCropped)="imageCropped($event)"
(imageLoaded)="imageLoaded($event)"
(cropperReady)="cropperReady()"
(loadImageFailed)="loadImageFailed()"
[imageURL]="imageUrl"
></image-cropper>
</div>
<input #fileInput type="file" (change)="fileChangeEvent($event)" style="display:none" accept="image/*"/>
<span class="btn btn-primary" (click)="fileInput.click()" i18n>Select image</span>
<input #fileInput type="file" (change)="fileChangeEvent($event)" style="display:none" accept="image/*"/>
<span class="btn btn-primary" (click)="fileInput.click()" i18n>Select image</span>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" i18n [disabled]="!isImageLoaded" (click)="save()">Apply</button>
<button type="submit" class="btn btn-primary" i18n [disabled]="!isImageLoaded" (click)="save()">Apply</button>
<button type="button" autofocus class="btn btn-secondary" (click)="modal.close('Save click')" i18n="@@buttonCancel">Cancel</button>
</div>
</div>
</ng-template>

View File

@@ -1,16 +1,28 @@
<div *ngIf="gradientItems" class="form-control gradient-select" (click)="handleToggleList()">
<div *ngIf="selectedItem">
<div *ngIf="showLabel">{{selectedItem?.name}}</div>
<fm-gradient [gradientItem]="selectedItem"></fm-gradient>
</div>
@if (gradientItems) {
<div class="form-control gradient-select" (click)="handleToggleList()">
@if (selectedItem) {
<div>
@if (showLabel) {
<div>{{selectedItem?.name}}</div>
}
<fm-gradient [gradientItem]="selectedItem"></fm-gradient>
</div>
}
<div class="gradient-list" [ngClass]="{'visible':listVisible}">
<ul *ngIf="gradientItems">
<li *ngFor="let item of gradientItems" (click)="handleSelect(item)" [ngClass]="{'bg-primary':isSelected(item),'text-white':isSelected(item)} ">
<div>{{item?.name}}</div>
<div><fm-gradient [gradientItem]="item"></fm-gradient></div>
@if (gradientItems) {
<ul>
@for (item of gradientItems; track item.code) {
<li (click)="handleSelect(item)" [ngClass]="{'bg-primary':isSelected(item),'text-white':isSelected(item)} ">
<div>{{item?.name}}</div>
<div><fm-gradient [gradientItem]="item"></fm-gradient></div>
</li>
}
</ul>
<div *ngIf="showAdd" class="addGradient"><a href="#" i18n (click)="handleAdd($event)" >Add gradient</a></div>
}
@if (showAdd) {
<div class="addGradient"><a href="#" i18n (click)="handleAdd($event)" >Add gradient</a></div>
}
</div>
</div>
</div>
}

View File

@@ -1,15 +1,12 @@
import { Directive, ViewContainerRef,TemplateRef,OnInit,Input,OnDestroy } from '@angular/core';
import { Directive, ViewContainerRef,TemplateRef,OnInit,Input } from '@angular/core';
import { Store} from '@ngrx/store';
import * as appCommonReducer from '../../reducers/app-common.reducer'
import { IPackages } from '../../models/package';
import { Observable, Subscription } from 'rxjs';
import { skip } from 'rxjs/operators';
import {OAuthService } from 'angular-oauth2-oidc';
import { Observable } from 'rxjs';
import { IUser } from '../../models/user';
@Directive({
selector: '[fm-hasclaim]',
standalone: false
@Directive({
selector: '[fm-hasclaim]',
standalone: false
})
export class HasClaimDirective implements OnInit{
@Input('fm-hasclaim') claim:string;

View File

@@ -1 +1 @@
<span class="item-link" (click)="copylink(copiedtt)" triggers="manual" ngbTooltip="Link copied" #copiedtt="ngbTooltip" ><i ngbTooltip='Copy link' class="fa-solid fa-link"></i> <span *ngIf="showText" i18n>Copy link</span></span>
<span class="item-link" (click)="copylink(copiedtt)" triggers="manual" ngbTooltip="Link copied" #copiedtt="ngbTooltip" ><i ngbTooltip='Copy link' class="fa-solid fa-link"></i> @if (showText) {<span i18n>Copy link</span>}</span>

View File

@@ -2,19 +2,29 @@
<div>
<div class="card">
<div class="card-header p-3 bg-primary text-white">
<span *ngIf="uploadService.isUploading">Uploading files (<span>{{uploadService.totalProgress | number:'1.1-1'}}</span> %)</span>
<span *ngIf="uploadService.isUploading == false">Uploaded <span>{{uploadService.files.length}}</span> files</span>
@if (uploadService.isUploading) {
<span>Uploading files (<span>{{uploadService.totalProgress | number:'1.1-1'}}</span> %)</span>
}
@if (uploadService.isUploading == false) {
<span>Uploaded <span>{{uploadService.files.length}}</span> files</span>
}
<span title="Cancel" class="fal fa-times float-end" (click)="uploadService.close()"></span><span title="Minimize" class="fal fa-chevron-down float-end me-2" (click)="uploadService.toggleMinimize()" [ngClass]="{'fa-chevron-down': uploadService.isMinimized == false, 'fa-chevron-up':uploadService.isMinimized}"></span>
</div>
<div [ngClass]="{'minimized': uploadService.isMinimized }">
<div class="card-block p-3">
<ul class="list-unstyled">
<li *ngFor="let file of uploadService.files" class="upload-file busy" [ngClass]="{'done': file.success,'busy':file.success == false,'error': file.error }">
<div *ngIf="file.success == false"><span class="file-name" [attr.title]="file?.fileName">{{file.fileName}}</span><span class="fal fa-times" title="Cancel" (click)="uploadService.cancelFile(file)"></span><span class="fal fa-check"></span></div>
<div *ngIf="file.success"><a href="#" (click)="handleUploadedFileClick($event,file)" class="file-name" [attr.title]="file?.fileName">{{file.fileName}}</a><span class="fal fa-check"></span></div>
<div class="progress-container"><div class="progress-bar" [style.width]="file.progress+'%'"></div></div>
<div class="errormessage">{{file.errorMessage}}</div>
</li>
@for (file of uploadService.files; track $index) {
<li class="upload-file busy" [ngClass]="{'done': file.success,'busy':file.success == false,'error': file.error }">
@if (file.success == false) {
<div><span class="file-name" [attr.title]="file?.fileName">{{file.fileName}}</span><span class="fal fa-times" title="Cancel" (click)="uploadService.cancelFile(file)"></span><span class="fal fa-check"></span></div>
}
@if (file.success) {
<div><a href="#" (click)="handleUploadedFileClick($event,file)" class="file-name" [attr.title]="file?.fileName">{{file.fileName}}</a><span class="fal fa-check"></span></div>
}
<div class="progress-container"><div class="progress-bar" [style.width]="file.progress+'%'"></div></div>
<div class="errormessage">{{file.errorMessage}}</div>
</li>
}
</ul>
</div>
</div>

View File

@@ -1,5 +1,4 @@
import { Injectable, OnDestroy } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { Subject , Subscription } from 'rxjs';
import { HttpClient } from "@angular/common/http";
import { UploadxService, UploadState,UploadxOptions} from 'ngx-uploadx';
@@ -19,7 +18,7 @@ export class ResumableFileUploadService implements OnDestroy{
private _eventSub:Subscription;
private initialized = false;
constructor(private httpClient: HttpClient,private oauthService: OAuthService,private uploadService: UploadxService,public appConfig: AppConfig) {
constructor(private httpClient: HttpClient,private uploadService: UploadxService,public appConfig: AppConfig) {
}
@@ -31,7 +30,6 @@ export class ResumableFileUploadService implements OnDestroy{
if(!this.initialized) {
this._eventSub=this.uploadService.init({
endpoint:this.endPoint(),
token:() => this.oauthService.getAccessToken(),
chunkSize: 2097152}).subscribe((uploadState:UploadState) => {
this.handleState(uploadState);
} );

View File

@@ -1,14 +1,16 @@
<div class="side-panel hidden" [ngClass]="{'hidden':!visible,'collapsed':collapsed,'resizeable':(resizeable && mobile),'resizing':resizing,'left':left,'extrawide':extrawide}" [ngStyle]="{'top':top}">
<div *ngIf="collapsable" class="arrow rounded-end p-2" (click)="handleToggleClick($event)">
<i class="fal fa-chevron-left" aria-hidden="true"></i>
</div>
<div draggable="true" class="resizegrip" (dragstart)="handleStartGripDrag($event)" (touchstart)="handleStartGripDrag($event)" (dragend)="handleEndGripDrag()" (touchend)="handleEndGripDrag()" (drag)="handleGripDrag($event)" (touchmove)="handleGripDrag($event)">
<div></div>
<span class="rounded"></span>
</div>
<div class="content">
<ng-content>
</ng-content>
</div>
</div>
<div class="side-panel hidden" [ngClass]="{'hidden':!visible,'collapsed':collapsed,'resizeable':(resizeable && mobile),'resizing':resizing,'left':left,'extrawide':extrawide}" [ngStyle]="{'top':top}">
@if (collapsable) {
<div class="arrow rounded-end p-2" (click)="handleToggleClick($event)">
<i class="fal fa-chevron-left" aria-hidden="true"></i>
</div>
}
<div draggable="true" class="resizegrip" (dragstart)="handleStartGripDrag($event)" (touchstart)="handleStartGripDrag($event)" (dragend)="handleEndGripDrag()" (touchend)="handleEndGripDrag()" (drag)="handleGripDrag($event)" (touchmove)="handleGripDrag($event)">
<div></div>
<span class="rounded"></span>
</div>
<div class="content">
<ng-content>
</ng-content>
</div>
</div>

View File

@@ -1,6 +1,8 @@
<div class="tags">
<span class="tag rounded bg-primary text-white" *ngFor="let tag of tags;"><span>{{tag}}</span> <i
(click)="handleDeleteTag(tag)" class="fal fa-times" aria-hidden="true"></i></span><input
@for (tag of tags; track $index) {
<span class="tag rounded bg-primary text-white"><span>{{tag}}</span> <i
(click)="handleDeleteTag(tag)" class="fal fa-times" aria-hidden="true"></i></span>
}<input
type="text" #tagInputElement
(blur)="handleBlur($event, false)"
(keyup)="handleKeyUp($event)"

View File

@@ -1,9 +1,15 @@
<div #thumbnail class="thumbnail" [style.background-color]="itemTypeService.getColor(item.itemType)" >
<div class="content">
<img *ngIf="hasThumbnail()" class="card-img-top" [src]="getThumbnailUrl(item)" />
<div *ngIf="!hasThumbnail()" class="large-icon" [style.font-size]="getFontSize()" [style.line-height]="getLineHeight()"><i [ngClass]="itemTypeService.getIcon(item.itemType)"></i></div>
<div *ngIf="canEdit()" class="edit btn btn-secondary rounded-circle" (click)="onEditClick()"><i class="fal fa-camera"></i></div>
</div>
</div>
<fm-edit-image-modal #modal (changed)="onChanged($event)"></fm-edit-image-modal>
<div #thumbnail class="thumbnail" [style.background-color]="itemTypeService.getColor(item.itemType)" >
<div class="content">
@if (hasThumbnail()) {
<img class="card-img-top" [src]="getThumbnailUrl(item)" />
}
@if (!hasThumbnail()) {
<div class="large-icon" [style.font-size]="getFontSize()" [style.line-height]="getLineHeight()"><i [ngClass]="itemTypeService.getIcon(item.itemType)"></i></div>
}
@if (canEdit()) {
<div class="edit btn btn-secondary rounded-circle" (click)="onEditClick()"><i class="fal fa-camera"></i></div>
}
</div>
</div>
<fm-edit-image-modal #modal (changed)="onChanged($event)"></fm-edit-image-modal>

View File

@@ -1,17 +1,23 @@
<div>
<div (click)="toggle($event)" class="rounded-circle menu-button hidden" [ngClass]="{'hidden':!user}">
<span *ngIf="user"><fm-avatar [user]="user" size="40"></fm-avatar></span>
<div class="menu hidden" [ngClass]="{'hidden':!showMenu}">
<div class="card" *ngIf="user">
<div class="card-body">
<div class="username">{{user.name}}</div>
<div *ngIf="getProvider(); let provider">
<small><span i18n>Provider</span><span> {{provider}}</span></small>
</div>
<div><a href="#" (click)="logout($event)" i18n>logout</a></div>
</div>
</div>
<router-outlet name="user-menu"></router-outlet>
<div (click)="toggle($event)" class="rounded-circle menu-button hidden" [ngClass]="{'hidden':!user}">
@if (user) {
<span><fm-avatar [user]="user" size="40"></fm-avatar></span>
}
<div class="menu hidden" [ngClass]="{'hidden':!showMenu}">
@if (user) {
<div class="card">
<div class="card-body">
<div class="username">{{user.name}}</div>
@if (getProvider(); as provider) {
<div>
<small><span i18n>Provider</span><span> {{provider}}</span></small>
</div>
}
<div><a href="#" (click)="logout($event)" i18n>logout</a></div>
</div>
</div>
}
<router-outlet name="user-menu"></router-outlet>
</div>
</div>
</div>

View File

@@ -1,5 +1,4 @@
import { Component, OnInit,Input } from '@angular/core';
import { OAuthService} from 'angular-oauth2-oidc'
import { IUser } from '../../models/user';
import {Store} from '@ngrx/store';
import * as appReducers from '../../reducers/app-common.reducer';
@@ -16,13 +15,13 @@ export class UserMenuComponent implements OnInit {
@Input() user:IUser;
@Input() showMenu:boolean;
constructor(private oauthService:OAuthService, private store: Store<appReducers.State>) { }
constructor(private store: Store<appReducers.State>) { }
ngOnInit(): void {
}
getProvider():string | null {
const ownedClaims = this.oauthService.getIdentityClaims();
const ownedClaims = [];//TODO FIX THIS
if(ownedClaims) {
if (ownedClaims["idp"] != "local") {
return ownedClaims["idp"];

View File

@@ -1,6 +1,5 @@
import { Injectable, Inject, LOCALE_ID } from '@angular/core';
import { Router } from '@angular/router';
import { OAuthService,UserInfo } from 'angular-oauth2-oidc';
import { Store } from '@ngrx/store';
import { Actions,ofType,createEffect } from '@ngrx/effects';
import { of,from,zip } from 'rxjs';
@@ -13,27 +12,13 @@ import { UserService } from '../services/user.service';
import { IItemTypes } from '../models/item.types';
import { IListItem } from '../models/list.item';
import {StateSerializerService} from '../services/state-serializer.service';
import { IUser } from '../common.module';
import { UserInfo } from '../models/user';
@Injectable()
export class AppCommonEffects {
locale: string;
login$ = createEffect(() => this.actions$.pipe(
ofType(appCommonActions.LOGIN),
withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)),
mergeMap(([action, initialized]) => {
const a = (action as appCommonActions.Login);
this.oauthService$.initCodeFlow(a.url,{"prompt":"login"});
return [];
})),{dispatch:false});
logout$ = createEffect(() => this.actions$.pipe(
ofType(appCommonActions.LOGOUT),
mergeMap((action) => {
this.oauthService$.revokeTokenAndLogout();
return [];
})),{dispatch:false});
loadItemTypes$ = createEffect(() => this.actions$.pipe(
ofType(appCommonActions.LOADITEMTYPES),
switchMap((action) => {
@@ -43,21 +28,16 @@ export class AppCommonEffects {
}
)));
//TODO FIX THIS
initUser$ = createEffect(() => this.actions$.pipe(
ofType(appCommonActions.INITUSER),
first(),
switchMap((action) => {
return zip(this.userService$.getCurrentUser(),from(this.oauthService$.loadUserProfile())).pipe(
switchMap(([user,userInfo]) => {
if (location.hostname === 'localhost' || user.language === undefined || user.language === this.locale)
{
return of(new appCommonActions.InitUserSuccess(user,userInfo as UserInfo))
}
return of(new appCommonActions.SwitchLanguage(user.language))
}),
catchError(error => of(new appCommonActions.Fail(error))))
}
)));
let user :IUser = {claims:[],searchable:true}
let userInfo : UserInfo = {sub:"aabbccdd"}
return of(new appCommonActions.InitUserSuccess(user,userInfo))
}
)));
switchLanguage$ = createEffect(() => this.actions$.pipe(
ofType(appCommonActions.SWITCHLANGUAGE),
@@ -213,19 +193,12 @@ export class AppCommonEffects {
ofType(appCommonActions.ONLINE),
switchMap((action) => {
//console.debug("Online: Check token");
if(!this.oauthService$.hasValidAccessToken()) {
//console.debug("No valid token, try to refresh");
if(this.oauthService$.getRefreshToken() != null ) {
//console.debug("We have a refresh token");
this.oauthService$.refreshToken();
}
}
return of(undefined);
})),{dispatch:false});
constructor(private actions$: Actions, private store$: Store<appCommonReducers.State>, private oauthService$: OAuthService, private itemService$: ItemService, private folderService$:FolderService, private userService$: UserService, private router$: Router, private stateSerializerService$:StateSerializerService, @Inject(LOCALE_ID) locale: string) {
constructor(private actions$: Actions, private store$: Store<appCommonReducers.State>, private itemService$: ItemService, private folderService$:FolderService, private userService$: UserService, private router$: Router, private stateSerializerService$:StateSerializerService, @Inject(LOCALE_ID) locale: string) {
this.locale = locale;
store$.dispatch(new appCommonActions.LoadItemTypes());
}

View File

@@ -0,0 +1,9 @@
export interface IAclRights {
id?: number,
claim: any,
expires: Date,
rights: number,
owner: any,
deep: boolean
}

View File

@@ -0,0 +1,6 @@
import { IAclRights } from './acl.rights';
import { IItem } from './item';
export interface IListItemAclRights extends IItem {
aclRights: IAclRights[]
}

View File

@@ -0,0 +1,13 @@
import { IUser } from "./user";
export interface ISharedItem {
code: string,
name: string,
type: string,
sharedToUser: IUser,
rights: Date,
expires: Date,
dataDate: Date,
childItems: ISharedItem[]
}

View File

@@ -16,4 +16,9 @@ export interface IUser {
searchable: boolean;
newsletter?: boolean;
language?: string;
}
export interface UserInfo {
sub: string;
[key: string]: any;
}

View File

@@ -3,7 +3,6 @@ import { Router, Route, ActivatedRouteSnapshot, RouterStateSnapshot, UrlSegment,
import { Store } from '@ngrx/store';
import { OAuthService } from 'angular-oauth2-oidc';
import { Observable } from 'rxjs';
@@ -15,7 +14,7 @@ import * as appCommonReducer from '../reducers/app-common.reducer';
})
export class AuthGuard {
constructor(private oauthService: OAuthService, private router: Router, private store: Store<appCommonReducer.State>) { }
constructor(private router: Router, private store: Store<appCommonReducer.State>) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
//console.debug("AuthGuard->canActivate", route, state);
@@ -36,36 +35,38 @@ export class AuthGuard {
return this.checkLogin(route.path, null);
}
//TODO FIX THIS
checkLogin(url: string, route: ActivatedRouteSnapshot): boolean {
//console.debug("AuthGuard->checkLogin", url, route);
if (!this.oauthService.hasValidAccessToken()) {
//console.debug("No valid token");
this.oauthService.initCodeFlow(url);
return false;
} else {
const requiredRoleClaim = route.data.role;
if (!requiredRoleClaim) { return true; }
const ownedClaims = this.oauthService.getIdentityClaims();
if (!ownedClaims) {
//console.debug("No owned claims");
return false;
}
const ownedRoleClaims: string[] = ownedClaims['role'];
if (!ownedRoleClaims) {
// console.debug("No owned role claims");
return false;
}
if (Array.isArray(ownedRoleClaims)) {
if (ownedRoleClaims.findIndex(r => r === requiredRoleClaim) <= -1) {
//console.debug("No required role claim", ownedRoleClaims, requiredRoleClaim);
return false;
}
}
else {
if (ownedRoleClaims !== requiredRoleClaim) { console.debug("No required role claim", ownedRoleClaims, requiredRoleClaim); return false; }
}
//console.debug("Has required role claim", requiredRoleClaim);
return true;
}
// if (!this.oauthService.hasValidAccessToken()) {
// //console.debug("No valid token");
// this.oauthService.initCodeFlow(url);
// return false;
// } else {
// const requiredRoleClaim = route.data.role;
// if (!requiredRoleClaim) { return true; }
// const ownedClaims = this.oauthService.getIdentityClaims();
// if (!ownedClaims) {
// //console.debug("No owned claims");
// return false;
// }
// const ownedRoleClaims: string[] = ownedClaims['role'];
// if (!ownedRoleClaims) {
// // console.debug("No owned role claims");
// return false;
// }
// if (Array.isArray(ownedRoleClaims)) {
// if (ownedRoleClaims.findIndex(r => r === requiredRoleClaim) <= -1) {
// //console.debug("No required role claim", ownedRoleClaims, requiredRoleClaim);
// return false;
// }
// }
// else {
// if (ownedRoleClaims !== requiredRoleClaim) { console.debug("No required role claim", ownedRoleClaims, requiredRoleClaim); return false; }
// }
// //console.debug("Has required role claim", requiredRoleClaim);
// return true;
// }
return true;
}
}

View File

@@ -0,0 +1,44 @@
import { Injectable } from '@angular/core';
import { Observable, ReplaySubject, Subscription, timer } from 'rxjs';
import { catchError, take } from 'rxjs/operators';
import { IItem } from '../models/item';
import { ItemService } from './item.service';
const REFRESH_INTERVAL = 15 * 60 * 1000; // 15m
@Injectable({ providedIn: 'root'})
export class CacheService {
private proxyCacheMap: { [key: string]: ReplaySubject<IItem[]> } = {};
private subscriptionMap: { [key: string]: Subscription } = {};
constructor(private itemService: ItemService) {
timer(0, REFRESH_INTERVAL).subscribe(() => {
this.subscriptionMap = {};
})
}
getItemList(itemType: string) : Observable<IItem[]> {
if (!this.proxyCacheMap[itemType]) {
this.proxyCacheMap[itemType] = new ReplaySubject(1);
}
if (!this.subscriptionMap[itemType]) {
this.subscriptionMap[itemType] = this.itemService.getItemList(itemType)
.pipe(
catchError(error => {
this.subscriptionMap[itemType].unsubscribe();
this.subscriptionMap[itemType] = null;
throw error;
}),
).subscribe(items => {
this.proxyCacheMap[itemType].next(items);
});
}
return this.proxyCacheMap[itemType].asObservable()
.pipe(
take(1)
);
}
}

View File

@@ -1,7 +1,6 @@
import { Injectable } from '@angular/core';
import { IEventMessage } from '../models/event.message';
import { Subject, timer } from 'rxjs';
import { OAuthService } from 'angular-oauth2-oidc';
import { HubConnection, HubConnectionBuilder, LogLevel ,HttpTransportType,HubConnectionState} from '@microsoft/signalr';
import { AppConfig } from "../shared/app.config";
@@ -16,7 +15,7 @@ export class EventService {
private _apiEndPoint: string;
public authenticated = false;
constructor(private oauthService: OAuthService, private appConfig: AppConfig) {
constructor(private appConfig: AppConfig) {
this._apiEndPoint = appConfig.getConfig("apiEndPoint");
this._connection = new HubConnectionBuilder().withUrl(`${ this._apiEndPoint}/eventHub`,
{ transport: HttpTransportType.WebSockets,
@@ -41,17 +40,9 @@ export class EventService {
});
}
private Authenticate() {
const accessToken = this.oauthService.getAccessToken();
if (this.oauthService.hasValidAccessToken()) {
this._connection.send('authenticate', this.oauthService.getAccessToken());
private Authenticate() { //TODO FIX THIS
this._connection.send('authenticate', "token");
this.authenticated=true;
} else {
//try again after half a second
setTimeout(() => {
this.Authenticate();
}, 800);
}
}
Stop() {

View File

@@ -0,0 +1,74 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { IListItemAclRights } from '../models/list.item.acl.rights';
import { ISharedItem } from '../models/shared.item';
import { AppConfig } from '../shared/app.config';
import { IListItem } from '../models/list.item';
@Injectable({
providedIn: 'root'
})
export class SharedItemService {
constructor(
public httpClient: HttpClient,
public appConfig: AppConfig) {
}
apiEndpoint() {
return this.appConfig.getConfig('apiEndPoint');
}
public getSharedUsersForItem(itemCode: string): Observable<any> {
return this.httpClient.get<any>(`${this.apiEndpoint()}/api/v1/items/${itemCode}/sharedusers`);
}
getSharedItemsWithRightsInfo(userCode: string): Observable<ISharedItem[]> {
return this.httpClient.get<any>(`${this.apiEndpoint()}/api/v1/users/${userCode}/shared`);
}
shareItem(itemCode: string, userCode: string, userRights: string, expireTimespan: string, deep: boolean): Observable<any> {
const body = {
rights: userRights,
expire: expireTimespan,
deep: deep
};
return this.httpClient.post<any>(`${this.apiEndpoint()}/api/v1/items/${itemCode}/share/${userCode}`, body);
}
revokeSharedItem(itemCode: string, userCode: string): Observable<any> {
return this.httpClient.delete<any>(`${this.apiEndpoint()}/api/v1/items/${itemCode}/share/${userCode}`);
}
getSharedItemRights(sharedByMe: boolean): Observable<IListItemAclRights[]> {
let params = new HttpParams();
params = params.append('byMe', sharedByMe);
return this.httpClient.get<any>(`${this.apiEndpoint()}/api/v1/user/rights`, {params: params});
}
getSharedItemsByParent(parentCode: string): Observable<ISharedItem[]> {
return this.httpClient.get<any>(`${this.apiEndpoint()}/api/v1/items/${parentCode}/sharedchildren`);
}
getSharedWithCurrentUser(profile: string): Observable<IListItem[]> {
let params = new HttpParams();
if (profile != null) {
params = params.append('sharedBy', profile);
}
return this.httpClient.get<any>(`${this.apiEndpoint()}/api/v1/items/shared/`, {params: params});
}
// this method does not belong here, belongs in some sort of item service
public copyItem(itemCode: string, newParentCode: string, newUserCode: string): Observable<any> {
const body = {
itemCode: itemCode,
newParentCode: newParentCode,
newUserCode: newUserCode
};
return this.httpClient.post<any>(`${this.apiEndpoint()}/api/v1/items/copy`, body);
}
}

View File

@@ -0,0 +1,35 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { AppConfig } from '../shared/app.config';
import { IUser } from '../models/user';
@Injectable({
providedIn: 'root'
})
export class UserDataService {
constructor(
public httpClient: HttpClient,
public appConfig: AppConfig) {
}
ApiEndpoint() {
return this.appConfig.getConfig('apiEndPoint');
}
loadUserNotifications(): Observable<any> {
return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/notifications`);
}
readUserNotification(notificationCode: string): Observable<any> {
return this.httpClient.put<any>(`${this.ApiEndpoint()}/api/v1/notifications/${notificationCode}`, null);
}
deleteUserNotification(notificationCode: string): Observable<any> {
return this.httpClient.delete<any>(`${this.ApiEndpoint()}/api/v1/notifications/${notificationCode}`);
}
getUserConnections(userCode: string, active: boolean): Observable<IUser[]> {
return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/users/${userCode}/connections?active=${active}`);
}
}

View File

@@ -1,40 +0,0 @@
import { Injectable, Injector, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common'
import { AppConfig } from "./app.config";
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { OAuthService } from 'angular-oauth2-oidc';
import { Observable } from 'rxjs';
@Injectable()
export class AccessTokenInterceptor implements HttpInterceptor {
private oauthService: OAuthService = null;
private audience: string[] = [];
private base: string;
constructor(private injector: Injector, private appConfig: AppConfig, @Inject(DOCUMENT) private document: any) {
this.base = document.location.href;
}
hasAudience(url: string): boolean {
const u = new URL(url,this.base);
for (const audience of this.audience) {
if (u.href.startsWith(audience)) return true;
}
return false;
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.oauthService && this.hasAudience(request.url)) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${this.oauthService.getAccessToken()}`
}
});
} else {
this.oauthService = this.injector.get(OAuthService, null);
if(this.oauthService && this.oauthService.issuer) this.audience = (this.appConfig.getConfig("audience") as string).split(",");
}
return next.handle(request);
}
}

View File

@@ -1,23 +0,0 @@
import { Injector } from '@angular/core';
import { Location} from '@angular/common';
import { Router,UrlSerializer } from '@angular/router';
import { AuthConfig, OAuthService, OAuthErrorEvent, OAuthStorage } from 'angular-oauth2-oidc';
import { AppConfig } from "./app.config";
import {ItemTypeService} from '../services/itemtype.service';
import { IAuthconfigFactory } from './authconfigFactory';
export function appConfigFactory(injector:Injector, appConfig: AppConfig, oauthService: OAuthService, authconfigFactory:IAuthconfigFactory,authStorage:OAuthStorage,itemtypeService:ItemTypeService): () => Promise<any> {
return (): Promise<any> => {
return new Promise<void>((resolve,reject) => {
appConfig.load().then(() => {
itemtypeService.load(appConfig);
oauthService.configure(authconfigFactory.getAuthConfig(appConfig));
oauthService.setStorage(authStorage);
oauthService.setupAutomaticSilentRefresh();
}).then(() => oauthService.loadDiscoveryDocument()
).then(() => resolve()).catch(() => reject());
});
}
}

View File

@@ -7,11 +7,8 @@ import {Observable} from 'rxjs';
export class AppConfig {
private config: Object = null;
private httpClient: HttpClient;
constructor(xhrBackend: HttpXhrBackend,private location:Location) {
this.httpClient = new HttpClient(xhrBackend);
this.config = null;
constructor() {
}
public getConfig(key: any) {
@@ -21,9 +18,10 @@ export class AppConfig {
return this.config[key];
}
public load(): Promise<any> {
const url = this.location.prepareExternalUrl('/configuration.json');
return this.httpClient.get(url)
public load(xhrBackend: HttpXhrBackend,location:Location): Promise<any> {
const httpClient = new HttpClient(xhrBackend);
const url = location.prepareExternalUrl('/configuration.json');
return httpClient.get(url)
.toPromise()
.then(data => {
this.config = data;

View File

@@ -1,22 +0,0 @@
import { AuthConfig } from 'angular-oauth2-oidc';
import {AppConfig} from './app.config';
export interface IAuthconfigFactory {
getAuthConfig(appConfig: AppConfig): AuthConfig;
}
export class AuthConfigFactory implements IAuthconfigFactory {
getAuthConfig(appConfig: AppConfig): AuthConfig {
const authConfig: AuthConfig = new AuthConfig();
authConfig.issuer = appConfig.getConfig("issuer");
authConfig.redirectUri = window.location.origin + "/cb";
authConfig.silentRefreshRedirectUri = window.location.origin + "/silent-refresh.html";
authConfig.clientId = appConfig.getConfig("clientId");
authConfig.customQueryParams = { audience: appConfig.getConfig("audience") };
authConfig.scope = "openid profile email";
authConfig.oidc = true;
authConfig.disableAtHashCheck = true;
authConfig.requireHttps = appConfig.getConfig("requireHttps");
return authConfig;
}
}

View File

@@ -1,36 +0,0 @@
import {OAuthStorage} from 'angular-oauth2-oidc';
import {Inject, Injectable} from '@angular/core';
@Injectable()
export class SecureOAuthStorage extends OAuthStorage {
private storage = {};
secureKey(key:string): boolean {
if(key == "nonce") return false;
if(key == "PKCI_verifier") return false;
return true;
}
getItem(key: string): string {
if(this.secureKey(key)) {
return this.storage[key];
} else {
return window.sessionStorage.getItem(key);
}
}
removeItem(key: string): void {
if(this.secureKey(key)) {
delete this.storage[key];
} else {
window.sessionStorage.removeItem(key);
}
}
setItem(key: string, data: string): void {
if(this.secureKey(key)) {
this.storage[key]=data;
} else {
window.sessionStorage.setItem(key,data);
}
}
}

View File

@@ -1,18 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false }
}
);

View File

@@ -3,7 +3,7 @@
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"module": "es2015",
"moduleResolution": "node",
"moduleResolution": "bundler",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
@@ -12,10 +12,6 @@
"importHelpers": true,
"esModuleInterop": true,
"types": [],
"lib": [
"dom",
"es2018"
],
"paths": {
"@angular/*": [
"node_modules/@angular/*"
@@ -31,7 +27,6 @@
"compilationMode": "partial"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -3,7 +3,7 @@
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"module": "es2015",
"moduleResolution": "node",
"moduleResolution": "bundler",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
@@ -12,15 +12,9 @@
"importHelpers": true,
"esModuleInterop": true,
"types": [],
"lib": [
"dom",
"es2018"
],
"paths": {
"@angular/*": [
"node_modules/@angular/*"
"node_modules/@angular/*"
]
}
},
@@ -33,7 +27,6 @@
"compilationMode": "partial"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -3,12 +3,10 @@
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",

View File

@@ -1,32 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/ng-openlayers'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true,
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true,
});
};

View File

@@ -1,54 +1,63 @@
{
"name": "ng-openlayers",
"name": "@farmmaps/ng-openlayers",
"version": "18.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ng-openlayers",
"name": "@farmmaps/ng-openlayers",
"version": "18.0.0",
"license": "MPL-2.0",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "^18.2.3",
"@angular/core": "^18.2.3",
"@angular/common": "21.1.0",
"@angular/core": "21.1.0",
"ol": "^8.2.0"
}
},
"node_modules/@angular/common": {
"version": "18.2.3",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.3.tgz",
"integrity": "sha512-NFL4yXXImSCH7i1xnHykUjHa9vl9827fGiwSV2mnf7LjSUsyDzFD8/54dNuYN9OY8AUD+PnK0YdNro6cczVyIA==",
"version": "21.1.0",
"resolved": "https://repository.akkerweb.nl/repository/npm-group/@angular/common/-/common-21.1.0.tgz",
"integrity": "sha512-hL3Chp51TU9iBcIfkNtoBS1wuseP1gsyDW2IFtK5HUpAVhbso9B3fdCaDTFkU98A2unluo2YgzI6D/6IS6N+1g==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/core": "18.2.3",
"@angular/core": "21.1.0",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/core": {
"version": "18.2.3",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.3.tgz",
"integrity": "sha512-VGhMJxj7d0rYpqVfQrcGRB7EE/BCziotft/I/YPl6bOMPSAvMukG7DXQuJdYpNrr62ks78mlzHlZX/cdmB9Prw==",
"version": "21.1.0",
"resolved": "https://repository.akkerweb.nl/repository/npm-group/@angular/core/-/core-21.1.0.tgz",
"integrity": "sha512-QTl9s8GYNN0pt1k3GE6UVlfe6zWtfdykhfchinKq2YJywQ6LBM4UcZgoc56YkgscmyrRFYrr4JYUJjlzTF57+A==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/compiler": "21.1.0",
"rxjs": "^6.5.3 || ^7.4.0",
"zone.js": "~0.14.10"
"zone.js": "~0.15.0 || ~0.16.0"
},
"peerDependenciesMeta": {
"@angular/compiler": {
"optional": true
},
"zone.js": {
"optional": true
}
}
},
"node_modules/@petamoriken/float16": {
@@ -246,9 +255,9 @@
}
},
"node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"version": "7.8.2",
"resolved": "https://repository.akkerweb.nl/repository/npm-group/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
@@ -274,13 +283,6 @@
"license": "CC0-1.0",
"peer": true
},
"node_modules/zone.js": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz",
"integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==",
"license": "MIT",
"peer": true
},
"node_modules/zstddec": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz",

View File

@@ -31,8 +31,8 @@
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "19.2.18",
"@angular/core": "19.2.18",
"@angular/common": "^21.1.0",
"@angular/core": "^21.1.0",
"ol": "^8.2.0"
}
}

View File

@@ -18,11 +18,10 @@
}
},
"test": {
"executor": "@angular-devkit/build-angular:karma",
"executor": "@angular/build:unit-test",
"options": {
"main": "libs/ng-openlayers/src/test.ts",
"tsConfig": "libs/ng-openlayers/tsconfig.spec.json",
"karmaConfig": "libs/ng-openlayers/karma.conf.js"
"browsers": ["chromium"]
}
},
"lint": {

View File

@@ -1,12 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false },
});

View File

@@ -3,7 +3,7 @@
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"module": "es2015",
"moduleResolution": "node",
"moduleResolution": "bundler",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
@@ -12,10 +12,6 @@
"importHelpers": true,
"esModuleInterop": true,
"types": [],
"lib": [
"dom",
"es2018"
],
"paths": {
"@angular/*": [
"node_modules/@angular/*"
@@ -31,7 +27,6 @@
"compilationMode": "partial"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -3,7 +3,7 @@
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"module": "es2015",
"moduleResolution": "node",
"moduleResolution": "bundler",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
@@ -12,15 +12,9 @@
"importHelpers": true,
"esModuleInterop": true,
"types": [],
"lib": [
"dom",
"es2018"
],
"paths": {
"@angular/*": [
"node_modules/@angular/*"
"node_modules/@angular/*"
]
}
},
@@ -33,7 +27,6 @@
"compilationMode": "partial"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -3,12 +3,10 @@
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",

View File

@@ -1,7 +0,0 @@
{
"extends": [
"./tslint.json",
"tslint-config-prettier",
"tslint-plugin-prettier"
]
}

View File

@@ -6,7 +6,7 @@ import {
} from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppCommonModule,AppCommonServiceModule,AuthConfigFactory,FM_COMMON_STARTPAGE } from '@farmmaps/common';
import { AppCommonModule,AppCommonServiceModule,FM_COMMON_STARTPAGE } from '@farmmaps/common';
import {AppRootComponent} from './app.component';
@@ -18,8 +18,6 @@ import {AppRoutingModule} from './app-routing.module';
import { LogoComponent } from './logo/logo.component';
import { MenuComponent } from './menu/menu.component';
import {RegisterDeviceComponent} from './registerdevice/registerdevice.component';
import { SecureOAuthStorage} from '@farmmaps/common';
import { OAuthStorage } from 'angular-oauth2-oidc';
import {Id4AuthconfigFactory} from './id4AuthconfigFactory';
import { TestComponent } from './test/test.component';
import { LandingpageComponent } from './landingpage/landingpage.component';
@@ -85,11 +83,6 @@ export const metaReducers: MetaReducer<any>[] = [debug];
EffectsModule.forRoot([]),
],
providers: [
AuthConfigFactory,
{
provide:AuthConfigFactory,
useClass:Id4AuthconfigFactory
},
{
provide: FM_COMMON_STARTPAGE,
useValue: '/map'

View File

@@ -1,23 +0,0 @@
import { IAuthconfigFactory, AppConfig } from '@farmmaps/common';
import { AuthConfig } from 'angular-oauth2-oidc';
import { Injectable } from "@angular/core";
@Injectable()
export class Id4AuthconfigFactory implements IAuthconfigFactory {
constructor() {
}
getAuthConfig(appConfig: AppConfig): AuthConfig {
const authConfig: AuthConfig = new AuthConfig();
authConfig.issuer = appConfig.getConfig("issuer");
authConfig.redirectUri = window.location.origin + "/cb";
authConfig.clientId = appConfig.getConfig("clientId");
authConfig.customQueryParams = { audience: appConfig.getConfig("audience") };
authConfig.scope = "openid profile api offline_access";
authConfig.disableAtHashCheck = true;
authConfig.responseType = "code";
authConfig.requireHttps = appConfig.getConfig("requireHttps");
return authConfig;
}
}

View File

@@ -1,22 +0,0 @@
import {IAuthconfigFactory,AppConfig} from '@farmmaps/common';
import {AuthConfig} from 'angular-oauth2-oidc';
export class LocalAuthconfigFactory implements IAuthconfigFactory {
getAuthConfig(appConfig:AppConfig): AuthConfig {
const authConfig: AuthConfig = new AuthConfig();
authConfig.issuer = appConfig.getConfig("issuer");
authConfig.redirectUri = window.location.origin + "/cb";
authConfig.silentRefreshRedirectUri = window.location.origin + "/silent-refresh.html";
authConfig.clientId = appConfig.getConfig("clientId");
authConfig.customQueryParams = { audience: appConfig.getConfig("audience") };
authConfig.scope = "openid profile email";
authConfig.oidc = true;
authConfig.disableAtHashCheck = true;
authConfig.openUri = uri => {
window.alert("OK "+uri);
}
authConfig.requireHttps = appConfig.getConfig("requireHttps");
return authConfig;
}
}

View File

@@ -1,32 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage/farmmaps-lib-app'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View File

@@ -1,6 +1,6 @@
import * as Cesium from 'cesium';
import { enableProdMode } from '@angular/core';
import { enableProdMode, provideZoneChangeDetection } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
@@ -15,5 +15,5 @@ if (environment.production) {
window['CESIUM_BASE_URL'] = '/assets/cesium/';
window['Cesium'] = Cesium;
platformBrowserDynamic().bootstrapModule(AppModule)
platformBrowserDynamic().bootstrapModule(AppModule, { applicationProviders: [provideZoneChangeDetection()], })
.catch(err => console.error(err));

Some files were not shown because too many files have changed in this diff Show More