diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..fd6405c9 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,67 @@ +module.exports = { + // https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy + // This option interrupts the configuration hierarchy at this file + // Remove this if you have an higher level ESLint config file (it usually happens into a monorepos) + root: true, + + parserOptions: { + parser: '@babel/eslint-parser', + ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features + sourceType: 'module' // Allows for the use of imports + }, + + env: { + browser: true, + 'vue/setup-compiler-macros': true + }, + + // Rules order is important, please avoid shuffling them + extends: [ + // Base ESLint recommended rules + // 'eslint:recommended', + + // Uncomment any of the lines below to choose desired strictness, + // but leave only one uncommented! + // See https://eslint.vuejs.org/rules/#available-rules + 'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention) + // 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability) + // 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead) + + // https://github.com/prettier/eslint-config-prettier#installation + // usage with Prettier, provided by 'eslint-config-prettier'. + 'prettier' + ], + + plugins: [ + // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files + // required to lint *.vue files + 'vue', + + // https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674 + // Prettier has not been included as plugin to avoid performance impact + // add it as an extension for your IDE + + ], + + globals: { + cordova: 'readonly', + __statics: 'readonly', + __QUASAR_SSR__: 'readonly', + __QUASAR_SSR_SERVER__: 'readonly', + __QUASAR_SSR_CLIENT__: 'readonly', + __QUASAR_SSR_PWA__: 'readonly', + process: 'readonly', + Capacitor: 'readonly', + chrome: 'readonly' + }, + + // add your custom rules here + rules: { + + 'prefer-promise-reject-errors': 'off', + 'vue/multi-word-component-names': 0, + + // allow debugger during development only + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' + } +} diff --git a/.gitignore b/.gitignore index c374ee9d..3d995b41 100644 --- a/.gitignore +++ b/.gitignore @@ -87,4 +87,4 @@ src/tours/core src/tours/map #KDK icons -src/assets/icons +public/icons/kdk/*.png diff --git a/.postcssrc.js b/.postcssrc.js index 1174fe52..0ee0d8c2 100644 --- a/.postcssrc.js +++ b/.postcssrc.js @@ -1,3 +1,4 @@ +/* eslint-disable */ // https://github.com/michael-ciniawsky/postcss-load-config module.exports = { diff --git a/.travis.yml b/.travis.yml index 9f3a1cb7..fb57ed4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,7 +62,7 @@ jobs: licenses: - '.+' before_install: - - nvm install 16 + - nvm install 12.16 - yes | sdkmanager "platforms;android-30" - npm install -g yarn - curl -k -O https://downloads.rclone.org/rclone-current-linux-amd64.zip @@ -85,7 +85,7 @@ jobs: language: objective-c osx_image: xcode13.2 before_install: - - nvm install 16 + - nvm install 12.16 - npm install -g yarn - brew update - brew install rclone diff --git a/babel.config.js b/babel.config.js index 9408c6cd..063ef53a 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,5 +1,14 @@ -module.exports = { - presets: [ - '@quasar/babel-preset-app' - ] +/* eslint-disable */ + +module.exports = api => { + return { + presets: [ + [ + '@quasar/babel-preset-app', + api.caller(caller => caller && caller.target === 'node') + ? { targets: { node: 'current' } } + : {} + ] + ] + } } diff --git a/config/server.crt b/config/server.crt deleted file mode 100644 index d96dd2a8..00000000 --- a/config/server.crt +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICeTCCAeICCQCAQm+gASW/hTANBgkqhkiG9w0BAQsFADCBgDELMAkGA1UEBhMC -RlIxEjAQBgNVBAgMCU9jY2l0YW5pZTEWMBQGA1UEBwwNQ2FzdGVsbmF1ZGFyeTEN -MAsGA1UECgwEa0FwcDESMBAGA1UEAwwJbG9jYWxob3N0MSIwIAYJKoZIhvcNAQkB -FhNrYWxpc2lvQGthbGlzaW8ueHl6MB4XDTE3MDkwMzE2NDUwMloXDTE4MDkwMzE2 -NDUwMlowgYAxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxFjAUBgNV -BAcMDUNhc3RlbG5hdWRhcnkxDTALBgNVBAoMBGtBcHAxEjAQBgNVBAMMCWxvY2Fs -aG9zdDEiMCAGCSqGSIb3DQEJARYTa2FsaXNpb0BrYWxpc2lvLnh5ejCBnzANBgkq -hkiG9w0BAQEFAAOBjQAwgYkCgYEAuL9HXRLZK849yfEDDNAzWJ5HK78yzUgX5+xY -oHEpGeMS6fSs3xIQq39wUrr5+oT8+eveKyY0o/DAIJm6wHbcmRVWENwqkf7D/pt9 -bP41/Aoib0eahj/KD2aSoPHWoq7ZxOWJHaySStBKxRgrn6Qx+zfBqpBG3v3I1yXU -cAB+kT8CAwEAATANBgkqhkiG9w0BAQsFAAOBgQCWQ7shHN0fQG6KTJxDNX2XR/jq -R8b0OgSZNgK7EnijpgLvLhcYAOp+wYeuZTRjmhg+S4L+YGQWaVgD+VYqV19svaCB -564JZLLevcw3lJhgtjdFrH67nESAlLdcATC/lzkNNs80DQmY3C21A9v4Yi0tNs4T -lOVKAMsd6c+xPU+HnQ== ------END CERTIFICATE----- diff --git a/config/server.key b/config/server.key deleted file mode 100644 index 0b1f5e45..00000000 --- a/config/server.key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICWwIBAAKBgQC4v0ddEtkrzj3J8QMM0DNYnkcrvzLNSBfn7FigcSkZ4xLp9Kzf -EhCrf3BSuvn6hPz5694rJjSj8MAgmbrAdtyZFVYQ3CqR/sP+m31s/jX8CiJvR5qG -P8oPZpKg8dairtnE5YkdrJJK0ErFGCufpDH7N8GqkEbe/cjXJdRwAH6RPwIDAQAB -AoGAErcV1KDnLR/cdIucwnyI5w5JqW3aCJ+oBE+V/tLX1g6ByP+HT6W12Jm1WDwm -vLEiK3Mwc3CQXpcx71uDZqHj4NXk4iVZzr+JgBhr38cdMMhvdnu5AdJNoL5b0XPg -GSLWQ1m4KRgfmZQ7r/qQ+cmQfNtEIM3jfFLU3C1efc4oNPECQQDtQ1QITxmkqa4c -an+7IcH+5xu0m9tJrdV29CtmgPx/9XWSqsh5cUYfPTM5AgBcxsWoUk+RqdhW7gV5 -7jWBDVcXAkEAx1ZBMfIP/sh3NrKsXa/IJuiKjbtSLylpeyVjUTQQHCzBxUGhzrr/ -/mTmbJBe+oT9WWm5FJTX0+LDFkK6LwFwGQJAZNFfO8ig1N9lJJ38eL/3dyEtHKm9 -nKlJM6q9ZmkY4iajNxdq6G/BpDSVN3wqO0Iba1LOjM7nJskteopGIfhRMQJAVeN2 -Z9zSw4cAxtKprhxlc05TFMqXmLaROnxblMuH/XvTafjjjT3Ju4UiO7jhE7RlrTb+ -hj2z2Rol0NujeUl3UQJAKczD1f+gCZ2StozLGc5vSI36bOMkb429jC9nF6+GyhP0 -NgTy2FOMzThU68yf+zqK2vQKuVSQh9w9k0qZVsP7yw== ------END RSA PRIVATE KEY----- diff --git a/package.json b/package.json index 5f6d8306..513ac9a7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "aktnmap", "description": "Kalisio Akt'n'Map Application", - "version": "2.0.0", + "version": "1.4.0", "homepage": "https://github.com/kalisio/aktnmap", "keywords": [ "kalisio" @@ -42,7 +42,7 @@ "analyze": "npm run analyze:stats && npm run analyze:treemap", "lint": "standard src/**/*.js src/**/*.vue test/**/*.js tutorials/**/*.js --fix", "benchmark": "node ./benchmark", - "icons:cp": "shx cp -R node_modules/@kalisio/kdk/extras/icons src/assets", + "icons:cp": "shx cp -R node_modules/@kalisio/kdk/extras/icons public/icons/kdk", "tours:cp": "shx cp -R node_modules/@kalisio/kdk/extras/tours/* src/tours", "mocha": "mocha test/**/*.test.js --timeout 30000 --require esm", "coverage": "c8 npm run mocha", @@ -66,7 +66,7 @@ "mocha" ], "ignore": [ - "public" + "src/statics" ], "globals": [ "DEV", @@ -88,11 +88,11 @@ }, "dependencies": { "@feathersjs/client": "^5.0.0-pre.22", + "@feathersjs/commons": "^5.0.0-pre.22", "@geoman-io/leaflet-geoman-free": "^2.11.1", "@kalisio/kdk": "https://github.com/kalisio/kdk#es-modules", "@mapbox/geojsonhint": "^3.0.1", - "@panter/vue-i18next": "^0.9.1", - "@quasar/extras": "^1.6.0", + "@quasar/extras": "^1.0.0", "@tmcw/togeojson": "^4.5.0", "@turf/area": "^6.0.1", "@turf/bbox": "^6.0.1", @@ -101,6 +101,7 @@ "@turf/clean-coords": "^6.0.0", "@turf/distance": "^6.0.0", "@turf/explode": "^6.2.0-alpha.2", + "@turf/flatten": "^6.0.0", "@turf/helpers": "^6.0.0", "@turf/invariant": "^6.0.0", "@turf/kinks": "^6.0.0", @@ -108,110 +109,108 @@ "@turf/meta": "^6.0.0", "@weacast/core": "^2.1.2", "@weacast/leaflet": "^2.1.2", - "Leaflet.Geodesic": "https://github.com/henrythasler/Leaflet.Geodesic.git", "abort-controller": "^3.0.0", - "ajv": "^6.0.0", + "ajv": "6.0.0", "ajv-i18n": "^3.6.0", - "babel-polyfill": "^6.23.0", - "babel-runtime": "^6.25.0", + "assert": "^2.0.0", + "browserify-zlib": "^0.2.0", "casl": "^1.0.2", "chart.js": "^3.7.1", "chartjs-adapter-moment": "^1.0.0", - "chartjs-plugin-annotation": "^1.3.1", + "chartjs-plugin-annotation": "^1.4.0", "chartjs-plugin-datalabels": "^2.0.0", "chartjs-plugin-zoom": "^1.2.1", - "chroma-js": "^2.0.2", - "cross-env": "^5.2.0", + "chroma-js": "^2.4.2", + "d3": "^4.9.1", "email-validator": "^2.0.4", "fastclick": "^1.0.6", - "feathers-authentication-management": "^1.0.0", - "feathers-client": "^2.4.0", - "feathers-memory": "^2.2.0", + "feathers-memory": "^4.1.0", "feathers-reactive": "^0.10.0", "formatcoords": "^1.1.3", "georaster": "^0.4.2", "geotiff": "^2.0.4", - "i18next": "^10.5.0", + "https-browserify": "^1.0.0", + "i18next": "^21.6.16", "iso8601-js-period": "^0.2.1", "jquery": "^3.2.1", "js-yaml": "^3.13.1", "jsdap": "^8.1.0", - "leaflet": "^1.2.0", + "leaflet": "1.6.0", "leaflet-fa-markers": "^0.0.5", "leaflet-fullscreen": "^1.0.2", "leaflet-heatmap": "^1.0.0", "leaflet-pixi-overlay": "1.8.1", "leaflet-realtime": "^2.2.0", "leaflet-timedimension": "^1.1.0", - "leaflet-velocity": "^1.2.2", + "leaflet-velocity": "^1.7.0", + "leaflet.geodesic": "^2.6.1", "leaflet.locatecontrol": "^0.67.0", "leaflet.markercluster": "1.1.0", "leaflet.vectorgrid": "^1.3.0", "lodash": "^4.17.21", - "loglevel": "^1.6.0", + "loglevel": "^1.8.0", "mapillary-js": "^4.0.0", - "mathjs": "^10.1.1", - "mime-types-browser": "^0.0.3", + "mathjs": "^10.5.0", + "mime": "^3.0.0", "moment": "^2.29.4", "moment-timezone": "^0.5.34", "node-fetch": "^2.6.7", - "papaparse": "^4.6.1", + "papaparse": "^5.3.2", "password-generator": "^2.2.0", + "path-browserify": "^1.0.1", "pinch-zoom-element": "^1.1.1", "pixi.js": "5.3.10", - "quasar": "^1.9.9", - "sanitize-html": "^1.27.1", - "showdown": "^1.9.1", + "quasar": "^2.7.7", + "sanitize-html": "^2.7.0", + "showdown": "^2.1.0", "shpjs": "^4.0.2", - "sift": "^8.0.0", + "sift": "^16.0.0", "socket.io-client": "^4.4.1", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "timers-browserify": "^2.0.12", "tween.js": "^16.6.0", - "vue-slider-component": "^3.0.33", + "vue": "^3.0.0", + "vue-i18n": "^9.2.0-beta.35", + "vue-router": "^4.0.0", + "vue-slider-component": "next", "vue-stripe-elements-plus": "^0.2.8", - "vue-tour": "~1.4.0", - "vue2-dropzone": "^3.6.0", + "vue3-tour": "https://github.com/alexandreDavid/vue3-tour", "whatwg-fetch": "^2.0.3", - "xml2js": "^0.4.22", + "xml2js": "^0.4.23", "yallist": "^4.0.0" }, "devDependencies": { - "@quasar/app": "1.1.4", - "babel-eslint": "^10.0.3", - "babel-plugin-add-module-exports": "^0.2.1", - "babel-plugin-transform-export-extensions": "^6.22.0", + "@babel/eslint-parser": "^7.13.14", + "@quasar/app-webpack": "^3.5.4", "c8": "^7.11.0", - "chai": "^4.3.4", + "chai": "^4.3.6", "chai-lint": "^0.1.1", - "clui": "^0.3.6", "colors": "^1.1.2", "commander": "^2.15.0", - "config": "^1.26.1", + "config": "^3.3.7", "connect-history-api-fallback": "^1.1.0", - "cordova": "^8.0.0", - "cordova-set-version": "^6.0.3", + "cordova": "^11.0.0", + "cordova-set-version": "^13.0.1", + "cross-env": "^7.0.3", "debug": "^4.1.0", - "envsub": "^3.0.9", - "es6-promise": "^4.1.1", - "eslint": "^4.4.1", - "eslint-friendly-formatter": "^3.0.0", - "eslint-loader": "^1.9.0", - "eslint-plugin-vue": "^5.2.3", + "envsub": "^4.0.7", + "eslint": "^8.10.0", + "eslint-config-prettier": "^8.1.0", + "eslint-plugin-vue": "^8.5.0", + "eslint-webpack-plugin": "^3.1.1", "esm": "^3.2.25", - "eventsource-polyfill": "^0.9.6", "express": "^4.15.4", "glob": "^7.1.2", - "mocha": "^9.1.1", + "mocha": "^10.0.0", "mongodb": "^3.1.13", - "npm-run-all": "^4.1.2", - "opn": "^5.0.0", "pixelmatch": "^5.2.0", "pngjs": "^5.0.0", + "prettier": "^2.5.1", "shelljs": "^0.8.5", - "shx": "^0.3.2", + "shx": "^0.3.4", "sns-mobile": "https://github.com/kalisio/sns-mobile", - "standard": "^14.0.0", - "winston": "^3.2.1", - "worker-farm": "^1.6.0" + "standard": "^17.0.0" }, "resolutions": { "@pixi/accessibility": "5.3.10", @@ -247,6 +246,23 @@ "@pixi/text": "5.3.10", "@pixi/text-bitmap": "5.3.10", "@pixi/ticker": "5.3.10", - "@pixi/utils": "5.3.10" - } + "@pixi/utils": "5.3.10", + "leaflet": "1.6.0" + }, + "engines": { + "node": ">= 16.0.0", + "npm": ">= 6.13.4", + "yarn": ">= 1.21.1" + }, + "browserslist": [ + "last 10 Chrome versions", + "last 10 Firefox versions", + "last 4 Edge versions", + "last 7 Safari versions", + "last 8 Android versions", + "last 8 ChromeAndroid versions", + "last 8 FirefoxAndroid versions", + "last 10 iOS versions", + "last 5 Opera versions" + ] } diff --git a/public/icons/kdk/.gitkeep b/public/icons/kdk/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/quasar.conf.js b/quasar.conf.js deleted file mode 100644 index d5b6654a..00000000 --- a/quasar.conf.js +++ /dev/null @@ -1,239 +0,0 @@ -// Configuration for your app -// https://quasar.dev/quasar-cli/quasar-conf-js - -const path = require('path') -const fs = require('fs') - -const serverPort = process.env.PORT || process.env.HTTPS_PORT || 8081 -const clientPort = process.env.CLIENT_PORT || process.env.HTTPS_CLIENT_PORT || 8080 - -// Load config based on current NODE_ENV, etc. -const clientConfig = require('config') -// Write JSON config -fs.writeFileSync(path.join('config', 'client-config.json'), JSON.stringify(clientConfig)) - -module.exports = function (ctx) { - return { - // app boot file (/src/boot) - // --> boot files are part of "main.js" - boot: [ - 'api', - 'i18n', - 'tour' - ], - - css: [ - 'app.scss' - ], - - extras: [ - 'roboto-font', - 'material-icons', - 'line-awesome', - 'fontawesome-v5' - ], - - framework: { - // iconSet: by default material-icons - // lang: 'de', // Quasar language - - components: [ - 'QAjaxBar', - 'QAvatar', - 'QBadge', - 'QBtn', - 'QCard', - 'QCardSection', - 'QCardActions', - 'QChip', - 'QDate', - 'QDialog', - 'QDrawer', - 'QExpansionItem', - 'QFab', - 'QFabAction', - 'QField', - 'QHeader', - 'QIcon', - 'QImg', - 'QInput', - 'QItem', - 'QItemSection', - 'QItemLabel', - 'QLayout', - 'QList', - 'QMenu', - 'QPage', - 'QPageContainer', - 'QPageSticky', - 'QPagination', - 'QPopupProxy', - 'QResizeObserver', - 'QScrollArea', - 'QSelect', - 'QSeparator', - 'QSpace', - 'QSpinnerCube', - 'QTab', - 'QTabPanel', - 'QTabPanels', - 'QTabs', - 'QTime', - 'QTimeline', - 'QTimelineEntry', - 'QToolbar', - 'QToolbarTitle', - 'QToggle', - 'QTooltip' - ], - - directives: [ - 'Ripple', - 'ClosePopup', - 'TouchSwipe', - 'TouchPan' - ], - - // Quasar plugins - plugins: [ - 'Notify', - 'Dialog', - 'Platform', - 'Loading', - 'BottomSheet', - 'AppFullscreen' - ] - }, - - animations: [], - - supportIE: false, - - build: { - scopeHoisting: true, - // vueRouterMode: 'history', - // vueCompiler: true, - // gzip: true, - // analyze: true, - // extractCSS: false, - extendWebpack (cfg) { - cfg.resolve.modules = [ - path.resolve(__dirname, 'src'), - path.resolve(__dirname, 'api/dist/common'), - path.resolve(__dirname, 'node_modules') - ], - cfg.resolve.alias = { - ...cfg.resolve.alias, // This adds the existing aliases - '@': path.resolve(__dirname, './src/components'), - common: path.resolve(__dirname, 'api/dist/common'), - config: path.resolve(__dirname, './config/client-config.json') - }, - cfg.optimization.minimize = process.env.DEBUG ? false : cfg.optimization.minimize - } - }, - - devServer: { - port: clientPort, - proxy: { - '/api': { - target: 'http://localhost:' + serverPort, - changeOrigin: true, - logLevel: 'debug' - }, - '/apiws': { - target: 'http://localhost:' + serverPort, - changeOrigin: true, - ws: true, - logLevel: 'debug' - }, - // The auth endpoints are not easy to prefix so we manage it manually - '/auth': { - target: 'http://localhost:' + serverPort, - changeOrigin: true, - logLevel: 'debug' - } - }, - // https: true, - // port: 8080, - open: true // opens browser window automatically - }, - - ssr: { - pwa: false - }, - - pwa: { - // workboxPluginMode: 'InjectManifest', - // workboxOptions: {}, // only for NON InjectManifest - manifest: { - name: 'Aktnmap', - short_name: 'Aktnmap', - description: 'Monitor real-time events on the field', - display: 'standalone', - orientation: 'portrait', - background_color: '#ffffff', - theme_color: '#027be3', - icons: [ - { - 'src': 'statics/icons/icon-128x128.png', - 'sizes': '128x128', - 'type': 'image/png' - }, - { - 'src': 'statics/icons/icon-192x192.png', - 'sizes': '192x192', - 'type': 'image/png' - }, - { - 'src': 'statics/icons/icon-256x256.png', - 'sizes': '256x256', - 'type': 'image/png' - }, - { - 'src': 'statics/icons/icon-384x384.png', - 'sizes': '384x384', - 'type': 'image/png' - }, - { - 'src': 'statics/icons/icon-512x512.png', - 'sizes': '512x512', - 'type': 'image/png' - } - ] - } - }, - - cordova: { - id: process.env.PACKAGE_ID - }, - - electron: { - // bundler: 'builder', // or 'packager' - - extendWebpack (cfg) { - // do something with Electron main process Webpack cfg - // chainWebpack also available besides this extendWebpack - }, - - packager: { - // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options - - // OS X / Mac App Store - // appBundleId: '', - // appCategoryType: '', - // osxSign: '', - // protocol: 'myapp://path', - - // Windows only - // win32metadata: { ... } - }, - - builder: { - // https://www.electron.build/configuration/configuration - - // appId: 'titi' - } - } - } -} - diff --git a/quasar.config.js b/quasar.config.js new file mode 100644 index 00000000..679605d3 --- /dev/null +++ b/quasar.config.js @@ -0,0 +1,356 @@ +/* eslint-env node */ + +/* + * This file runs in a Node context (it's NOT transpiled by Babel), so use only + * the ES6 features that are supported by your Node version. https://node.green/ + */ + +// Configuration for your app +// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js + +const path = require('path') +const fs = require('fs') +const ESLintPlugin = require('eslint-webpack-plugin') +const { configure } = require('quasar/wrappers') + +const serverPort = process.env.PORT || process.env.HTTPS_PORT || 8081 +const clientPort = process.env.CLIENT_PORT || process.env.HTTPS_CLIENT_PORT || 8080 + +// Load config based on current NODE_ENV, etc. +const clientConfig = require('config') +// Write JSON config +fs.writeFileSync(path.join('config', 'client-config.json'), JSON.stringify(clientConfig)) + +module.exports = configure(function (ctx) { + return { + // https://v2.quasar.dev/quasar-cli-webpack/supporting-ts + supportTS: false, + + // https://v2.quasar.dev/quasar-cli-webpack/prefetch-feature + // preFetch: true, + + // app boot file (/src/boot) + // --> boot files are part of "main.js" + // https://v2.quasar.dev/quasar-cli-webpack/boot-files + boot: [ + 'i18n', + 'kdk', + 'tour' + ], + + // https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-css + css: [ + 'app.scss' + ], + + // https://github.com/quasarframework/quasar/tree/dev/extras + extras: [ + 'roboto-font', + 'material-icons', + 'line-awesome', + 'fontawesome-v5' + ], + + // https://quasar.dev/quasar-cli-webpack/quasar-config-js#property-htmlvariables + htmlVariables: { + appName: 'Akt\'n\'Map', + appSlug: 'aktnmap', + appDescription: 'Monitor real-time events on the field' + }, + + // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-build + build: { + vueRouterMode: 'hash', // available values: 'hash', 'history' + + // transpile: false, + // publicPath: '/', + + // Add dependencies for transpiling with Babel (Array of string/regex) + // (from node_modules, which are by default not transpiled). + // Applies only if "transpile" is set to true. + // transpileDependencies: [], + + // rtl: true, // https://quasar.dev/options/rtl-support + // preloadChunks: true, + // showProgress: false, + // gzip: true, + // analyze: true, + + // Options below are automatically set depending on the env, set them if you want to override + // extractCSS: false, + + // https://v2.quasar.dev/quasaextend r-cli-webpack/handling-webpack + // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain + + chainWebpack (chain) { + chain.plugin('eslint-webpack-plugin').use(ESLintPlugin, [{ extensions: [ 'js', 'vue' ] }]) + }, + + extendWebpack (cfg) { + cfg.resolve.fallback = { + fs: false, + 'dom-serializer': false, + assert: require.resolve('assert'), + http: require.resolve('stream-http'), + https: require.resolve('https-browserify'), + path: require.resolve('path-browserify'), + stream: require.resolve('stream-browserify'), + timers: require.resolve('timers-browserify'), + zlib: require.resolve('browserify-zlib') + }, + // Required for old dependencies, i.e. feathers.js + cfg.resolve.modules = [ + path.resolve(__dirname, 'node_modules') + ], + cfg.resolve.alias = { + ...cfg.resolve.alias, // This adds the existing aliases + '@components': [ + path.resolve(__dirname, 'src/components'), + path.resolve(__dirname, 'node_modules/@kalisio/kdk/core/client/components'), + path.resolve(__dirname, 'node_modules/@kalisio/kdk/map/client/components') + ], + '@schemas': [ + path.resolve(__dirname, 'src/schemas'), + path.resolve(__dirname, 'node_modules/@kalisio/kdk/core/common/schemas'), + path.resolve(__dirname, 'node_modules/@kalisio/kdk/map/common/schemas') + ], + '@i18n': [ + path.resolve(__dirname, 'src/i18n'), + path.resolve(__dirname, 'node_modules/@kalisio/kdk/core/client/i18n'), + path.resolve(__dirname, 'node_modules/@kalisio/kdk/map/client/i18n') + ], + config: path.resolve(__dirname, 'config/client-config.json') + }, + cfg.optimization.minimize = process.env.DEBUG ? false : cfg.optimization.minimize + } + }, + + // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-devServer + devServer: { + port: clientPort, + proxy: { + '/api': { + target: 'http://localhost:' + serverPort, + changeOrigin: true, + logLevel: 'debug' + }, + '/apiws': { + target: 'http://localhost:' + serverPort, + changeOrigin: true, + ws: true, + logLevel: 'debug' + }, + // The auth endpoints are not easy to prefix so we manage it manually + '/auth': { + target: 'http://localhost:' + serverPort, + changeOrigin: true, + logLevel: 'debug' + } + }, + open: true // opens browser window automatically + }, + + // https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-framework + framework: { + config: {}, + + // lang: 'en-US', // Quasar language pack + + components: [ + 'QAjaxBar', + 'QAvatar', + 'QBadge', + 'QBtn', + 'QCard', + 'QCardSection', + 'QCardActions', + 'QChip', + 'QDate', + 'QDialog', + 'QDrawer', + 'QExpansionItem', + 'QFab', + 'QFabAction', + 'QField', + 'QFooter', + 'QHeader', + 'QIcon', + 'QImg', + 'QInput', + 'QItem', + 'QItemSection', + 'QItemLabel', + 'QLayout', + 'QList', + 'QMarkupTable', + 'QMenu', + 'QPage', + 'QPageContainer', + 'QPageSticky', + 'QPagination', + 'QPopupProxy', + 'QResizeObserver', + 'QRouteTab', + 'QScrollArea', + 'QSelect', + 'QSeparator', + 'QSpace', + 'QTab', + 'QTabPanel', + 'QTabPanels', + 'QTabs', + 'QTime', + 'QTimeline', + 'QTimelineEntry', + 'QToolbar', + 'QToolbarTitle', + 'QToggle', + 'QTooltip' + ], + + directives: [ + 'ClosePopup', + 'Ripple', + 'TouchSwipe', + 'TouchPan' + ], + + // Quasar plugins + plugins: [ + 'Notify', + 'Dialog', + 'Platform', + 'Loading', + 'AppFullscreen' + ] + }, + + // animations: 'all', // --- includes all animations + // https://quasar.dev/options/animations + animations: [ + 'fadeIn', + 'fadeOut' + ], + + // https://v2.quasar.dev/quasar-cli-webpack/developing-ssr/configuring-ssr + ssr: { + pwa: false, + + // manualStoreHydration: true, + // manualPostHydrationTrigger: true, + + prodPort: 3000, // The default port that the production server should use + // (gets superseded if process.env.PORT is specified at runtime) + + maxAge: 1000 * 60 * 60 * 24 * 30, + // Tell browser when a file from the server should expire from cache (in ms) + + chainWebpackWebserver (chain) { + chain.plugin('eslint-webpack-plugin') + .use(ESLintPlugin, [{ extensions: [ 'js' ] }]) + }, + + middlewares: [ + ctx.prod ? 'compression' : '', + 'render' // keep this as last one + ] + }, + + // https://v2.quasar.dev/quasar-cli-webpack/developing-pwa/configuring-pwa + pwa: { + workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest' + workboxOptions: {}, // only for GenerateSW + + // for the custom service worker ONLY (/src-pwa/custom-service-worker.[js|ts]) + // if using workbox in InjectManifest mode + + chainWebpackCustomSW (chain) { + chain.plugin('eslint-webpack-plugin') + .use(ESLintPlugin, [{ extensions: [ 'js' ] }]) + }, + + manifest: { + name: 'Akt\'n\'Map', + short_name: 'aktnmap', + description: 'Monitor real-time events on the field', + display: 'standalone', + orientation: 'portrait', + icons: [ + { + src: 'icons/aktnmap-icon-32x32.png', + sizes: '32x32', + type: 'image/png' + }, + { + src: 'icons/aktnmap-icon-64x64.png', + sizes: '64x64', + type: 'image/png' + }, + { + src: 'icons/aktnmap-icon-128x128.png', + sizes: '128x128', + type: 'image/png' + }, + { + src: 'icons/aktnmap-icon-256x256.png', + sizes: '256x256', + type: 'image/png' + }, + { + src: 'icons/aktnmap-icon-512x512.png', + sizes: '512x512', + type: 'image/png' + } + ] + } + }, + + // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-cordova-apps/configuring-cordova + cordova: { + id: process.env.PACKAGE_ID + // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing + }, + + // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-capacitor-apps/configuring-capacitor + capacitor: { + hideSplashscreen: true + }, + + // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-electron-apps/configuring-electron + electron: { + bundler: 'packager', // 'packager' or 'builder' + + packager: { + // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options + + // OS X / Mac App Store + // appBundleId: '', + // appCategoryType: '', + // osxSign: '', + // protocol: 'myapp://path', + + // Windows only + // win32metadata: { ... } + }, + + builder: { + // https://www.electron.build/configuration/configuration + + appId: 'kano' + }, + + // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain + + chainWebpackMain (chain) { + chain.plugin('eslint-webpack-plugin') + .use(ESLintPlugin, [{ extensions: [ 'js' ] }]) + }, + + chainWebpackPreload (chain) { + chain.plugin('eslint-webpack-plugin') + .use(ESLintPlugin, [{ extensions: [ 'js' ] }]) + }, + + } + } +}) diff --git a/src/boot/api.js b/src/boot/api.js deleted file mode 100644 index 42c2d184..00000000 --- a/src/boot/api.js +++ /dev/null @@ -1,29 +0,0 @@ -import 'whatwg-fetch' -import config from 'config' -import appHooks from '../app.hooks' -import services from '../services' -import plugin from '../vue-kdk' -import { kalisio, beforeGuard, authenticationGuard } from '@kalisio/kdk/core.client' - -export default async ({ app, router, Vue }) => { - const api = kalisio() - - // Setup app hooks - api.hooks(appHooks) - // Then all services - services.call(api) - - Vue.use(plugin, { api }) - - // Add global guard - beforeGuard.registerGuard(authenticationGuard) - - api.on('authenticated', (data) => { - // Store API gateway token if any - if (data.gatewayToken) api.get('storage').setItem(config.gatewayJwt, data.gatewayToken) - }) - api.on('logout', (data) => { - // Remove API gateway token if any - api.get('storage').removeItem(config.gatewayJwt) - }) -} diff --git a/src/boot/i18n.js b/src/boot/i18n.js index 8b947ce5..033b104f 100644 --- a/src/boot/i18n.js +++ b/src/boot/i18n.js @@ -1,45 +1,5 @@ -import logger from 'loglevel' -import i18next from 'i18next' -import VueI18next from '@panter/vue-i18next' -import { Quasar } from 'quasar' -import { utils as kCoreUtils } from '@kalisio/kdk/core.client' -import * as utils from '../utils' -import config from 'config' +import { i18n } from '@kalisio/kdk/core.client' -export default async ({ app, Vue }) => { - // Define the locale to be used - const localeConfig = config.locale || {} - const localeBrowser = kCoreUtils.getLocale() - let locale = localeConfig.default || localeBrowser - // Initializes i18next - i18next.init({ - lng: locale, - fallbackLng: localeConfig.fallback || 'en', - defaultNS: ['kdk'] - }) - // Set Quasar language pack - try { - const lang = await import('quasar/lang/' + locale) - Quasar.lang.set(lang.default) - } - catch (error) { - logger.error(error.message) - } - // Load the translation files - const modules = ['core', 'map', 'aktnmap'] - try { - // Build the translation resolvers - const translationResolvers = modules.map(module => { - return utils.loadTranslation(module, locale) - }) - // Apply the resolvers and add the translation bundles to i18next - let translations = await Promise.all(translationResolvers) - translations.forEach((translation) => { - i18next.addResourceBundle(locale, 'kdk', translation, true, true) - }) - } catch (error) { - logger.error(error.message) - } - Vue.use(VueI18next) - app.i18n = new VueI18next(i18next) +export default async ({ app }) => { + await i18n.initialize(app, ['core', 'map', 'app']) } diff --git a/src/boot/kdk.js b/src/boot/kdk.js new file mode 100644 index 00000000..00961a45 --- /dev/null +++ b/src/boot/kdk.js @@ -0,0 +1,86 @@ +import _ from 'lodash' +import config from 'config' +import utils from '../utils' +import appHooks from '../app.hooks' +import services from '../services' +import { api, utils as kdkCoreUtils, Store, Layout, Events, Theme, beforeGuard, authenticationGuard } from '@kalisio/kdk/core.client' +import { Geolocation } from '@kalisio/kdk/map.client.map' + +function updateThemeColors () { + const theme = config.theme + // Default theme override + if (theme) Theme.apply(theme) +} + +export default async ({ app }) => { + // Required to make injections reactively linked to the provider + // https://vuejs.org/guide/components/provide-inject.html#working-with-reactivity + app.config.unwrapInjectedRef = true + + // Setup app hooks + api.hooks(appHooks) + // Then all services + services.call(api) + + // Register global properties to the the vue app + app.config.globalProperties.$store = Store + app.config.globalProperties.$layout = Layout + app.config.globalProperties.$events = Events + app.config.globalProperties.$api = api + app.config.globalProperties.$can = api.can + app.config.globalProperties.$toast = kdkCoreUtils.toast + app.config.globalProperties.$tie = function (key, param) { + if (_.isEmpty(key)) return key + return this.$te(key) ? this.$t(key, param) : key + } + app.config.globalProperties.$geolocation = Geolocation + app.config.globalProperties.$config = function (path, defaultValue) { + return _.get(config, path, defaultValue) + } + app.config.globalProperties.$checkBillingOption = async function (option) { + if (this.$config('flavor') === 'dev') return + const perspective = await this.$api.getService('organisations') + .get(this.contextId, { query: { $select: ['name', 'billing'] } }) + const options = _.get(perspective, 'billing.options', []) + if (!_.find(options, { plan: option })) { + Dialog.create({ + title: this.$t('OOPS'), + message: this.$t('errors.UNSUBSCRIBED_OPTION'), + persistent: true + }).onOk(() => { + this.$router.push({ + name: 'edit-organisation-billing', + params: { objectId: this.contextId, title: perspective.name } + }) + }) + } + } + + // Register global components + app.component('KAction', await kdkCoreUtils.loadComponent('frame/KAction')) + app.component('KPanel', await kdkCoreUtils.loadComponent('frame/KPanel')) + app.component('KStamp', await kdkCoreUtils.loadComponent('frame/KStamp')) + app.component('KModal', await kdkCoreUtils.loadComponent('frame/KModal')) + app.component('KForm', await kdkCoreUtils.loadComponent('form/KForm')) + app.component('KPage', await kdkCoreUtils.loadComponent('layout/KPage')) + + // Register global properties + // FIXME: This is used for testing purpose, don't know how to access this from Puppeteer otherwise + global.$store = app.config.globalProperties.$store + global.$layout = app.config.globalProperties.$layout + global.$api = app.config.globalProperties.$api + + // Add global guard + beforeGuard.registerGuard(authenticationGuard) + + updateThemeColors() + + api.on('authenticated', (data) => { + // Store API gateway token if any + if (data.gatewayToken) api.get('storage').setItem(config.gatewayJwt, data.gatewayToken) + }) + api.on('logout', (data) => { + // Remove API gateway token if any + api.get('storage').removeItem(config.gatewayJwt) + }) +} diff --git a/src/boot/tour.js b/src/boot/tour.js index 7a071f52..86e7d073 100644 --- a/src/boot/tour.js +++ b/src/boot/tour.js @@ -1,6 +1,6 @@ -import VueTour from 'vue-tour' -import 'vue-tour/dist/vue-tour.css' +import Vue3Tour from 'vue3-tour/src/lib.js' +// TODO import 'vue3-tour/dist/vue3-tour.css' -export default async ({ app, Vue }) => { - Vue.use(VueTour) +export default async ({ app }) => { + app.use(Vue3Tour) } diff --git a/src/components/ArchivedEventCard.vue b/src/components/ArchivedEventCard.vue index fef57ec4..94a7aaee 100644 --- a/src/components/ArchivedEventCard.vue +++ b/src/components/ArchivedEventCard.vue @@ -267,7 +267,7 @@ export default { if (this.$store.get('user')) this.refresh() this.$events.$on('user-changed', this.refresh) }, - beforeDestroy () { + beforeUnmount () { this.$events.$off('user-changed', this.refresh) } } diff --git a/src/components/ArchivedEventsActivity.vue b/src/components/ArchivedEventsActivity.vue index 241c506d..d277f385 100644 --- a/src/components/ArchivedEventsActivity.vue +++ b/src/components/ArchivedEventsActivity.vue @@ -611,7 +611,7 @@ export default { this.$on('collection-refreshed', this.onCollectionRefreshed) this.$events.$on('time-range-changed', this.onTimeRangeChanged) }, - beforeDestroy () { + beforeUnmount () { // Release the chart if nay if (this.chart) this.chart.destroy() // Restore the current time diff --git a/src/components/ArchivedPlansActivity.vue b/src/components/ArchivedPlansActivity.vue index 4736f138..79c579b8 100644 --- a/src/components/ArchivedPlansActivity.vue +++ b/src/components/ArchivedPlansActivity.vue @@ -105,7 +105,7 @@ export default { // Check if option has been subscribed this.$checkBillingOption('archiving') }, - beforeDestroy () { + beforeUnmount () { // Restore the current time Time.setCurrentTime(this.currentTime) // Releases listeners diff --git a/src/components/BillingEditor.vue b/src/components/BillingEditor.vue index b688a02d..aeb8d6d9 100644 --- a/src/components/BillingEditor.vue +++ b/src/components/BillingEditor.vue @@ -160,7 +160,7 @@ export default { this.currentOptions = _.get(perspective, 'billing.options', []).map(option => option.plan) this.customer = _.get(perspective, 'billing.customer') }, - beforeDestroy () { + beforeUnmount () { this.$events.$off('capabilities-api-changed', this.refreshPlans) } } diff --git a/src/components/CatalogActivity.vue b/src/components/CatalogActivity.vue index 85a2840f..63d949ea 100644 --- a/src/components/CatalogActivity.vue +++ b/src/components/CatalogActivity.vue @@ -594,7 +594,7 @@ export default { this.$on('edit-start', this.onEditStartEvent) this.$on('edit-stop', this.onEditStopEvent) }, - beforeDestroy () { + beforeUnmount () { this.alerts.$off('collection-refreshed', this.onAlertCollectionRefreshed) this.events.$off('collection-refreshed', this.onEventCollectionRefreshed) this.$off('edit-start', this.onEditStartEvent) diff --git a/src/components/Context.vue b/src/components/Context.vue index 9822a485..e35efef0 100644 --- a/src/components/Context.vue +++ b/src/components/Context.vue @@ -30,7 +30,7 @@ export default { this.$events.$on('context-changed', this.setupContext) this.service.on('patched', this.watchOrganisation) }, - beforeDestroy () { + beforeUnmount () { this.$events.$off('context-changed', this.setupContext) this.service.off('patched', this.watchOrganisation) Theme.restore() diff --git a/src/components/EventActivity.vue b/src/components/EventActivity.vue index b5a2b0a3..de27944d 100644 --- a/src/components/EventActivity.vue +++ b/src/components/EventActivity.vue @@ -327,7 +327,7 @@ export default { this.$events.$on('zoom-to-participant', this.onZoomToParticipant) this.$events.$on('filter-participant-states', this.onFilterParticipantStates) }, - beforeDestroy () { + beforeUnmount () { // Remove event connections // this.$off('popupopen', this.onPopupOpen) this.$off('click', this.onFeatureClicked) diff --git a/src/components/EventCard.vue b/src/components/EventCard.vue index 834fc63c..b8ce6c56 100644 --- a/src/components/EventCard.vue +++ b/src/components/EventCard.vue @@ -702,7 +702,7 @@ export default { if (this.$store.get('user')) this.refresh() this.$events.$on('user-changed', this.refresh) }, - beforeDestroy () { + beforeUnmount () { this.$events.$off('user-changed', this.refresh) this.unsubscribeParticipantLog() this.unsubscribeCoordinatorLog() diff --git a/src/components/EventEditor.vue b/src/components/EventEditor.vue index 16a28376..2367423a 100644 --- a/src/components/EventEditor.vue +++ b/src/components/EventEditor.vue @@ -227,7 +227,7 @@ export default { if (this.getMode() === 'create') this.notify = true else this.notify = false }, - beforeDestroy () { + beforeUnmount () { this.$off('applied', this.closeModal) } } diff --git a/src/components/EventTemplateEditor.vue b/src/components/EventTemplateEditor.vue index 6c107c59..ceb8e51c 100644 --- a/src/components/EventTemplateEditor.vue +++ b/src/components/EventTemplateEditor.vue @@ -86,7 +86,7 @@ export default { this.$options.components['k-form'] = this.$load('form/KForm') this.$on('applied', this.closeModal) }, - beforeDestroy () { + beforeUnmount () { this.$off('applied', this.closeModal) } } diff --git a/src/components/EventTemplateWorkflowEditor.vue b/src/components/EventTemplateWorkflowEditor.vue index 448dd808..e29ff51a 100644 --- a/src/components/EventTemplateWorkflowEditor.vue +++ b/src/components/EventTemplateWorkflowEditor.vue @@ -47,7 +47,7 @@ export default { this.$options.components['event-template-workflow-form'] = this.$load('EventTemplateWorkflowForm') this.$on('applied', this.closeModal) }, - beforeDestroy () { + beforeUnmount () { this.$off('applied', this.closeModal) } } diff --git a/src/components/EventsActivity.vue b/src/components/EventsActivity.vue index c06ad68e..f15f7da3 100644 --- a/src/components/EventsActivity.vue +++ b/src/components/EventsActivity.vue @@ -215,7 +215,7 @@ export default { eventsService.on('removed', this.onEventRemoved) } }, - beforeDestroy () { + beforeUnmount () { this.$events.$off('user-changed', this.refreshFab) if (this.planId) { const eventsService = this.$api.getService('events', this.contextId) diff --git a/src/components/Index.vue b/src/components/Index.vue index 1f4e4aca..2430ca8d 100644 --- a/src/components/Index.vue +++ b/src/components/Index.vue @@ -167,7 +167,7 @@ export default { this.redirect(null) } }, - beforeDestroy () { + beforeUnmount () { this.$events.$off('user-changed', this.redirect) } } diff --git a/src/components/OrganisationCard.vue b/src/components/OrganisationCard.vue index 6ef86e2e..8afd147d 100644 --- a/src/components/OrganisationCard.vue +++ b/src/components/OrganisationCard.vue @@ -252,7 +252,7 @@ export default { plansService.on('updated', this.updateCounts) plansService.on('removed', this.updateCounts) }, - beforeDestroy () { + beforeUnmount () { const eventsService = this.$api.getService('events', this.item._id) eventsService.off('created', this.updateCounts) eventsService.off('patched', this.updateCounts) diff --git a/src/components/OrganisationMenu.vue b/src/components/OrganisationMenu.vue index af54e407..25f11461 100644 --- a/src/components/OrganisationMenu.vue +++ b/src/components/OrganisationMenu.vue @@ -194,7 +194,7 @@ export default { plansService.on('updated', this.updateCounts) plansService.on('removed', this.updateCounts) }, - beforeDestroy () { + beforeUnmount () { const eventsService = this.$api.getService('events', this.organisation._id) eventsService.off('created', this.updateCounts) eventsService.off('patched', this.updateCounts) diff --git a/src/components/PlanEditor.vue b/src/components/PlanEditor.vue index a39a4bd9..7d8075c8 100644 --- a/src/components/PlanEditor.vue +++ b/src/components/PlanEditor.vue @@ -65,7 +65,7 @@ export default { this.refresh() this.$on('applied', this.closeModal) }, - beforeDestroy () { + beforeUnmount () { this.$off('applied', this.closeModal) } } diff --git a/src/components/PlansActivity.vue b/src/components/PlansActivity.vue index e3b5956f..b3b1c223 100644 --- a/src/components/PlansActivity.vue +++ b/src/components/PlansActivity.vue @@ -143,7 +143,7 @@ export default { // Check if option has been subscribed this.$checkBillingOption('archiving') }, - beforeDestroy () { + beforeUnmount () { this.$events.$off('user-changed', this.configureActivity) const eventsService = this.$api.getService('events', this.contextId) eventsService.off('created', this.updateFilterQuery) diff --git a/src/css/quasar.variables.scss b/src/css/quasar.variables.scss index 784b5668..f37b7c1f 100644 --- a/src/css/quasar.variables.scss +++ b/src/css/quasar.variables.scss @@ -9,6 +9,7 @@ // to match your app's branding. // Tip: Use the "Theme Builder" on Quasar's documentation website. +// Theme $primary: #1b5e20; $secondary: lighten($primary, 50%); $accent: lighten($primary, 25%); @@ -17,3 +18,7 @@ $info: $accent; $positive: #7bb946; $negative: #c74a4a; $warning: #d09931; + +// Layout +$sticky-z-index: 1000; +$left-pane-width: 300px; \ No newline at end of file diff --git a/src/index.template.html b/src/index.template.html index b21969b6..2e0d2e23 100644 --- a/src/index.template.html +++ b/src/index.template.html @@ -1,18 +1,24 @@
+