forked from verdnatura/hedera-web
refs #4922 Login, logout, home, layout style
This commit is contained in:
parent
042b8b0309
commit
0d0be4ee5f
|
@ -2060,6 +2060,7 @@
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-2.2.2.tgz",
|
||||||
"integrity": "sha512-vngkvlIVV8ZJoyC5VqMvqJd2nvsx+qMN7pQjPiPjOrVndeiR7Dlue0k86Q8FsFUzyksW3HJZZi833ldxwbFzTA==",
|
"integrity": "sha512-vngkvlIVV8ZJoyC5VqMvqJd2nvsx+qMN7pQjPiPjOrVndeiR7Dlue0k86Q8FsFUzyksW3HJZZi833ldxwbFzTA==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/message-compiler": "^9.1.0",
|
"@intlify/message-compiler": "^9.1.0",
|
||||||
"@intlify/shared": "^9.1.0",
|
"@intlify/shared": "^9.1.0",
|
||||||
|
@ -2140,6 +2141,7 @@
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/vue-i18n-loader/-/vue-i18n-loader-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/vue-i18n-loader/-/vue-i18n-loader-4.2.0.tgz",
|
||||||
"integrity": "sha512-d7aBmMNWJskcZPT5rJH4h2XHe/PwNoJUaY0PGla9g+NSD4B0UR8LBKrp126nlaUfA74Xt0FEGvzCfG9KdC9KoA==",
|
"integrity": "sha512-d7aBmMNWJskcZPT5rJH4h2XHe/PwNoJUaY0PGla9g+NSD4B0UR8LBKrp126nlaUfA74Xt0FEGvzCfG9KdC9KoA==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/bundle-utils": "^2.2.2",
|
"@intlify/bundle-utils": "^2.2.2",
|
||||||
"@intlify/shared": "^9.1.0",
|
"@intlify/shared": "^9.1.0",
|
||||||
|
@ -2167,12 +2169,14 @@
|
||||||
"node_modules/@intlify/vue-i18n-loader/node_modules/argparse": {
|
"node_modules/@intlify/vue-i18n-loader/node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@intlify/vue-i18n-loader/node_modules/js-yaml": {
|
"node_modules/@intlify/vue-i18n-loader/node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": "^2.0.1"
|
"argparse": "^2.0.1"
|
||||||
},
|
},
|
||||||
|
@ -2184,6 +2188,7 @@
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
||||||
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
||||||
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"json5": "lib/cli.js"
|
"json5": "lib/cli.js"
|
||||||
},
|
},
|
||||||
|
@ -2195,6 +2200,7 @@
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
|
||||||
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
|
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"big.js": "^5.2.2",
|
"big.js": "^5.2.2",
|
||||||
"emojis-list": "^3.0.0",
|
"emojis-list": "^3.0.0",
|
||||||
|
@ -3693,6 +3699,7 @@
|
||||||
"version": "8.8.1",
|
"version": "8.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
|
||||||
"integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
|
"integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
|
||||||
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
|
@ -3713,6 +3720,7 @@
|
||||||
"version": "5.3.2",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
||||||
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
||||||
|
"dev": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||||
}
|
}
|
||||||
|
@ -4415,6 +4423,7 @@
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||||
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
|
@ -6084,6 +6093,7 @@
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||||
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
|
@ -9041,6 +9051,7 @@
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-1.4.1.tgz",
|
||||||
"integrity": "sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==",
|
"integrity": "sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^7.4.1",
|
"acorn": "^7.4.1",
|
||||||
"eslint-utils": "^2.1.0",
|
"eslint-utils": "^2.1.0",
|
||||||
|
@ -9056,6 +9067,7 @@
|
||||||
"version": "7.4.1",
|
"version": "7.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
|
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
|
||||||
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
|
@ -9067,6 +9079,7 @@
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
|
||||||
"integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
|
"integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eslint-visitor-keys": "^1.1.0"
|
"eslint-visitor-keys": "^1.1.0"
|
||||||
},
|
},
|
||||||
|
@ -9081,6 +9094,7 @@
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
|
||||||
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
|
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
|
@ -9089,6 +9103,7 @@
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
|
||||||
"integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
|
"integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^7.1.1",
|
"acorn": "^7.1.1",
|
||||||
"acorn-jsx": "^5.2.0",
|
"acorn-jsx": "^5.2.0",
|
||||||
|
@ -9102,6 +9117,7 @@
|
||||||
"version": "6.3.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
}
|
}
|
||||||
|
@ -9251,7 +9267,8 @@
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.debounce": {
|
"node_modules/lodash.debounce": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
|
@ -14538,6 +14555,7 @@
|
||||||
"version": "1.10.2",
|
"version": "1.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
|
@ -14546,6 +14564,7 @@
|
||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-0.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-0.3.2.tgz",
|
||||||
"integrity": "sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==",
|
"integrity": "sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eslint-visitor-keys": "^1.3.0",
|
"eslint-visitor-keys": "^1.3.0",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
|
@ -14556,6 +14575,7 @@
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
|
||||||
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
|
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
|
@ -16055,6 +16075,7 @@
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-2.2.2.tgz",
|
||||||
"integrity": "sha512-vngkvlIVV8ZJoyC5VqMvqJd2nvsx+qMN7pQjPiPjOrVndeiR7Dlue0k86Q8FsFUzyksW3HJZZi833ldxwbFzTA==",
|
"integrity": "sha512-vngkvlIVV8ZJoyC5VqMvqJd2nvsx+qMN7pQjPiPjOrVndeiR7Dlue0k86Q8FsFUzyksW3HJZZi833ldxwbFzTA==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@intlify/message-compiler": "^9.1.0",
|
"@intlify/message-compiler": "^9.1.0",
|
||||||
"@intlify/shared": "^9.1.0",
|
"@intlify/shared": "^9.1.0",
|
||||||
|
@ -16109,6 +16130,7 @@
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/vue-i18n-loader/-/vue-i18n-loader-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/vue-i18n-loader/-/vue-i18n-loader-4.2.0.tgz",
|
||||||
"integrity": "sha512-d7aBmMNWJskcZPT5rJH4h2XHe/PwNoJUaY0PGla9g+NSD4B0UR8LBKrp126nlaUfA74Xt0FEGvzCfG9KdC9KoA==",
|
"integrity": "sha512-d7aBmMNWJskcZPT5rJH4h2XHe/PwNoJUaY0PGla9g+NSD4B0UR8LBKrp126nlaUfA74Xt0FEGvzCfG9KdC9KoA==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@intlify/bundle-utils": "^2.2.2",
|
"@intlify/bundle-utils": "^2.2.2",
|
||||||
"@intlify/shared": "^9.1.0",
|
"@intlify/shared": "^9.1.0",
|
||||||
|
@ -16120,12 +16142,14 @@
|
||||||
"argparse": {
|
"argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"js-yaml": {
|
"js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"argparse": "^2.0.1"
|
"argparse": "^2.0.1"
|
||||||
}
|
}
|
||||||
|
@ -16133,12 +16157,14 @@
|
||||||
"json5": {
|
"json5": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
||||||
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA=="
|
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"loader-utils": {
|
"loader-utils": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
|
||||||
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
|
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"big.js": "^5.2.2",
|
"big.js": "^5.2.2",
|
||||||
"emojis-list": "^3.0.0",
|
"emojis-list": "^3.0.0",
|
||||||
|
@ -17350,7 +17376,8 @@
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "8.8.1",
|
"version": "8.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
|
||||||
"integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA=="
|
"integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"acorn-import-assertions": {
|
"acorn-import-assertions": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
|
@ -17363,6 +17390,7 @@
|
||||||
"version": "5.3.2",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
||||||
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"acorn-walk": {
|
"acorn-walk": {
|
||||||
|
@ -17882,7 +17910,8 @@
|
||||||
"big.js": {
|
"big.js": {
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||||
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
|
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"binary-extensions": {
|
"binary-extensions": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
|
@ -19114,7 +19143,8 @@
|
||||||
"emojis-list": {
|
"emojis-list": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||||
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
|
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"encodeurl": {
|
"encodeurl": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
@ -21303,6 +21333,7 @@
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-1.4.1.tgz",
|
||||||
"integrity": "sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==",
|
"integrity": "sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"acorn": "^7.4.1",
|
"acorn": "^7.4.1",
|
||||||
"eslint-utils": "^2.1.0",
|
"eslint-utils": "^2.1.0",
|
||||||
|
@ -21314,12 +21345,14 @@
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "7.4.1",
|
"version": "7.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
|
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"eslint-utils": {
|
"eslint-utils": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
|
||||||
"integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
|
"integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"eslint-visitor-keys": "^1.1.0"
|
"eslint-visitor-keys": "^1.1.0"
|
||||||
}
|
}
|
||||||
|
@ -21327,12 +21360,14 @@
|
||||||
"eslint-visitor-keys": {
|
"eslint-visitor-keys": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
|
||||||
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="
|
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"espree": {
|
"espree": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
|
||||||
"integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
|
"integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"acorn": "^7.1.1",
|
"acorn": "^7.1.1",
|
||||||
"acorn-jsx": "^5.2.0",
|
"acorn-jsx": "^5.2.0",
|
||||||
|
@ -21342,7 +21377,8 @@
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -21459,7 +21495,8 @@
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"lodash.debounce": {
|
"lodash.debounce": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
|
@ -25311,12 +25348,14 @@
|
||||||
"yaml": {
|
"yaml": {
|
||||||
"version": "1.10.2",
|
"version": "1.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
|
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"yaml-eslint-parser": {
|
"yaml-eslint-parser": {
|
||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-0.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-0.3.2.tgz",
|
||||||
"integrity": "sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==",
|
"integrity": "sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"eslint-visitor-keys": "^1.3.0",
|
"eslint-visitor-keys": "^1.3.0",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
|
@ -25326,7 +25365,8 @@
|
||||||
"eslint-visitor-keys": {
|
"eslint-visitor-keys": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
|
||||||
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="
|
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
|
||||||
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"front": "webpack serve --open",
|
"front": "webpack serve --open",
|
||||||
"back": "cd ../salix && gulp backOnly",
|
"back": "cd ../salix && gulp backOnly",
|
||||||
"db": "cd ../vn-database && myvc run",
|
"db": "cd ../vn-database && myvc run -d",
|
||||||
"build": "rm -rf build/ ; webpack",
|
"build": "rm -rf build/ ; webpack",
|
||||||
"clean": "rm -rf build/",
|
"clean": "rm -rf build/",
|
||||||
"lint": "eslint --ext .js,.vue ./"
|
"lint": "eslint --ext .js,.vue ./"
|
||||||
|
|
|
@ -100,7 +100,18 @@ module.exports = configure(function (ctx) {
|
||||||
type: 'http'
|
type: 'http'
|
||||||
},
|
},
|
||||||
port: 8080,
|
port: 8080,
|
||||||
open: true // opens browser window automatically
|
open: true, // opens browser window automatically
|
||||||
|
|
||||||
|
// static: __dirname,
|
||||||
|
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||||
|
// stats: { chunks: false },
|
||||||
|
proxy: {
|
||||||
|
'/api': 'http://localhost:3000',
|
||||||
|
'/': {
|
||||||
|
target: 'http://localhost/projects/hedera-web',
|
||||||
|
bypass: (req) => req.path !== '/' ? req.path : null
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-framework
|
// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-framework
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { boot } from 'quasar/wrappers'
|
import { boot } from 'quasar/wrappers'
|
||||||
|
import { Connection } from '../js/db/connection'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
// Be careful when using SSR for cross-request state pollution
|
// Be careful when using SSR for cross-request state pollution
|
||||||
|
@ -7,7 +8,18 @@ import axios from 'axios'
|
||||||
// good idea to move this instance creation inside of the
|
// good idea to move this instance creation inside of the
|
||||||
// "export default () => {}" function below (which runs individually
|
// "export default () => {}" function below (which runs individually
|
||||||
// for each client)
|
// for each client)
|
||||||
const api = axios.create({ baseURL: 'https://api.example.com' })
|
const api = axios.create({
|
||||||
|
baseURL: `//${location.hostname}:${location.port}/api/`
|
||||||
|
})
|
||||||
|
api.interceptors.request.use(function addToken (config) {
|
||||||
|
const token = localStorage.getItem('vnToken')
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = token
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
})
|
||||||
|
|
||||||
|
const jApi = new Connection()
|
||||||
|
|
||||||
export default boot(({ app }) => {
|
export default boot(({ app }) => {
|
||||||
// for use inside Vue files (Options API) through this.$axios and this.$api
|
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||||
|
@ -19,6 +31,8 @@ export default boot(({ app }) => {
|
||||||
app.config.globalProperties.$api = api
|
app.config.globalProperties.$api = api
|
||||||
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
|
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
|
||||||
// so you can easily perform requests against your app's API
|
// so you can easily perform requests against your app's API
|
||||||
|
|
||||||
|
app.config.globalProperties.$jApi = jApi
|
||||||
})
|
})
|
||||||
|
|
||||||
export { api }
|
export { api, jApi }
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-item
|
|
||||||
clickable
|
|
||||||
tag="a"
|
|
||||||
target="_blank"
|
|
||||||
:href="link"
|
|
||||||
>
|
|
||||||
<q-item-section
|
|
||||||
v-if="icon"
|
|
||||||
avatar
|
|
||||||
>
|
|
||||||
<q-icon :name="icon" />
|
|
||||||
</q-item-section>
|
|
||||||
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>{{ title }}</q-item-label>
|
|
||||||
<q-item-label caption>{{ caption }}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'EssentialLink',
|
|
||||||
props: {
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
|
|
||||||
caption: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
|
|
||||||
link: {
|
|
||||||
type: String,
|
|
||||||
default: '#'
|
|
||||||
},
|
|
||||||
|
|
||||||
icon: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -2,9 +2,18 @@
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Poppins;
|
font-family: Poppins;
|
||||||
src: url(./poppins.ttf);
|
src: url(./poppins.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
src: url(./opensans.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Poppins', 'Verdana', 'Sans';
|
font-family: 'Poppins', 'Verdana', 'Sans';
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
.q-card {
|
||||||
|
border-radius: 7px;
|
||||||
|
box-shadow: 0 0 3px rgba(0, 0, 0, .1);
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
$primary : #1A1A1A;
|
$primary : #1A1A1A;
|
||||||
$secondary : #26A69A;
|
$secondary : #26A69A;
|
||||||
$accent : #8ed300;
|
$accent : #8cc63f;
|
||||||
|
|
||||||
$dark : #1D1D1D;
|
$dark : #1D1D1D;
|
||||||
$dark-page : #121212;
|
$dark-page : #121212;
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
import { JsonConnection } from '../vn/json-connection'
|
||||||
|
import { ResultSet } from './result-set'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates a connection to a database by making asynchronous requests to a
|
||||||
|
* remote REST service that returns the results in JSON format.
|
||||||
|
* Using this class can perform any operation that can be done with a database,
|
||||||
|
* like open/close a connection or selecion/updating queries.
|
||||||
|
*
|
||||||
|
* Warning! You should set a well defined dababase level privileges to use this
|
||||||
|
* class or you could have a serious security hole in you application becasuse
|
||||||
|
* the user can send any statement to the server. For example: DROP DATABASE
|
||||||
|
*/
|
||||||
|
const Flag = {
|
||||||
|
NOT_NULL: 1,
|
||||||
|
PRI_KEY: 2,
|
||||||
|
AI: 512 | 2 | 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const Type = {
|
||||||
|
BOOLEAN: 1,
|
||||||
|
INTEGER: 3,
|
||||||
|
DOUBLE: 4,
|
||||||
|
STRING: 5,
|
||||||
|
DATE: 8,
|
||||||
|
DATE_TIME: 9
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Connection extends JsonConnection {
|
||||||
|
static Flag = Flag
|
||||||
|
static Type = Type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a SQL query on the database.
|
||||||
|
*
|
||||||
|
* @param {String} sql The SQL statement
|
||||||
|
* @return {ResultSet} The result
|
||||||
|
*/
|
||||||
|
async execSql (sql) {
|
||||||
|
const json = await this.send('core/query', { sql })
|
||||||
|
const results = []
|
||||||
|
let err
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
try {
|
||||||
|
if (json && json instanceof Array) {
|
||||||
|
for (let i = 0; i < json.length; i++) {
|
||||||
|
if (json[i] !== true) {
|
||||||
|
const rows = json[i].data
|
||||||
|
const columns = json[i].columns
|
||||||
|
|
||||||
|
const data = new Array(rows.length)
|
||||||
|
results.push({
|
||||||
|
data,
|
||||||
|
columns,
|
||||||
|
tables: json[i].tables
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let j = 0; j < rows.length; j++) {
|
||||||
|
const row = data[j] = {}
|
||||||
|
for (let k = 0; k < columns.length; k++) { row[columns[k].name] = rows[j][k] }
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j < columns.length; j++) {
|
||||||
|
let castFunc = null
|
||||||
|
const col = columns[j]
|
||||||
|
|
||||||
|
switch (col.type) {
|
||||||
|
case Type.DATE:
|
||||||
|
case Type.DATE_TIME:
|
||||||
|
case Type.TIMESTAMP:
|
||||||
|
castFunc = this.valueToDate
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (castFunc !== null) {
|
||||||
|
if (col.def != null) { col.def = castFunc(col.def) }
|
||||||
|
|
||||||
|
for (let k = 0; k < data.length; k++) {
|
||||||
|
if (data[k][col.name] != null) { data[k][col.name] = castFunc(data[k][col.name]) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { results.push(json[i]) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
err = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResultSet(results, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a query on the database.
|
||||||
|
*
|
||||||
|
* @param {String} query The SQL statement
|
||||||
|
* @param {Object} params The query params
|
||||||
|
* @return {ResultSet} The result
|
||||||
|
*/
|
||||||
|
async execQuery (query, params) {
|
||||||
|
const sql = query.replace(/#\w+/g, function (key) {
|
||||||
|
const value = params[key.substring(1)]
|
||||||
|
return value ? this.renderValue(params) : key
|
||||||
|
})
|
||||||
|
|
||||||
|
return await this.execSql(sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
async query (query, params) {
|
||||||
|
const res = await this.execQuery(query, params)
|
||||||
|
return res.fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
async getObject (query, params) {
|
||||||
|
const res = await this.execQuery(query, params)
|
||||||
|
return res.fetchObject()
|
||||||
|
}
|
||||||
|
|
||||||
|
async getValue (query, params) {
|
||||||
|
const res = await this.execQuery(query, params)
|
||||||
|
return res.fetchValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
renderValue (v) {
|
||||||
|
switch (typeof v) {
|
||||||
|
case 'number':
|
||||||
|
return v
|
||||||
|
case 'boolean':
|
||||||
|
return (v) ? 'TRUE' : 'FALSE'
|
||||||
|
case 'string':
|
||||||
|
return "'" + v.replace(this.regexp, this.replaceFunc) + "'"
|
||||||
|
default:
|
||||||
|
if (v instanceof Date) {
|
||||||
|
if (!isNaN(v.getTime())) {
|
||||||
|
const unixTime = parseInt(fixTz(v).getTime() / 1000)
|
||||||
|
return 'DATE(FROM_UNIXTIME(' + unixTime + '))'
|
||||||
|
} else { return '0000-00-00' }
|
||||||
|
} else { return 'NULL' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parses a value to date.
|
||||||
|
*/
|
||||||
|
valueToDate (value) {
|
||||||
|
return fixTz(new Date(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Read time zone from db configuration
|
||||||
|
const tz = { timeZone: 'Europe/Madrid' }
|
||||||
|
const isLocal = Intl
|
||||||
|
.DateTimeFormat()
|
||||||
|
.resolvedOptions()
|
||||||
|
.timeZone === tz.timeZone
|
||||||
|
|
||||||
|
function fixTz (date) {
|
||||||
|
if (isLocal) return date
|
||||||
|
|
||||||
|
const localDate = new Date(date.toLocaleString('en-US', tz))
|
||||||
|
const hasTime = localDate.getHours() ||
|
||||||
|
localDate.getMinutes() ||
|
||||||
|
localDate.getSeconds() ||
|
||||||
|
localDate.getMilliseconds()
|
||||||
|
|
||||||
|
if (!hasTime) {
|
||||||
|
date.setHours(date.getHours() + 12)
|
||||||
|
date.setHours(0, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return date
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
|
||||||
|
import { Result } from './result'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class stores the database results.
|
||||||
|
*/
|
||||||
|
export class ResultSet {
|
||||||
|
results = null
|
||||||
|
error = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initilizes the resultset object.
|
||||||
|
*/
|
||||||
|
constructor (results, error) {
|
||||||
|
this.results = results
|
||||||
|
this.error = error
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the query error.
|
||||||
|
*
|
||||||
|
* @return {Db.Err} the error or null if no errors hapened
|
||||||
|
*/
|
||||||
|
getError () {
|
||||||
|
return this.error
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch () {
|
||||||
|
if (this.error) { throw this.error }
|
||||||
|
|
||||||
|
if (this.results !== null &&
|
||||||
|
this.results.length > 0) { return this.results.shift() }
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetchs the next result from the resultset.
|
||||||
|
*
|
||||||
|
* @return {Db.Result} the result or %null if error or there are no more results
|
||||||
|
*/
|
||||||
|
fetchResult () {
|
||||||
|
const result = this.fetch()
|
||||||
|
|
||||||
|
if (result !== null) {
|
||||||
|
if (result.data instanceof Array) { return new Result(result) } else { return true }
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetchs the first row object from the next resultset.
|
||||||
|
*
|
||||||
|
* @return {Array} the row if success, %null otherwise
|
||||||
|
*/
|
||||||
|
fetchObject () {
|
||||||
|
const result = this.fetch()
|
||||||
|
|
||||||
|
if (result !== null &&
|
||||||
|
result.data instanceof Array &&
|
||||||
|
result.data.length > 0) { return result.data[0] }
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetchs data from the next resultset.
|
||||||
|
*
|
||||||
|
* @return {Array} the data
|
||||||
|
*/
|
||||||
|
fetchData () {
|
||||||
|
const result = this.fetch()
|
||||||
|
|
||||||
|
if (result !== null &&
|
||||||
|
result.data instanceof Array) { return result.data }
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetchs the first row and column value from the next resultset.
|
||||||
|
*
|
||||||
|
* @return {Object} the value if success, %null otherwise
|
||||||
|
*/
|
||||||
|
fetchValue () {
|
||||||
|
const row = this.fetchRow()
|
||||||
|
|
||||||
|
if (row instanceof Array && row.length > 0) { return row[0] }
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetchs the first row from the next resultset.
|
||||||
|
*
|
||||||
|
* @return {Array} the row if success, %null otherwise
|
||||||
|
*/
|
||||||
|
fetchRow () {
|
||||||
|
const result = this.fetch()
|
||||||
|
|
||||||
|
if (result !== null &&
|
||||||
|
result.data instanceof Array &&
|
||||||
|
result.data.length > 0) {
|
||||||
|
const object = result.data[0]
|
||||||
|
const row = new Array(result.columns.length)
|
||||||
|
for (let i = 0; i < row.length; i++) { row[i] = object[result.columns[i].name] }
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
* This class stores a database result.
|
||||||
|
*/
|
||||||
|
export class Result {
|
||||||
|
/**
|
||||||
|
* Initilizes the result object.
|
||||||
|
*/
|
||||||
|
constructor (result) {
|
||||||
|
this.data = result.data
|
||||||
|
this.tables = result.tables
|
||||||
|
this.columns = result.columns
|
||||||
|
this.row = -1
|
||||||
|
|
||||||
|
if (this.columns) {
|
||||||
|
this.columnMap = {}
|
||||||
|
|
||||||
|
for (let i = 0; i < this.columns.length; i++) {
|
||||||
|
const col = this.columns[i]
|
||||||
|
col.index = i
|
||||||
|
this.columnMap[col.name] = col
|
||||||
|
}
|
||||||
|
} else { this.columnMap = null }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a value from de result.
|
||||||
|
*
|
||||||
|
* @param {String} columnName The column name
|
||||||
|
* @return {Object} The cell value
|
||||||
|
*/
|
||||||
|
get (columnName) {
|
||||||
|
return this.data[this.row][columnName]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a row.
|
||||||
|
*
|
||||||
|
* @return {Object} The cell value
|
||||||
|
*/
|
||||||
|
getObject () {
|
||||||
|
return this.data[this.row]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the result iterator.
|
||||||
|
*/
|
||||||
|
reset () {
|
||||||
|
this.row = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the internal iterator to the next row.
|
||||||
|
*/
|
||||||
|
next () {
|
||||||
|
this.row++
|
||||||
|
|
||||||
|
if (this.row >= this.data.length) { return false }
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
|
||||||
|
import { VnObject } from './object'
|
||||||
|
import { JsonException } from './json-exception'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for JSON rest connections.
|
||||||
|
*/
|
||||||
|
export class JsonConnection extends VnObject {
|
||||||
|
_connected = false
|
||||||
|
_requestsCount = 0
|
||||||
|
token = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the specified REST service with the given params and calls
|
||||||
|
* the callback when response is received.
|
||||||
|
*
|
||||||
|
* @param {String} url The service path
|
||||||
|
* @param {Object} params The params to pass to the service
|
||||||
|
* @return {Object} The parsed JSON response
|
||||||
|
*/
|
||||||
|
async send (url, params) {
|
||||||
|
if (!params) params = {}
|
||||||
|
params.srv = `json:${url}`
|
||||||
|
return this.sendWithUrl('POST', '.', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendForm (form) {
|
||||||
|
const params = {}
|
||||||
|
const elements = form.elements
|
||||||
|
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
if (elements[i].name) { params[elements[i].name] = elements[i].value }
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendWithUrl('POST', form.action, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendFormMultipart (form) {
|
||||||
|
return this.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: form.action,
|
||||||
|
data: new FormData(form)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendFormData (formData) {
|
||||||
|
return this.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: '',
|
||||||
|
data: formData
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called when REST response is received.
|
||||||
|
*/
|
||||||
|
async sendWithUrl (method, url, params) {
|
||||||
|
const urlParams = new URLSearchParams(params)
|
||||||
|
return this.request({
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
data: urlParams.toString(),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async request (config) {
|
||||||
|
const request = new XMLHttpRequest()
|
||||||
|
request.open(config.method, config.url, true)
|
||||||
|
|
||||||
|
const token = localStorage.getItem('vnToken')
|
||||||
|
if (token) { request.setRequestHeader('Authorization', token) }
|
||||||
|
|
||||||
|
const headers = config.headers
|
||||||
|
if (headers) {
|
||||||
|
for (const header in headers) { request.setRequestHeader(header, headers[header]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
request.onreadystatechange =
|
||||||
|
() => this._onStateChange(request, resolve, reject)
|
||||||
|
})
|
||||||
|
|
||||||
|
request.send(config.data)
|
||||||
|
|
||||||
|
this._requestsCount++
|
||||||
|
|
||||||
|
if (this._requestsCount === 1) { this.emit('loading-changed', true) }
|
||||||
|
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
||||||
|
_onStateChange (request, resolve, reject) {
|
||||||
|
if (request.readyState !== 4) { return }
|
||||||
|
|
||||||
|
this._requestsCount--
|
||||||
|
|
||||||
|
if (this._requestsCount === 0) { this.emit('loading-changed', false) }
|
||||||
|
|
||||||
|
let data = null
|
||||||
|
let error = null
|
||||||
|
try {
|
||||||
|
if (request.status === 0) {
|
||||||
|
const err = new JsonException()
|
||||||
|
err.message = 'The server does not respond, please check your Internet connection'
|
||||||
|
err.statusCode = request.status
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
|
let contentType = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
contentType = request
|
||||||
|
.getResponseHeader('Content-Type')
|
||||||
|
.split(';')[0]
|
||||||
|
.trim()
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentType !== 'application/json') {
|
||||||
|
const err = new JsonException()
|
||||||
|
err.message = request.statusText
|
||||||
|
err.statusCode = request.status
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
|
let json
|
||||||
|
let jsData
|
||||||
|
|
||||||
|
if (request.responseText) { json = JSON.parse(request.responseText) }
|
||||||
|
if (json) { jsData = json.data || json }
|
||||||
|
|
||||||
|
if (request.status >= 200 && request.status < 300) {
|
||||||
|
data = jsData
|
||||||
|
} else {
|
||||||
|
let exception = jsData.exception
|
||||||
|
const error = jsData.error
|
||||||
|
const err = new JsonException()
|
||||||
|
|
||||||
|
if (exception) {
|
||||||
|
exception = exception
|
||||||
|
.replace(/\\/g, '.')
|
||||||
|
.replace(/Exception$/, '')
|
||||||
|
.replace(/^Vn\.Web\./, '')
|
||||||
|
|
||||||
|
err.exception = exception
|
||||||
|
err.message = jsData.message
|
||||||
|
err.code = jsData.code
|
||||||
|
err.file = jsData.file
|
||||||
|
err.line = jsData.line
|
||||||
|
err.trace = jsData.trace
|
||||||
|
err.statusCode = request.status
|
||||||
|
} else if (error) {
|
||||||
|
err.message = error.message
|
||||||
|
err.code = error.code
|
||||||
|
err.statusCode = request.status
|
||||||
|
} else {
|
||||||
|
err.message = request.statusText
|
||||||
|
err.statusCode = request.status
|
||||||
|
}
|
||||||
|
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
data = null
|
||||||
|
error = e
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
if (error.exception === 'SessionExpired') { this.clearToken() }
|
||||||
|
|
||||||
|
this.emit('error', error)
|
||||||
|
reject(error)
|
||||||
|
} else { resolve(data) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* This class stores the database errors.
|
||||||
|
*/
|
||||||
|
export class JsonException {
|
||||||
|
constructor (exception, message, code, file, line, trace, statucCode) {
|
||||||
|
this.name = 'JsonException'
|
||||||
|
this.exception = exception
|
||||||
|
this.message = message
|
||||||
|
this.code = code
|
||||||
|
this.file = file
|
||||||
|
this.line = line
|
||||||
|
this.trace = trace
|
||||||
|
this.statusCode = statucCode
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,248 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main base class. Manages the signal system. Objects based on this class
|
||||||
|
* can be instantiated declaratively using XML.
|
||||||
|
*/
|
||||||
|
export class VnObject {
|
||||||
|
/**
|
||||||
|
* Tag to be used when the class instance is defined via XML. All classes
|
||||||
|
* must define this attribute, even if it is not used.
|
||||||
|
*/
|
||||||
|
static Tag = 'vn-object'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class public properties.
|
||||||
|
*/
|
||||||
|
static Properties = {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reference count.
|
||||||
|
*/
|
||||||
|
_refCount = 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Signal handlers data.
|
||||||
|
*/
|
||||||
|
_thisArg = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object and sets all properties passed to the class
|
||||||
|
* constructor.
|
||||||
|
*
|
||||||
|
* @param {Object} props The properties passed to the contructor
|
||||||
|
*/
|
||||||
|
constructor (props) {
|
||||||
|
this.setProperties(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize (props) {
|
||||||
|
this.setProperties(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a group of object properties.
|
||||||
|
*
|
||||||
|
* @param {Object} props Properties
|
||||||
|
*/
|
||||||
|
setProperties (props) {
|
||||||
|
for (const prop in props) { this[prop] = props[prop] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increases the object reference count.
|
||||||
|
*/
|
||||||
|
ref () {
|
||||||
|
this._refCount++
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decreases the object reference count.
|
||||||
|
*/
|
||||||
|
unref () {
|
||||||
|
this._refCount--
|
||||||
|
|
||||||
|
if (this._refCount === 0) { this._destroy() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from @Vn.Builder when it finds a custom tag as a child of the
|
||||||
|
* element.
|
||||||
|
*
|
||||||
|
* @param {Vn.Scope} scope The scope instance
|
||||||
|
* @param {Node} node The custom tag child nodes
|
||||||
|
*/
|
||||||
|
loadXml () {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from @Vn.Builder when it finds a a child tag that isn't
|
||||||
|
* associated to any property.
|
||||||
|
*
|
||||||
|
* @param {Object} child The child object instance
|
||||||
|
*/
|
||||||
|
appendChild () {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conects a signal with a function.
|
||||||
|
*
|
||||||
|
* @param {string} id The signal identifier
|
||||||
|
* @param {function} callback The callback
|
||||||
|
* @param {Object} instance The instance
|
||||||
|
*/
|
||||||
|
on (id, callback, instance) {
|
||||||
|
if (!(callback instanceof Function)) {
|
||||||
|
console.warn('Vn.Object: Invalid callback for signal \'%s\'', id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this._signalInit()
|
||||||
|
let callbacks = this._thisArg.signals[id]
|
||||||
|
|
||||||
|
if (!callbacks) { callbacks = this._thisArg.signals[id] = [] }
|
||||||
|
|
||||||
|
callbacks.push({
|
||||||
|
blocked: false,
|
||||||
|
callback,
|
||||||
|
instance
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locks/Unlocks a signal emission to the specified object.
|
||||||
|
*
|
||||||
|
* @param {string} id The signal identifier
|
||||||
|
* @param {function} callback The callback
|
||||||
|
* @param {boolean} block %true for lock the signal, %false for unlock
|
||||||
|
*/
|
||||||
|
blockSignal (id, callback, block, instance) {
|
||||||
|
if (!this._thisArg) { return }
|
||||||
|
|
||||||
|
const callbacks = this._thisArg.signals[id]
|
||||||
|
|
||||||
|
if (!callbacks) { return }
|
||||||
|
|
||||||
|
for (let i = 0; i < callbacks.length; i++) {
|
||||||
|
if (callbacks[i].callback === callback &&
|
||||||
|
callbacks[i].instance === instance) { callbacks[i].blocked = block }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits a signal in the object.
|
||||||
|
*
|
||||||
|
* @param {string} id The signal identifier
|
||||||
|
*/
|
||||||
|
emit (id) {
|
||||||
|
if (!this._thisArg) { return }
|
||||||
|
|
||||||
|
const callbacks = this._thisArg.signals[id]
|
||||||
|
|
||||||
|
if (!callbacks) { return }
|
||||||
|
|
||||||
|
const callbackArgs = []
|
||||||
|
callbackArgs.push(this)
|
||||||
|
|
||||||
|
for (let i = 1; i < arguments.length; i++) { callbackArgs.push(arguments[i]) }
|
||||||
|
|
||||||
|
for (let i = 0; i < callbacks.length; i++) {
|
||||||
|
if (!callbacks[i].blocked) { callbacks[i].callback.apply(callbacks[i].instance, callbackArgs) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects a signal from current object.
|
||||||
|
*
|
||||||
|
* @param {string} id The signal identifier
|
||||||
|
* @param {function} callback The connected callback
|
||||||
|
* @param {Object} instance The instance
|
||||||
|
*/
|
||||||
|
disconnect (id, callback, instance) {
|
||||||
|
if (!this._thisArg) { return }
|
||||||
|
|
||||||
|
const callbacks = this._thisArg.signals[id]
|
||||||
|
|
||||||
|
if (callbacks) {
|
||||||
|
for (let i = callbacks.length; i--;) {
|
||||||
|
if (callbacks[i].callback === callback &&
|
||||||
|
callbacks[i].instance === instance) { callbacks.splice(i, 1) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects all signals for the given instance.
|
||||||
|
*
|
||||||
|
* @param {Object} instance The instance
|
||||||
|
*/
|
||||||
|
disconnectByInstance (instance) {
|
||||||
|
if (!this._thisArg) { return }
|
||||||
|
|
||||||
|
const signals = this._thisArg.signals
|
||||||
|
|
||||||
|
for (const signalId in signals) {
|
||||||
|
const callbacks = signals[signalId]
|
||||||
|
|
||||||
|
if (callbacks) {
|
||||||
|
for (let i = callbacks.length; i--;) {
|
||||||
|
if (callbacks[i].instance === instance) { callbacks.splice(i, 1) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the object, this method should only be called before losing
|
||||||
|
* the last reference to the object. It can be overwritten by child classes
|
||||||
|
* but should always call the parent method.
|
||||||
|
*/
|
||||||
|
_destroy () {
|
||||||
|
if (!this._thisArg) { return }
|
||||||
|
|
||||||
|
const links = this._thisArg.links
|
||||||
|
|
||||||
|
for (const key in links) { this._unlink(links[key]) }
|
||||||
|
|
||||||
|
this._thisArg = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Links the object with another object.
|
||||||
|
*
|
||||||
|
* @param {Object} prop The linked property
|
||||||
|
* @param {Object} handlers The object events to listen with
|
||||||
|
*/
|
||||||
|
link (prop, handlers) {
|
||||||
|
this._signalInit()
|
||||||
|
const links = this._thisArg.links
|
||||||
|
|
||||||
|
for (const key in prop) {
|
||||||
|
const newObject = prop[key]
|
||||||
|
const oldObject = this[key]
|
||||||
|
|
||||||
|
if (oldObject) { this._unlink(oldObject) }
|
||||||
|
|
||||||
|
this[key] = newObject
|
||||||
|
|
||||||
|
if (newObject) {
|
||||||
|
links[key] = newObject.ref()
|
||||||
|
|
||||||
|
for (const signal in handlers) { newObject.on(signal, handlers[signal], this) }
|
||||||
|
} else if (oldObject) { links[key] = undefined }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_unlink (object) {
|
||||||
|
if (!object) return
|
||||||
|
object.disconnectByInstance(this)
|
||||||
|
object.unref()
|
||||||
|
}
|
||||||
|
|
||||||
|
_signalInit () {
|
||||||
|
if (!this._thisArg) {
|
||||||
|
this._thisArg = {
|
||||||
|
signals: {},
|
||||||
|
links: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<q-layout view="lHh Lpr lFf">
|
<q-layout view="lHh Lpr lFf">
|
||||||
<q-header elevated>
|
<q-header reveal>
|
||||||
<q-toolbar>
|
<q-toolbar>
|
||||||
<q-btn
|
<q-btn
|
||||||
flat
|
flat
|
||||||
|
@ -12,30 +12,55 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-toolbar-title>
|
<q-toolbar-title>
|
||||||
Quasar App
|
Home
|
||||||
</q-toolbar-title>
|
</q-toolbar-title>
|
||||||
|
|
||||||
<div>Quasar v{{ $q.version }}</div>
|
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
</q-header>
|
</q-header>
|
||||||
|
|
||||||
<q-drawer
|
<q-drawer
|
||||||
v-model="leftDrawerOpen"
|
v-model="leftDrawerOpen"
|
||||||
|
:width="250"
|
||||||
show-if-above
|
show-if-above
|
||||||
bordered
|
|
||||||
>
|
>
|
||||||
<q-list>
|
<q-toolbar class="logo">
|
||||||
<q-item-label
|
<img src="statics/logo-dark.svg">
|
||||||
header
|
</q-toolbar>
|
||||||
>
|
<div class="user-info">
|
||||||
Essential Links
|
<div>
|
||||||
</q-item-label>
|
<span id="user-name">{{user.nickname}}</span>
|
||||||
|
<q-btn flat icon="logout" alt="_Exit" @click="logout()"/>
|
||||||
<EssentialLink
|
</div>
|
||||||
v-for="link in essentialLinks"
|
<div id="supplant" class="supplant">
|
||||||
:key="link.title"
|
<span id="supplanted">{{supplantedUser}}</span>
|
||||||
v-bind="link"
|
<q-btn flat icon="logout" alt="_Exit"/>
|
||||||
/>
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-list
|
||||||
|
v-for="item in essentialLinks"
|
||||||
|
:key="item.id">
|
||||||
|
<q-item
|
||||||
|
v-if="!item.childs"
|
||||||
|
:to="`/${item.path}`">
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{item.description}}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-expansion-item
|
||||||
|
v-if="item.childs"
|
||||||
|
:label="item.description"
|
||||||
|
expand-separator>
|
||||||
|
<q-list>
|
||||||
|
<q-item
|
||||||
|
v-for="subitem in item.childs"
|
||||||
|
:key="subitem.id"
|
||||||
|
:to="`/${subitem.path}`"
|
||||||
|
class="q-pl-lg">
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{subitem.description}}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-expansion-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-drawer>
|
</q-drawer>
|
||||||
|
|
||||||
|
@ -45,72 +70,125 @@
|
||||||
</q-layout>
|
</q-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.q-toolbar {
|
||||||
|
min-height: 64px;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
background-color: $primary;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
& > img {
|
||||||
|
width: 160px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.user-info {
|
||||||
|
margin: 25px;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #eaeaea;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
padding: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.q-btn {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
padding: 9px;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.supplant {
|
||||||
|
display: none;
|
||||||
|
border-top: none;
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.q-drawer {
|
||||||
|
.q-item {
|
||||||
|
padding-left: 38px;
|
||||||
|
}
|
||||||
|
.q-list .q-list .q-item{
|
||||||
|
padding-left: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent, ref } from 'vue'
|
import { defineComponent, ref } from 'vue'
|
||||||
import EssentialLink from 'components/EssentialLink.vue'
|
import { userStore } from 'stores/user'
|
||||||
|
|
||||||
const linksList = [
|
|
||||||
{
|
|
||||||
title: 'Docs',
|
|
||||||
caption: 'quasar.dev',
|
|
||||||
icon: 'school',
|
|
||||||
link: 'https://quasar.dev'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Github',
|
|
||||||
caption: 'github.com/quasarframework',
|
|
||||||
icon: 'code',
|
|
||||||
link: 'https://github.com/quasarframework'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Discord Chat Channel',
|
|
||||||
caption: 'chat.quasar.dev',
|
|
||||||
icon: 'chat',
|
|
||||||
link: 'https://chat.quasar.dev'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Forum',
|
|
||||||
caption: 'forum.quasar.dev',
|
|
||||||
icon: 'record_voice_over',
|
|
||||||
link: 'https://forum.quasar.dev'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Twitter',
|
|
||||||
caption: '@quasarframework',
|
|
||||||
icon: 'rss_feed',
|
|
||||||
link: 'https://twitter.quasar.dev'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Facebook',
|
|
||||||
caption: '@QuasarFramework',
|
|
||||||
icon: 'public',
|
|
||||||
link: 'https://facebook.quasar.dev'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Quasar Awesome',
|
|
||||||
caption: 'Community Quasar projects',
|
|
||||||
icon: 'favorite',
|
|
||||||
link: 'https://awesome.quasar.dev'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'MainLayout',
|
name: 'MainLayout',
|
||||||
|
props: {},
|
||||||
components: {
|
|
||||||
EssentialLink
|
|
||||||
},
|
|
||||||
|
|
||||||
setup () {
|
setup () {
|
||||||
const leftDrawerOpen = ref(false)
|
const leftDrawerOpen = ref(false)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
essentialLinks: linksList,
|
user: userStore(),
|
||||||
|
supplantedUser: ref(''),
|
||||||
|
essentialLinks: ref(null),
|
||||||
leftDrawerOpen,
|
leftDrawerOpen,
|
||||||
toggleLeftDrawer () {
|
toggleLeftDrawer () {
|
||||||
leftDrawerOpen.value = !leftDrawerOpen.value
|
leftDrawerOpen.value = !leftDrawerOpen.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async mounted () {
|
||||||
|
await this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async fetchData () {
|
||||||
|
const sections = await this.$jApi.query('SELECT * FROM myMenu')
|
||||||
|
|
||||||
|
const sectionMap = new Map()
|
||||||
|
for (const section of sections) {
|
||||||
|
sectionMap.set(section.id, section)
|
||||||
|
}
|
||||||
|
|
||||||
|
const sectionTree = []
|
||||||
|
for (const section of sections) {
|
||||||
|
const parent = section.parentFk
|
||||||
|
if (parent) {
|
||||||
|
const parentSection = sectionMap.get(parent)
|
||||||
|
if (!parentSection) continue
|
||||||
|
let childs = parentSection.childs
|
||||||
|
if (!childs) { childs = parentSection.childs = [] }
|
||||||
|
childs.push(section)
|
||||||
|
} else {
|
||||||
|
sectionTree.push(section)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.essentialLinks = sectionTree
|
||||||
|
},
|
||||||
|
|
||||||
|
async logout () {
|
||||||
|
this.user.logout()
|
||||||
|
this.$router.push('/login')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="q-pa-sm row items-start">
|
||||||
|
<div
|
||||||
|
class="new-card q-pa-sm"
|
||||||
|
v-for="myNew in news"
|
||||||
|
:key="myNew.id">
|
||||||
|
<q-card>
|
||||||
|
<q-img :src="`https://verdnatura.es/vn-image-data/news/full/${myNew.image}`">
|
||||||
|
</q-img>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-h5">{{ myNew.title }}</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section class="new-body">
|
||||||
|
<div v-html="myNew.text"/>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-page-sticky position="bottom-right" :offset="[18, 18]">
|
||||||
|
<q-btn
|
||||||
|
fab
|
||||||
|
icon="add_shopping_cart"
|
||||||
|
color="accent"
|
||||||
|
:to="{name: 'catalog'}"
|
||||||
|
:title="$t('startOrder')"
|
||||||
|
/>
|
||||||
|
</q-page-sticky>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.new-card {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@media screen and (min-width: 1000px) and (max-width: 1399px) {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 1400px) and (max-width: 1699px) {
|
||||||
|
width: 33.33%;
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 1700px) {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.new-body {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #6a1;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PageIndex',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
news: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted () {
|
||||||
|
this.news = await this.$jApi.query(
|
||||||
|
`SELECT title, text, image, id
|
||||||
|
FROM news
|
||||||
|
ORDER BY priority, created DESC`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
en-US:
|
||||||
|
startOrder: Start order
|
||||||
|
es-ES:
|
||||||
|
startOrder: Empezar pedido
|
||||||
|
</i18n>
|
|
@ -129,16 +129,21 @@ a {
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { userStore } from 'stores/user'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'VnLogin',
|
name: 'VnLogin',
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
user: userStore(),
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
remember: false,
|
remember: false,
|
||||||
showPwd: true
|
showPwd: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
if (this.$route.query.emailConfirmed !== undefined) {
|
if (this.$route.query.emailConfirmed !== undefined) {
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
|
@ -151,27 +156,11 @@ export default {
|
||||||
this.$refs.password.focus()
|
this.$refs.password.focus()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async onLogin () {
|
async onLogin () {
|
||||||
const params = {
|
await this.user.login(this.email, this.password)
|
||||||
password: this.password
|
this.$router.push('/')
|
||||||
}
|
|
||||||
|
|
||||||
if (this.email.indexOf('@') !== -1) {
|
|
||||||
params.email = this.email
|
|
||||||
} else {
|
|
||||||
params.username = this.email
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await this.$axios.post('users/login', params)
|
|
||||||
localStorage.setItem('token', res.data.id)
|
|
||||||
Object.assign(this.$state.user, {
|
|
||||||
loggedIn: true,
|
|
||||||
token: res.data.id,
|
|
||||||
id: res.data.userId,
|
|
||||||
name: this.email
|
|
||||||
})
|
|
||||||
this.$router.push('/home')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,15 @@ const routes = [
|
||||||
path: '/',
|
path: '/',
|
||||||
component: () => import('layouts/MainLayout.vue'),
|
component: () => import('layouts/MainLayout.vue'),
|
||||||
children: [
|
children: [
|
||||||
{ path: '', component: () => import('pages/IndexPage.vue') }
|
{
|
||||||
|
name: '',
|
||||||
|
path: '',
|
||||||
|
component: () => import('pages/Home.vue')
|
||||||
|
}, {
|
||||||
|
name: 'home',
|
||||||
|
path: '/cms/home',
|
||||||
|
component: () => import('pages/Home.vue')
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { api, jApi } from 'boot/axios'
|
||||||
|
|
||||||
|
export const userStore = defineStore('user', {
|
||||||
|
state: () => ({
|
||||||
|
token: null,
|
||||||
|
id: null,
|
||||||
|
name: null,
|
||||||
|
nickname: null
|
||||||
|
}),
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
loggedIn: state => state.token != null
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async login (user, password) {
|
||||||
|
const params = { user, password }
|
||||||
|
const res = await api.post('Accounts/login', params)
|
||||||
|
localStorage.setItem('vnToken', res.data.token)
|
||||||
|
|
||||||
|
const userData = await jApi.getObject(
|
||||||
|
'SELECT id, nickname FROM account.myUser'
|
||||||
|
)
|
||||||
|
|
||||||
|
this.$patch({
|
||||||
|
token: res.data.token,
|
||||||
|
name: user,
|
||||||
|
id: userData.id,
|
||||||
|
nickname: userData.nickname
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
async logout () {
|
||||||
|
if (localStorage.getItem('vnToken') != null) {
|
||||||
|
await api.post('Accounts/logout')
|
||||||
|
}
|
||||||
|
localStorage.removeItem('vnToken')
|
||||||
|
this.$reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in New Issue