0
1
Fork 0

refs #4922 Login, logout, home, layout style

This commit is contained in:
Juan Ferrer 2022-12-06 11:41:41 +01:00
parent 042b8b0309
commit 0d0be4ee5f
20 changed files with 1172 additions and 157 deletions

View File

@ -51,4 +51,4 @@
} }
.new-text li { .new-text li {
margin: 4px 0; margin: 4px 0;
} }

66
package-lock.json generated
View File

@ -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
} }
} }
}, },

View File

@ -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 ./"

View File

@ -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

View File

@ -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 }

View File

@ -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>

View File

@ -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);
} }

BIN
src/css/opensans.ttf Normal file

Binary file not shown.

View File

@ -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;

174
src/js/db/connection.js Normal file
View File

@ -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
}

113
src/js/db/result-set.js Normal file
View File

@ -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
}
}

61
src/js/db/result.js Normal file
View File

@ -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
}
}

View File

@ -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) }
}
}

View File

@ -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
}
}

248
src/js/vn/object.js Normal file
View File

@ -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: {}
}
}
}
}

View File

@ -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>

83
src/pages/Home.vue Normal file
View File

@ -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>

View File

@ -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')
} }
} }
} }

View File

@ -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')
}
] ]
}, },

42
src/stores/user.js Normal file
View File

@ -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()
}
}
})