Init config #68
139
.eslintrc.js
139
.eslintrc.js
|
@ -1,79 +1,82 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
|
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
|
||||||
// This option interrupts the configuration hierarchy at this file
|
// This option interrupts the configuration hierarchy at this file
|
||||||
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
|
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
|
||||||
root: true,
|
root: true,
|
||||||
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: '@babel/eslint-parser',
|
parser: '@babel/eslint-parser',
|
||||||
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
|
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
|
||||||
sourceType: 'module' // Allows for the use of imports
|
sourceType: 'module', // Allows for the use of imports
|
||||||
},
|
},
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
'vue/setup-compiler-macros': true
|
'vue/setup-compiler-macros': true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Rules order is important, please avoid shuffling them
|
extends: [
|
||||||
extends: [
|
// Base ESLint recommended rules
|
||||||
// Base ESLint recommended rules
|
// 'eslint:recommended',
|
||||||
// 'eslint:recommended',
|
|
||||||
|
|
||||||
// Uncomment any of the lines below to choose desired strictness,
|
// Uncomment any of the lines below to choose desired strictness,
|
||||||
// but leave only one uncommented!
|
// but leave only one uncommented!
|
||||||
// See https://eslint.vuejs.org/rules/#available-rules
|
// See https://eslint.vuejs.org/rules/#available-rules
|
||||||
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
|
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
|
||||||
// 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
|
// 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
|
||||||
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
||||||
|
|
||||||
'standard'
|
'standard',
|
||||||
|
],
|
||||||
|
|
||||||
],
|
plugins: ['vue', 'prettier'],
|
||||||
|
|
||||||
plugins: [
|
globals: {
|
||||||
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
|
ga: 'readonly', // Google Analytics
|
||||||
// required to lint *.vue files
|
cordova: 'readonly',
|
||||||
'vue',
|
__statics: 'readonly',
|
||||||
|
__QUASAR_SSR__: 'readonly',
|
||||||
|
__QUASAR_SSR_SERVER__: 'readonly',
|
||||||
|
__QUASAR_SSR_CLIENT__: 'readonly',
|
||||||
|
__QUASAR_SSR_PWA__: 'readonly',
|
||||||
|
process: 'readonly',
|
||||||
|
Capacitor: 'readonly',
|
||||||
|
chrome: 'readonly',
|
||||||
|
},
|
||||||
|
|
||||||
],
|
// add your custom rules here
|
||||||
|
rules: {
|
||||||
|
// allow async-await
|
||||||
|
'generator-star-spacing': 'off',
|
||||||
|
// allow paren-less arrow functions
|
||||||
|
'arrow-parens': 'off',
|
||||||
|
'one-var': 'off',
|
||||||
|
'no-void': 'off',
|
||||||
|
'multiline-ternary': 'off',
|
||||||
|
|
||||||
globals: {
|
'import/first': 'off',
|
||||||
ga: 'readonly', // Google Analytics
|
'import/named': 'error',
|
||||||
cordova: 'readonly',
|
'import/namespace': 'error',
|
||||||
__statics: 'readonly',
|
'import/default': 'error',
|
||||||
__QUASAR_SSR__: 'readonly',
|
'import/export': 'error',
|
||||||
__QUASAR_SSR_SERVER__: 'readonly',
|
'import/extensions': 'off',
|
||||||
__QUASAR_SSR_CLIENT__: 'readonly',
|
'import/no-unresolved': 'off',
|
||||||
__QUASAR_SSR_PWA__: 'readonly',
|
'import/no-extraneous-dependencies': 'off',
|
||||||
process: 'readonly',
|
|
||||||
Capacitor: 'readonly',
|
|
||||||
chrome: 'readonly'
|
|
||||||
},
|
|
||||||
|
|
||||||
// add your custom rules here
|
'prefer-promise-reject-errors': 'off',
|
||||||
rules: {
|
semi: 'off',
|
||||||
|
// allow debugger during development only
|
||||||
// allow async-await
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
'generator-star-spacing': 'off',
|
},
|
||||||
// allow paren-less arrow functions
|
overrides: [
|
||||||
'arrow-parens': 'off',
|
{
|
||||||
'one-var': 'off',
|
extends: ['plugin:vue/vue3-essential'],
|
||||||
'no-void': 'off',
|
files: ['src/**/*.{js,vue,scss}'], // Aplica ESLint solo a archivos .js, .vue y .scss dentro de src (Proyecto de quasar)
|
||||||
'multiline-ternary': 'off',
|
rules: {
|
||||||
|
semi: 'off',
|
||||||
'import/first': 'off',
|
indent: ['error', 4, { SwitchCase: 1 }],
|
||||||
'import/named': 'error',
|
'space-before-function-paren': 'off',
|
||||||
'import/namespace': 'error',
|
},
|
||||||
'import/default': 'error',
|
},
|
||||||
'import/export': 'error',
|
],
|
||||||
'import/extensions': 'off',
|
};
|
||||||
'import/no-unresolved': 'off',
|
|
||||||
'import/no-extraneous-dependencies': 'off',
|
|
||||||
|
|
||||||
'prefer-promise-reject-errors': 'off',
|
|
||||||
|
|
||||||
// allow debugger during development only
|
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
module.exports = {
|
||||||
|
printWidth: 80,
|
||||||
|
tabWidth: 4,
|
||||||
|
useTabs: false,
|
||||||
|
singleQuote: true,
|
||||||
|
bracketSpacing: true,
|
||||||
|
arrowParens: 'avoid',
|
||||||
|
trailingComma: 'none'
|
||||||
|
};
|
|
@ -4,14 +4,7 @@
|
||||||
"editor.bracketPairColorization.enabled": true,
|
"editor.bracketPairColorization.enabled": true,
|
||||||
"editor.guides.bracketPairs": true,
|
"editor.guides.bracketPairs": true,
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.codeActionsOnSave": [
|
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
|
||||||
"source.fixAll.eslint"
|
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"]
|
||||||
],
|
|
||||||
"eslint.validate": [
|
|
||||||
"javascript",
|
|
||||||
"javascriptreact",
|
|
||||||
"typescript",
|
|
||||||
"vue"
|
|
||||||
]
|
|
||||||
}
|
}
|
|
@ -1,34 +1,25 @@
|
||||||
#!/usr/bin/env groovy
|
#!/usr/bin/env groovy
|
||||||
|
|
||||||
|
def BRANCH_ENV = [
|
||||||
|
test: 'test',
|
||||||
|
master: 'production'
|
||||||
|
]
|
||||||
|
def remote = [:]
|
||||||
|
|
||||||
|
node {
|
||||||
|
stage('Setup') {
|
||||||
|
env.NODE_ENV = BRANCH_ENV[env.BRANCH_NAME] ?: 'dev'
|
||||||
|
|
||||||
|
echo "NODE_NAME: ${env.NODE_NAME}"
|
||||||
|
echo "WORKSPACE: ${env.WORKSPACE}"
|
||||||
|
}
|
||||||
|
}
|
||||||
pipeline {
|
pipeline {
|
||||||
agent any
|
agent any
|
||||||
environment {
|
environment {
|
||||||
PROJECT_NAME = 'hedera-web'
|
PROJECT_NAME = 'hedera-web'
|
||||||
STACK_NAME = "${env.PROJECT_NAME}-${env.BRANCH_NAME}"
|
|
||||||
}
|
}
|
||||||
stages {
|
stages {
|
||||||
stage('Checkout') {
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
def packageJson = readJSON file: 'package.json'
|
|
||||||
env.VERSION = packageJson.version
|
|
||||||
|
|
||||||
switch (env.BRANCH_NAME) {
|
|
||||||
case 'master':
|
|
||||||
env.NODE_ENV = 'production'
|
|
||||||
env.MAIN_REPLICAS = 3
|
|
||||||
env.CRON_REPLICAS = 1
|
|
||||||
break
|
|
||||||
case 'test':
|
|
||||||
env.NODE_ENV = 'test'
|
|
||||||
env.MAIN_REPLICAS = 1
|
|
||||||
env.CRON_REPLICAS = 0
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setEnv()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Debuild') {
|
stage('Debuild') {
|
||||||
when {
|
when {
|
||||||
anyOf {
|
anyOf {
|
||||||
|
@ -38,31 +29,28 @@ pipeline {
|
||||||
}
|
}
|
||||||
agent {
|
agent {
|
||||||
docker {
|
docker {
|
||||||
image 'registry.verdnatura.es/debuild:2.21.3-vn2'
|
image 'registry.verdnatura.es/verdnatura/debuild:2.23.4-vn7'
|
||||||
registryUrl 'https://registry.verdnatura.es/'
|
registryUrl 'https://registry.verdnatura.es/'
|
||||||
registryCredentialsId 'docker-registry'
|
registryCredentialsId 'docker-registry'
|
||||||
args '-v /mnt/appdata/reprepro:/reprepro'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
sh 'debuild -us -uc -b'
|
sh 'debuild -us -uc -b'
|
||||||
sh 'vn-includedeb stretch'
|
sh 'mkdir -p debuild'
|
||||||
}
|
sh 'mv ../hedera-web_* debuild'
|
||||||
}
|
|
||||||
stage('Container') {
|
script {
|
||||||
when {
|
def files = findFiles(glob: 'debuild/*.changes')
|
||||||
anyOf {
|
files.each { file -> env.CHANGES_FILE = file.name }
|
||||||
branch 'master'
|
}
|
||||||
branch 'test'
|
|
||||||
|
configFileProvider([
|
||||||
|
configFile(fileId: "dput.cf", variable: 'DPUT_CONFIG')
|
||||||
|
]) {
|
||||||
|
sshagent(credentials: ['jenkins-agent']) {
|
||||||
|
sh 'dput --config "$DPUT_CONFIG" verdnatura "debuild/$CHANGES_FILE"'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
environment {
|
|
||||||
CREDS = credentials('docker-registry')
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY'
|
|
||||||
sh 'docker-compose build --build-arg BUILD_ID=$BUILD_ID --parallel'
|
|
||||||
sh 'docker-compose push'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Deploy') {
|
stage('Deploy') {
|
||||||
|
@ -73,15 +61,41 @@ pipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
environment {
|
environment {
|
||||||
DOCKER_HOST = "${env.SWARM_HOST}"
|
CREDS = credentials('docker-registry')
|
||||||
|
IMAGE = "$REGISTRY/verdnatura/hedera-web"
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
sh "docker stack deploy --with-registry-auth --compose-file docker-compose.yml ${env.STACK_NAME}"
|
script {
|
||||||
|
def packageJson = readJSON file: 'package.json'
|
||||||
|
env.VERSION = "${packageJson.version}"
|
||||||
|
env.TAG = "${packageJson.version}-build${env.BUILD_ID}"
|
||||||
|
}
|
||||||
|
|
||||||
|
sh 'docker-compose build --build-arg BUILD_ID=$BUILD_ID --parallel'
|
||||||
|
sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY'
|
||||||
|
sh 'docker push $IMAGE:$TAG'
|
||||||
|
|
||||||
|
script {
|
||||||
|
if (env.BRANCH_NAME == 'master') {
|
||||||
|
sh 'docker tag $IMAGE:$TAG $IMAGE:latest'
|
||||||
|
sh 'docker push $IMAGE:latest'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
withKubeConfig([
|
||||||
|
serverUrl: "$KUBERNETES_API",
|
||||||
|
credentialsId: 'kubernetes',
|
||||||
|
namespace: 'salix'
|
||||||
|
]) {
|
||||||
|
sh 'kubectl set image deployment/hedera-web-$BRANCH_NAME hedera-web-$BRANCH_NAME=$IMAGE:$TAG'
|
||||||
|
sh 'kubectl set image deployment/hedera-web-cron-$BRANCH_NAME hedera-web-cron-$BRANCH_NAME=$IMAGE:$TAG'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post {
|
post {
|
||||||
unsuccessful {
|
unsuccessful {
|
||||||
|
setEnv()
|
||||||
sendEmail()
|
sendEmail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
168
package.json
168
package.json
|
@ -1,82 +1,90 @@
|
||||||
{
|
{
|
||||||
"name": "hedera-web",
|
"name": "hedera-web",
|
||||||
"version": "22.48.2",
|
"version": "22.48.2",
|
||||||
"description": "Verdnatura web page",
|
"description": "Verdnatura web page",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"author": "Juan Ferrer Toribio <juan@verdnatura.es>",
|
"productName": "Salix",
|
||||||
"repository": {
|
"author": "Verdnatura",
|
||||||
"type": "git",
|
"repository": {
|
||||||
"url": "https://git.verdnatura.es/hedera-web"
|
"type": "git",
|
||||||
},
|
"url": "https://git.verdnatura.es/hedera-web"
|
||||||
"devDependencies": {
|
},
|
||||||
"@babel/eslint-parser": "^7.13.14",
|
"devDependencies": {
|
||||||
"@babel/preset-env": "^7.20.2",
|
"@babel/eslint-parser": "^7.13.14",
|
||||||
"@intlify/vue-i18n-loader": "^4.2.0",
|
"@babel/preset-env": "^7.20.2",
|
||||||
"@quasar/app-webpack": "^3.0.0",
|
"@intlify/vue-i18n-loader": "^4.2.0",
|
||||||
"archiver": "^5.3.1",
|
"@quasar/app-webpack": "^3.0.0",
|
||||||
"assets-webpack-plugin": "^7.1.1",
|
"archiver": "^5.3.1",
|
||||||
"babel-loader": "^9.1.0",
|
"assets-webpack-plugin": "^7.1.1",
|
||||||
"bundle-loader": "^0.5.6",
|
"babel-loader": "^9.1.0",
|
||||||
"eslint": "^8.10.0",
|
"bundle-loader": "^0.5.6",
|
||||||
"eslint-config-standard": "^17.0.0",
|
"css-loader": "^5.2.7",
|
||||||
"eslint-plugin-import": "^2.19.1",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-n": "^15.0.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-promise": "^6.0.0",
|
"eslint-config-standard": "^17.0.0",
|
||||||
"eslint-plugin-vue": "^9.0.0",
|
"eslint-plugin-import": "^2.19.1",
|
||||||
"eslint-webpack-plugin": "^3.1.1",
|
"eslint-plugin-n": "^15.0.0",
|
||||||
"file-loader": "^6.2.0",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"fs-extra": "^10.1.0",
|
"eslint-plugin-promise": "^6.0.0",
|
||||||
"glob": "^8.0.3",
|
"eslint-plugin-vue": "^9.27.0",
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"eslint-webpack-plugin": "^3.1.1",
|
||||||
"json-loader": "^0.5.7",
|
"file-loader": "^6.2.0",
|
||||||
"mini-css-extract-plugin": "^2.7.0",
|
"fs-extra": "^10.1.0",
|
||||||
"node-sass": "^7.0.1",
|
"glob": "^8.0.3",
|
||||||
"raw-loader": "^4.0.2",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"sass-loader": "^12.6.0",
|
"json-loader": "^0.5.7",
|
||||||
"style-loader": "^3.3.1",
|
"mini-css-extract-plugin": "^2.7.0",
|
||||||
"url-loader": "^4.1.1",
|
"node-sass": "^7.0.1",
|
||||||
"webpack": "^5.75.0",
|
"postcss": "^8.4.39",
|
||||||
"webpack-cli": "^4.10.0",
|
"postcss-import": "^13.0.0",
|
||||||
"webpack-dev-server": "^4.11.1",
|
"postcss-loader": "^4.3.0",
|
||||||
"webpack-merge": "^5.8.0",
|
"postcss-url": "^10.1.3",
|
||||||
"yaml-loader": "^0.5.0"
|
"raw-loader": "^4.0.2",
|
||||||
},
|
"sass-loader": "^12.6.0",
|
||||||
"dependencies": {
|
"style-loader": "^3.3.1",
|
||||||
"@quasar/extras": "^1.0.0",
|
"url-loader": "^4.1.1",
|
||||||
"axios": "^0.21.1",
|
"webpack": "^5.75.0",
|
||||||
"core-js": "^3.6.5",
|
"webpack-cli": "^4.10.0",
|
||||||
"js-yaml": "^3.12.1",
|
"webpack-dev-server": "^4.11.1",
|
||||||
"mootools": "^1.5.2",
|
"webpack-merge": "^5.8.0",
|
||||||
"pinia": "^2.0.11",
|
"yaml-loader": "^0.5.0"
|
||||||
"promise-polyfill": "^8.2.3",
|
},
|
||||||
"quasar": "^2.6.0",
|
"dependencies": {
|
||||||
"require-yaml": "0.0.1",
|
"@quasar/extras": "^1.0.0",
|
||||||
"tinymce": "^6.3.0",
|
"axios": "^0.21.1",
|
||||||
"vue": "^3.0.0",
|
"core-js": "^3.6.5",
|
||||||
"vue-i18n": "^9.0.0",
|
"js-yaml": "^3.12.1",
|
||||||
"vue-router": "^4.0.0"
|
"mootools": "^1.5.2",
|
||||||
},
|
"pinia": "^2.0.11",
|
||||||
"scripts": {
|
"promise-polyfill": "^8.2.3",
|
||||||
"front": "webpack serve --open",
|
"quasar": "^2.6.0",
|
||||||
"back": "cd ../vn-database && myvc start && cd ../salix && gulp backOnly",
|
"require-yaml": "0.0.1",
|
||||||
"build": "rm -rf build/ ; webpack",
|
"tinymce": "^6.3.0",
|
||||||
"clean": "rm -rf build/",
|
"vue": "^3.0.0",
|
||||||
"lint": "eslint --ext .js,.vue ./"
|
"vue-i18n": "^9.0.0",
|
||||||
},
|
"vue-router": "^4.0.0"
|
||||||
"browserslist": [
|
},
|
||||||
"last 10 Chrome versions",
|
"scripts": {
|
||||||
"last 10 Firefox versions",
|
"front": "webpack serve --open",
|
||||||
"last 4 Edge versions",
|
"back": "cd ../vn-database && myvc start && cd ../salix && gulp backOnly",
|
||||||
"last 7 Safari versions",
|
"build": "rm -rf build/ ; webpack",
|
||||||
"last 8 Android versions",
|
"clean": "rm -rf build/",
|
||||||
"last 8 ChromeAndroid versions",
|
"lint": "eslint --ext .js,.vue ./"
|
||||||
"last 8 FirefoxAndroid versions",
|
},
|
||||||
"last 10 iOS versions",
|
"browserslist": [
|
||||||
"last 5 Opera versions"
|
"last 10 Chrome versions",
|
||||||
],
|
"last 10 Firefox versions",
|
||||||
"engines": {
|
"last 4 Edge versions",
|
||||||
"node": ">= 12.22.1",
|
"last 7 Safari versions",
|
||||||
"npm": ">= 6.13.4",
|
"last 8 Android versions",
|
||||||
"yarn": ">= 1.21.1"
|
"last 8 ChromeAndroid versions",
|
||||||
}
|
"last 8 FirefoxAndroid versions",
|
||||||
|
"last 10 iOS versions",
|
||||||
|
"last 5 Opera versions"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.22.1",
|
||||||
|
"npm": ">= 6.13.4",
|
||||||
|
"yarn": ">= 1.21.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,15 +104,14 @@ module.exports = configure(function (ctx) {
|
||||||
type: 'http'
|
type: 'http'
|
||||||
},
|
},
|
||||||
port: 8080,
|
port: 8080,
|
||||||
open: true, // opens browser window automatically
|
open: false,
|
||||||
|
|
||||||
// static: __dirname,
|
// static: __dirname,
|
||||||
headers: { 'Access-Control-Allow-Origin': '*' },
|
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||||
// stats: { chunks: false },
|
// stats: { chunks: false },
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': 'http://localhost:3000',
|
'/api': 'http://localhost:3000',
|
||||||
'/': {
|
'/': {
|
||||||
target: 'http://localhost/projects/hedera-web',
|
target: 'http://localhost:3001',
|
||||||
bypass: (req) => req.path !== '/' ? req.path : null
|
bypass: (req) => req.path !== '/' ? req.path : null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,7 +120,7 @@ module.exports = configure(function (ctx) {
|
||||||
// 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
|
||||||
framework: {
|
framework: {
|
||||||
config: {},
|
config: {},
|
||||||
|
autoImportComponentCase: 'pascal',
|
||||||
// iconSet: 'material-icons', // Quasar icon set
|
// iconSet: 'material-icons', // Quasar icon set
|
||||||
// lang: 'en-US', // Quasar language pack
|
// lang: 'en-US', // Quasar language pack
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<router-view />
|
<router-view />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'App'
|
name: 'App'
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { appStore } from 'stores/app'
|
||||||
import { userStore } from 'stores/user'
|
import { userStore } from 'stores/user'
|
||||||
|
|
||||||
export default boot(({ app }) => {
|
export default boot(({ app }) => {
|
||||||
const props = app.config.globalProperties
|
const props = app.config.globalProperties
|
||||||
props.$app = appStore()
|
props.$app = appStore()
|
||||||
props.$user = userStore()
|
props.$user = userStore()
|
||||||
props.$actions = document.createElement('div')
|
props.$actions = document.createElement('div')
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,33 +10,33 @@ import axios from 'axios'
|
||||||
// "export default () => {}" function below (which runs individually
|
// "export default () => {}" function below (which runs individually
|
||||||
// for each client)
|
// for each client)
|
||||||
const api = axios.create({
|
const api = axios.create({
|
||||||
baseURL: `//${location.hostname}:${location.port}/api/`
|
baseURL: `//${location.hostname}:${location.port}/api/`
|
||||||
})
|
})
|
||||||
|
|
||||||
const jApi = new Connection()
|
const jApi = new Connection()
|
||||||
|
|
||||||
export default boot(({ app }) => {
|
export default boot(({ app }) => {
|
||||||
const user = userStore()
|
const user = userStore()
|
||||||
function addToken (config) {
|
function addToken (config) {
|
||||||
if (user.token) {
|
if (user.token) {
|
||||||
config.headers.Authorization = user.token
|
config.headers.Authorization = user.token
|
||||||
|
}
|
||||||
|
return config
|
||||||
}
|
}
|
||||||
return config
|
api.interceptors.request.use(addToken)
|
||||||
}
|
jApi.use(addToken)
|
||||||
api.interceptors.request.use(addToken)
|
|
||||||
jApi.use(addToken)
|
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
app.config.globalProperties.$axios = axios
|
app.config.globalProperties.$axios = axios
|
||||||
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
|
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
|
||||||
// so you won't necessarily have to import axios in each vue file
|
// so you won't necessarily have to import axios in each vue file
|
||||||
|
|
||||||
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
|
app.config.globalProperties.$jApi = jApi
|
||||||
})
|
})
|
||||||
|
|
||||||
export { api, jApi }
|
export { api, jApi }
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
|
|
||||||
export default async ({ app }) => {
|
export default async ({ app }) => {
|
||||||
/*
|
/*
|
||||||
window.addEventListener('error',
|
window.addEventListener('error',
|
||||||
e => onWindowError(e));
|
e => onWindowError(e));
|
||||||
window.addEventListener('unhandledrejection',
|
window.addEventListener('unhandledrejection',
|
||||||
|
@ -13,55 +12,55 @@ export default async ({ app }) => {
|
||||||
errorHandler(event.reason);
|
errorHandler(event.reason);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
app.config.errorHandler = (err, vm, info) => {
|
app.config.errorHandler = (err, vm, info) => {
|
||||||
errorHandler(err, vm)
|
errorHandler(err, vm)
|
||||||
}
|
|
||||||
|
|
||||||
function errorHandler (err, vm) {
|
|
||||||
let message
|
|
||||||
let tMessage
|
|
||||||
let res = err.response
|
|
||||||
|
|
||||||
// XXX: Compatibility with old JSON service
|
|
||||||
if (err.name === 'JsonException') {
|
|
||||||
res = {
|
|
||||||
status: err.statusCode,
|
|
||||||
data: { error: { message: err.message } }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res) {
|
function errorHandler (err, vm) {
|
||||||
const status = res.status
|
let message
|
||||||
|
let tMessage
|
||||||
|
let res = err.response
|
||||||
|
|
||||||
if (status >= 400 && status < 500) {
|
// XXX: Compatibility with old JSON service
|
||||||
switch (status) {
|
if (err.name === 'JsonException') {
|
||||||
case 401:
|
res = {
|
||||||
tMessage = 'loginFailed'
|
status: err.statusCode,
|
||||||
break
|
data: { error: { message: err.message } }
|
||||||
case 403:
|
}
|
||||||
tMessage = 'authenticationRequired'
|
|
||||||
vm.$router.push('/login')
|
|
||||||
break
|
|
||||||
case 404:
|
|
||||||
tMessage = 'notFound'
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
message = res.data.error.message
|
|
||||||
}
|
}
|
||||||
} else if (status >= 500) {
|
|
||||||
tMessage = 'internalServerError'
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tMessage = 'somethingWentWrong'
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tMessage) {
|
if (res) {
|
||||||
message = vm.$t(tMessage)
|
const status = res.status
|
||||||
|
|
||||||
|
if (status >= 400 && status < 500) {
|
||||||
|
switch (status) {
|
||||||
|
case 401:
|
||||||
|
tMessage = 'loginFailed'
|
||||||
|
break
|
||||||
|
case 403:
|
||||||
|
tMessage = 'authenticationRequired'
|
||||||
|
vm.$router.push('/login')
|
||||||
|
break
|
||||||
|
case 404:
|
||||||
|
tMessage = 'notFound'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
message = res.data.error.message
|
||||||
|
}
|
||||||
|
} else if (status >= 500) {
|
||||||
|
tMessage = 'internalServerError'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tMessage = 'somethingWentWrong'
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tMessage) {
|
||||||
|
message = vm.$t(tMessage)
|
||||||
|
}
|
||||||
|
vm.$q.notify({
|
||||||
|
message,
|
||||||
|
type: 'negative'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
vm.$q.notify({
|
|
||||||
message,
|
|
||||||
type: 'negative'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
import { boot } from 'quasar/wrappers'
|
import { boot } from 'quasar/wrappers';
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n';
|
||||||
import messages from 'src/i18n'
|
import messages from 'src/i18n';
|
||||||
|
|
||||||
export default boot(({ app }) => {
|
const i18n = createI18n({
|
||||||
const i18n = createI18n({
|
locale: navigator.language || navigator.userLanguage,
|
||||||
locale: 'es-ES',
|
fallbackLocale: 'en',
|
||||||
globalInjection: true,
|
globalInjection: true,
|
||||||
|
missingWarn: false,
|
||||||
|
fallbackWarn: false,
|
||||||
|
legacy: false,
|
||||||
silentTranslationWarn: true,
|
silentTranslationWarn: true,
|
||||||
silentFallbackWarn: true,
|
silentFallbackWarn: true,
|
||||||
messages
|
messages
|
||||||
})
|
});
|
||||||
|
export default boot(({ app }) => {
|
||||||
|
// Set i18n instance on app
|
||||||
|
app.use(i18n);
|
||||||
|
|
||||||
// Set i18n instance on app
|
window.i18n = i18n.global;
|
||||||
app.use(i18n)
|
});
|
||||||
|
|
||||||
window.i18n = i18n.global
|
export { i18n };
|
||||||
})
|
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
src: url(./poppins.ttf) format('truetype');
|
src: url(./poppins.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Open Sans';
|
font-family: 'Open Sans';
|
||||||
src: url(./opensans.ttf) format('truetype');
|
src: url(./opensans.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@mixin mobile {
|
@mixin mobile {
|
||||||
@media screen and (max-width: 960px) {
|
@media screen and (max-width: 960px) {
|
||||||
|
@ -28,7 +28,7 @@ a.link {
|
||||||
}
|
}
|
||||||
.q-card {
|
.q-card {
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
box-shadow: 0 0 3px rgba(0, 0, 0, .1);
|
box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
.q-page-sticky.fixed-bottom-right {
|
.q-page-sticky.fixed-bottom-right {
|
||||||
margin: 18px;
|
margin: 18px;
|
||||||
|
|
|
@ -12,17 +12,17 @@
|
||||||
// to match your app's branding.
|
// to match your app's branding.
|
||||||
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
||||||
|
|
||||||
$primary : #1A1A1A;
|
$primary: #1a1a1a;
|
||||||
$secondary : #26A69A;
|
$secondary: #26a69a;
|
||||||
$accent : #8cc63f;
|
$accent: #8cc63f;
|
||||||
|
|
||||||
$dark : #1D1D1D;
|
$dark: #1d1d1d;
|
||||||
$dark-page : #121212;
|
$dark-page: #121212;
|
||||||
|
|
||||||
$positive : #21BA45;
|
$positive: #21ba45;
|
||||||
$negative : #C10015;
|
$negative: #c10015;
|
||||||
$info : #31CCEC;
|
$info: #31ccec;
|
||||||
$warning : #F2C037;
|
$warning: #f2c037;
|
||||||
|
|
||||||
// Width
|
// Width
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,24 @@
|
||||||
|
|
||||||
%margin-auto {
|
%margin-auto {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
.vn-w-xs {
|
.vn-w-xs {
|
||||||
@extend %margin-auto;
|
@extend %margin-auto;
|
||||||
max-width: $width-xs;
|
max-width: $width-xs;
|
||||||
}
|
}
|
||||||
.vn-w-sm {
|
.vn-w-sm {
|
||||||
@extend %margin-auto;
|
@extend %margin-auto;
|
||||||
max-width: $width-sm;
|
max-width: $width-sm;
|
||||||
}
|
}
|
||||||
.vn-w-md {
|
.vn-w-md {
|
||||||
@extend %margin-auto;
|
@extend %margin-auto;
|
||||||
max-width: $width-md;
|
max-width: $width-md;
|
||||||
}
|
}
|
||||||
.vn-w-lg {
|
.vn-w-lg {
|
||||||
@extend %margin-auto;
|
@extend %margin-auto;
|
||||||
max-width: $width-lg;
|
max-width: $width-lg;
|
||||||
}
|
}
|
||||||
.vn-w-xl {
|
.vn-w-xl {
|
||||||
@extend %margin-auto;
|
@extend %margin-auto;
|
||||||
max-width: $width-xl;
|
max-width: $width-xl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,84 +2,76 @@
|
||||||
// so you can safely delete all default props below
|
// so you can safely delete all default props below
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
failed: 'Action failed',
|
failed: 'Action failed',
|
||||||
success: 'Action was successful',
|
success: 'Action was successful',
|
||||||
internalServerError: 'Internal server error',
|
internalServerError: 'Internal server error',
|
||||||
somethingWentWrong: 'Something went wrong',
|
somethingWentWrong: 'Something went wrong',
|
||||||
loginFailed: 'Login failed',
|
loginFailed: 'Login failed',
|
||||||
authenticationRequired: 'Authentication required',
|
authenticationRequired: 'Authentication required',
|
||||||
notFound: 'Not found',
|
notFound: 'Not found',
|
||||||
today: 'Today',
|
today: 'Today',
|
||||||
yesterday: 'Yesterday',
|
yesterday: 'Yesterday',
|
||||||
tomorrow: 'Tomorrow',
|
tomorrow: 'Tomorrow',
|
||||||
date: {
|
date: {
|
||||||
days: [
|
days: [
|
||||||
'Sunday',
|
'Sunday',
|
||||||
'Monday',
|
'Monday',
|
||||||
'Tuesday',
|
'Tuesday',
|
||||||
'Wednesday',
|
'Wednesday',
|
||||||
'Thursday',
|
'Thursday',
|
||||||
'Friday',
|
'Friday',
|
||||||
'Saturday'
|
'Saturday'
|
||||||
],
|
],
|
||||||
daysShort: [
|
daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||||
'Sun',
|
months: [
|
||||||
'Mon',
|
'January',
|
||||||
'Tue',
|
'February',
|
||||||
'Wed',
|
'March',
|
||||||
'Thu',
|
'April',
|
||||||
'Fri',
|
'May',
|
||||||
'Sat'
|
'June',
|
||||||
],
|
'July',
|
||||||
months: [
|
'August',
|
||||||
'January',
|
'September',
|
||||||
'February',
|
'October',
|
||||||
'March',
|
'November',
|
||||||
'April',
|
'December'
|
||||||
'May',
|
],
|
||||||
'June',
|
shortMonths: [
|
||||||
'July',
|
'Jan',
|
||||||
'August',
|
'Feb',
|
||||||
'September',
|
'Mar',
|
||||||
'October',
|
'Apr',
|
||||||
'November',
|
'May',
|
||||||
'December'
|
'Jun',
|
||||||
],
|
'Jul',
|
||||||
shortMonths: [
|
'Aug',
|
||||||
'Jan',
|
'Sep',
|
||||||
'Feb',
|
'Oct',
|
||||||
'Mar',
|
'Nov',
|
||||||
'Apr',
|
'Dec'
|
||||||
'May',
|
]
|
||||||
'Jun',
|
},
|
||||||
'Jul',
|
|
||||||
'Aug',
|
|
||||||
'Sep',
|
|
||||||
'Oct',
|
|
||||||
'Nov',
|
|
||||||
'Dec'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
// menu
|
// menu
|
||||||
home: 'Home',
|
home: 'Home',
|
||||||
catalog: 'Catalog',
|
catalog: 'Catalog',
|
||||||
orders: 'Orders',
|
orders: 'Orders',
|
||||||
order: 'Pending order',
|
order: 'Pending order',
|
||||||
ticket: 'Order',
|
ticket: 'Order',
|
||||||
conditions: 'Conditions',
|
conditions: 'Conditions',
|
||||||
about: 'About us',
|
about: 'About us',
|
||||||
admin: 'Administration',
|
admin: 'Administration',
|
||||||
panel: 'Control panel',
|
panel: 'Control panel',
|
||||||
users: 'Users',
|
users: 'Users',
|
||||||
connections: 'Connections',
|
connections: 'Connections',
|
||||||
visits: 'Visits',
|
visits: 'Visits',
|
||||||
news: 'News',
|
news: 'News',
|
||||||
newEdit: 'Edit new',
|
newEdit: 'Edit new',
|
||||||
images: 'Images',
|
images: 'Images',
|
||||||
items: 'Items',
|
items: 'Items',
|
||||||
config: 'Configuration',
|
config: 'Configuration',
|
||||||
user: 'User',
|
user: 'User',
|
||||||
addresses: 'Addresses',
|
addresses: 'Addresses',
|
||||||
addressEdit: 'Edit address'
|
addressEdit: 'Edit address'
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,84 +2,76 @@
|
||||||
// so you can safely delete all default props below
|
// so you can safely delete all default props below
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
failed: 'Acción fallida',
|
failed: 'Acción fallida',
|
||||||
success: 'Acción exitosa',
|
success: 'Acción exitosa',
|
||||||
internalServerError: 'Error interno del servidor',
|
internalServerError: 'Error interno del servidor',
|
||||||
somethingWentWrong: 'Algo salió mal',
|
somethingWentWrong: 'Algo salió mal',
|
||||||
loginFailed: 'Usuario o contraseña incorrectos',
|
loginFailed: 'Usuario o contraseña incorrectos',
|
||||||
authenticationRequired: 'Autenticación requerida',
|
authenticationRequired: 'Autenticación requerida',
|
||||||
notFound: 'No encontrado',
|
notFound: 'No encontrado',
|
||||||
today: 'Hoy',
|
today: 'Hoy',
|
||||||
yesterday: 'Ayer',
|
yesterday: 'Ayer',
|
||||||
tomorrow: 'Mañana',
|
tomorrow: 'Mañana',
|
||||||
date: {
|
date: {
|
||||||
days: [
|
days: [
|
||||||
'Domingo',
|
'Domingo',
|
||||||
'Lunes',
|
'Lunes',
|
||||||
'Martes',
|
'Martes',
|
||||||
'Miércoles',
|
'Miércoles',
|
||||||
'Jueves',
|
'Jueves',
|
||||||
'Viernes',
|
'Viernes',
|
||||||
'Sábado'
|
'Sábado'
|
||||||
],
|
],
|
||||||
daysShort: [
|
daysShort: ['Do', 'Lu', 'Mi', 'Mi', 'Ju', 'Vi', 'Sa'],
|
||||||
'Do',
|
months: [
|
||||||
'Lu',
|
'Enero',
|
||||||
'Mi',
|
'Febrero',
|
||||||
'Mi',
|
'Marzo',
|
||||||
'Ju',
|
'Abril',
|
||||||
'Vi',
|
'Mayo',
|
||||||
'Sa'
|
'Junio',
|
||||||
],
|
'Julio',
|
||||||
months: [
|
'Agosto',
|
||||||
'Enero',
|
'Septiembre',
|
||||||
'Febrero',
|
'Octubre',
|
||||||
'Marzo',
|
'Noviembre',
|
||||||
'Abril',
|
'Diciembre'
|
||||||
'Mayo',
|
],
|
||||||
'Junio',
|
shortMonths: [
|
||||||
'Julio',
|
'Ene',
|
||||||
'Agosto',
|
'Feb',
|
||||||
'Septiembre',
|
'Mar',
|
||||||
'Octubre',
|
'Abr',
|
||||||
'Noviembre',
|
'May',
|
||||||
'Diciembre'
|
'Jun',
|
||||||
],
|
'Jul',
|
||||||
shortMonths: [
|
'Ago',
|
||||||
'Ene',
|
'Sep',
|
||||||
'Feb',
|
'Oct',
|
||||||
'Mar',
|
'Nov',
|
||||||
'Abr',
|
'Dic'
|
||||||
'May',
|
]
|
||||||
'Jun',
|
},
|
||||||
'Jul',
|
|
||||||
'Ago',
|
|
||||||
'Sep',
|
|
||||||
'Oct',
|
|
||||||
'Nov',
|
|
||||||
'Dic'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
// Menu
|
// Menu
|
||||||
home: 'Inicio',
|
home: 'Inicio',
|
||||||
catalog: 'Catálogo',
|
catalog: 'Catálogo',
|
||||||
orders: 'Pedidos',
|
orders: 'Pedidos',
|
||||||
order: 'Pedido pendiente',
|
order: 'Pedido pendiente',
|
||||||
ticket: 'Pedido',
|
ticket: 'Pedido',
|
||||||
conditions: 'Condiciones',
|
conditions: 'Condiciones',
|
||||||
about: 'Sobre nosotros',
|
about: 'Sobre nosotros',
|
||||||
admin: 'Administración',
|
admin: 'Administración',
|
||||||
panel: 'Panel de control',
|
panel: 'Panel de control',
|
||||||
users: 'Usuarios',
|
users: 'Usuarios',
|
||||||
connections: 'Conexiones',
|
connections: 'Conexiones',
|
||||||
visits: 'Visitas',
|
visits: 'Visitas',
|
||||||
news: 'Noticias',
|
news: 'Noticias',
|
||||||
newEdit: 'Editar noticia',
|
newEdit: 'Editar noticia',
|
||||||
images: 'Imágenes',
|
images: 'Imágenes',
|
||||||
items: 'Artículos',
|
items: 'Artículos',
|
||||||
config: 'Configuración',
|
config: 'Configuración',
|
||||||
user: 'Usuario',
|
user: 'Usuario',
|
||||||
addresses: 'Direcciones',
|
addresses: 'Direcciones',
|
||||||
addressEdit: 'Editar dirección'
|
addressEdit: 'Editar dirección'
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,6 @@ import enUS from './en-US'
|
||||||
import esES from './es-ES'
|
import esES from './es-ES'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
'en-US': enUS,
|
'en-US': enUS,
|
||||||
'es-ES': esES
|
'es-ES': esES
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,163 +12,173 @@ import { ResultSet } from './result-set'
|
||||||
* the user can send any statement to the server. For example: DROP DATABASE
|
* the user can send any statement to the server. For example: DROP DATABASE
|
||||||
*/
|
*/
|
||||||
const Flag = {
|
const Flag = {
|
||||||
NOT_NULL: 1,
|
NOT_NULL: 1,
|
||||||
PRI_KEY: 2,
|
PRI_KEY: 2,
|
||||||
AI: 512 | 2 | 1
|
AI: 512 | 2 | 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const Type = {
|
const Type = {
|
||||||
BOOLEAN: 1,
|
BOOLEAN: 1,
|
||||||
INTEGER: 3,
|
INTEGER: 3,
|
||||||
DOUBLE: 4,
|
DOUBLE: 4,
|
||||||
STRING: 5,
|
STRING: 5,
|
||||||
DATE: 8,
|
DATE: 8,
|
||||||
DATE_TIME: 9
|
DATE_TIME: 9
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Connection extends JsonConnection {
|
export class Connection extends JsonConnection {
|
||||||
static Flag = Flag
|
static Flag = Flag
|
||||||
static Type = Type
|
static Type = Type
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs a SQL query on the database.
|
* Runs a SQL query on the database.
|
||||||
*
|
*
|
||||||
* @param {String} sql The SQL statement
|
* @param {String} sql The SQL statement
|
||||||
* @return {ResultSet} The result
|
* @return {ResultSet} The result
|
||||||
*/
|
*/
|
||||||
async execSql (sql) {
|
async execSql (sql) {
|
||||||
const json = await this.send('core/query', { sql })
|
const json = await this.send('core/query', { sql })
|
||||||
const results = []
|
const results = []
|
||||||
let err
|
let err
|
||||||
|
|
||||||
if (json) {
|
if (json) {
|
||||||
try {
|
try {
|
||||||
if (json && json instanceof Array) {
|
if (json && json instanceof Array) {
|
||||||
for (let i = 0; i < json.length; i++) {
|
for (let i = 0; i < json.length; i++) {
|
||||||
if (json[i] !== true) {
|
if (json[i] !== true) {
|
||||||
const rows = json[i].data
|
const rows = json[i].data
|
||||||
const columns = json[i].columns
|
const columns = json[i].columns
|
||||||
|
|
||||||
const data = new Array(rows.length)
|
const data = new Array(rows.length)
|
||||||
results.push({
|
results.push({
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
tables: json[i].tables
|
tables: json[i].tables
|
||||||
})
|
})
|
||||||
|
|
||||||
for (let j = 0; j < rows.length; j++) {
|
for (let j = 0; j < rows.length; j++) {
|
||||||
const row = data[j] = {}
|
const row = (data[j] = {})
|
||||||
for (let k = 0; k < columns.length; k++) { row[columns[k].name] = rows[j][k] }
|
for (let k = 0; k < columns.length; k++) {
|
||||||
}
|
row[columns[k].name] = rows[j][k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (let j = 0; j < columns.length; j++) {
|
for (let j = 0; j < columns.length; j++) {
|
||||||
let castFunc = null
|
let castFunc = null
|
||||||
const col = columns[j]
|
const col = columns[j]
|
||||||
|
|
||||||
switch (col.type) {
|
switch (col.type) {
|
||||||
case Type.DATE:
|
case Type.DATE:
|
||||||
case Type.DATE_TIME:
|
case Type.DATE_TIME:
|
||||||
case Type.TIMESTAMP:
|
case Type.TIMESTAMP:
|
||||||
castFunc = this.valueToDate
|
castFunc = this.valueToDate
|
||||||
break
|
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) {
|
||||||
if (castFunc !== null) {
|
err = e
|
||||||
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)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ResultSet(results, err)
|
/**
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs a query on the database.
|
* Runs a query on the database.
|
||||||
*
|
*
|
||||||
* @param {String} query The SQL statement
|
* @param {String} query The SQL statement
|
||||||
* @param {Object} params The query params
|
* @param {Object} params The query params
|
||||||
* @return {ResultSet} The result
|
* @return {ResultSet} The result
|
||||||
*/
|
*/
|
||||||
async execQuery (query, params) {
|
async execQuery (query, params) {
|
||||||
const sql = query.replace(/#\w+/g, key => {
|
const sql = query.replace(/#\w+/g, (key) => {
|
||||||
const value = params[key.substring(1)]
|
const value = params[key.substring(1)]
|
||||||
return value ? this.renderValue(value) : key
|
return value ? this.renderValue(value) : key
|
||||||
})
|
})
|
||||||
|
|
||||||
return await this.execSql(sql)
|
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' }
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
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.
|
* Parses a value to date.
|
||||||
*/
|
*/
|
||||||
valueToDate (value) {
|
valueToDate (value) {
|
||||||
return fixTz(new Date(value))
|
return fixTz(new Date(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Read time zone from db configuration
|
// TODO: Read time zone from db configuration
|
||||||
const tz = { timeZone: 'Europe/Madrid' }
|
const tz = { timeZone: 'Europe/Madrid' }
|
||||||
const isLocal = Intl
|
const isLocal = Intl.DateTimeFormat().resolvedOptions().timeZone === tz.timeZone
|
||||||
.DateTimeFormat()
|
|
||||||
.resolvedOptions()
|
|
||||||
.timeZone === tz.timeZone
|
|
||||||
|
|
||||||
function fixTz (date) {
|
function fixTz (date) {
|
||||||
if (isLocal) return date
|
if (isLocal) return date
|
||||||
|
|
||||||
const localDate = new Date(date.toLocaleString('en-US', tz))
|
const localDate = new Date(date.toLocaleString('en-US', tz))
|
||||||
const hasTime = localDate.getHours() ||
|
const hasTime =
|
||||||
|
localDate.getHours() ||
|
||||||
localDate.getMinutes() ||
|
localDate.getMinutes() ||
|
||||||
localDate.getSeconds() ||
|
localDate.getSeconds() ||
|
||||||
localDate.getMilliseconds()
|
localDate.getMilliseconds()
|
||||||
|
|
||||||
if (!hasTime) {
|
if (!hasTime) {
|
||||||
date.setHours(date.getHours() + 12)
|
date.setHours(date.getHours() + 12)
|
||||||
date.setHours(0, 0, 0, 0)
|
date.setHours(0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return date
|
return date
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,121 +1,130 @@
|
||||||
|
|
||||||
import { Result } from './result'
|
import { Result } from './result'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class stores the database results.
|
* This class stores the database results.
|
||||||
*/
|
*/
|
||||||
export class ResultSet {
|
export class ResultSet {
|
||||||
results = null
|
results = null
|
||||||
error = null
|
error = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initilizes the resultset object.
|
* Initilizes the resultset object.
|
||||||
*/
|
*/
|
||||||
constructor (results, error) {
|
constructor (results, error) {
|
||||||
this.results = results
|
this.results = results
|
||||||
this.error = error
|
this.error = error
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the query error.
|
* Gets the query error.
|
||||||
*
|
*
|
||||||
* @return {Db.Err} the error or null if no errors hapened
|
* @return {Db.Err} the error or null if no errors hapened
|
||||||
*/
|
*/
|
||||||
getError () {
|
getError () {
|
||||||
return this.error
|
return this.error
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch () {
|
fetch () {
|
||||||
if (this.error) { throw this.error }
|
if (this.error) {
|
||||||
|
throw this.error
|
||||||
|
}
|
||||||
|
|
||||||
if (this.results !== null &&
|
if (this.results !== null && this.results.length > 0) {
|
||||||
this.results.length > 0) { return this.results.shift() }
|
return this.results.shift()
|
||||||
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetchs the next result from the resultset.
|
* Fetchs the next result from the resultset.
|
||||||
*
|
*
|
||||||
* @return {Db.Result} the result or %null if error or there are no more results
|
* @return {Db.Result} the result or %null if error or there are no more results
|
||||||
*/
|
*/
|
||||||
fetchResult () {
|
fetchResult () {
|
||||||
const result = this.fetch()
|
const result = this.fetch()
|
||||||
|
|
||||||
if (result !== null) {
|
if (result !== null) {
|
||||||
if (result.data instanceof Array) {
|
if (result.data instanceof Array) {
|
||||||
return new Result(result)
|
return new Result(result)
|
||||||
} else {
|
} else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
/**
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetchs the first row object from the next resultset.
|
* Fetchs the first row object from the next resultset.
|
||||||
*
|
*
|
||||||
* @return {Array} the row if success, %null otherwise
|
* @return {Array} the row if success, %null otherwise
|
||||||
*/
|
*/
|
||||||
fetchObject () {
|
fetchObject () {
|
||||||
const result = this.fetch()
|
const result = this.fetch()
|
||||||
|
|
||||||
if (result !== null &&
|
if (
|
||||||
result.data instanceof Array &&
|
result !== null &&
|
||||||
result.data.length > 0) { return result.data[0] }
|
result.data instanceof Array &&
|
||||||
|
result.data.length > 0
|
||||||
|
) {
|
||||||
|
return result.data[0]
|
||||||
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetchs data from the next resultset.
|
* Fetchs data from the next resultset.
|
||||||
*
|
*
|
||||||
* @return {Array} the data
|
* @return {Array} the data
|
||||||
*/
|
*/
|
||||||
fetchData () {
|
fetchData () {
|
||||||
const result = this.fetch()
|
const result = this.fetch()
|
||||||
|
|
||||||
if (result !== null &&
|
if (result !== null && result.data instanceof Array) {
|
||||||
result.data instanceof Array) {
|
return result.data
|
||||||
return result.data
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
/**
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetchs the first row and column value from the next resultset.
|
* Fetchs the first row and column value from the next resultset.
|
||||||
*
|
*
|
||||||
* @return {Object} the value if success, %null otherwise
|
* @return {Object} the value if success, %null otherwise
|
||||||
*/
|
*/
|
||||||
fetchValue () {
|
fetchValue () {
|
||||||
const row = this.fetchRow()
|
const row = this.fetchRow()
|
||||||
|
|
||||||
if (row instanceof Array && row.length > 0) { return row[0] }
|
if (row instanceof Array && row.length > 0) {
|
||||||
|
return row[0]
|
||||||
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetchs the first row from the next resultset.
|
* Fetchs the first row from the next resultset.
|
||||||
*
|
*
|
||||||
* @return {Array} the row if success, %null otherwise
|
* @return {Array} the row if success, %null otherwise
|
||||||
*/
|
*/
|
||||||
fetchRow () {
|
fetchRow () {
|
||||||
const result = this.fetch()
|
const result = this.fetch()
|
||||||
|
|
||||||
if (result !== null &&
|
if (
|
||||||
result.data instanceof Array &&
|
result !== null &&
|
||||||
result.data.length > 0) {
|
result.data instanceof Array &&
|
||||||
const object = result.data[0]
|
result.data.length > 0
|
||||||
const row = new Array(result.columns.length)
|
) {
|
||||||
for (let i = 0; i < row.length; i++) {
|
const object = result.data[0]
|
||||||
row[i] = object[result.columns[i].name]
|
const row = new Array(result.columns.length)
|
||||||
}
|
for (let i = 0; i < row.length; i++) {
|
||||||
return row
|
row[i] = object[result.columns[i].name]
|
||||||
|
}
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,60 +2,64 @@
|
||||||
* This class stores a database result.
|
* This class stores a database result.
|
||||||
*/
|
*/
|
||||||
export class Result {
|
export class Result {
|
||||||
/**
|
/**
|
||||||
* Initilizes the result object.
|
* Initilizes the result object.
|
||||||
*/
|
*/
|
||||||
constructor (result) {
|
constructor (result) {
|
||||||
this.data = result.data
|
this.data = result.data
|
||||||
this.tables = result.tables
|
this.tables = result.tables
|
||||||
this.columns = result.columns
|
this.columns = result.columns
|
||||||
this.row = -1
|
this.row = -1
|
||||||
|
|
||||||
if (this.columns) {
|
if (this.columns) {
|
||||||
this.columnMap = {}
|
this.columnMap = {}
|
||||||
|
|
||||||
for (let i = 0; i < this.columns.length; i++) {
|
for (let i = 0; i < this.columns.length; i++) {
|
||||||
const col = this.columns[i]
|
const col = this.columns[i]
|
||||||
col.index = i
|
col.index = i
|
||||||
this.columnMap[col.name] = col
|
this.columnMap[col.name] = col
|
||||||
}
|
}
|
||||||
} else { this.columnMap = null }
|
} else {
|
||||||
}
|
this.columnMap = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a value from de result.
|
* Gets a value from de result.
|
||||||
*
|
*
|
||||||
* @param {String} columnName The column name
|
* @param {String} columnName The column name
|
||||||
* @return {Object} The cell value
|
* @return {Object} The cell value
|
||||||
*/
|
*/
|
||||||
get (columnName) {
|
get (columnName) {
|
||||||
return this.data[this.row][columnName]
|
return this.data[this.row][columnName]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a row.
|
* Gets a row.
|
||||||
*
|
*
|
||||||
* @return {Object} The cell value
|
* @return {Object} The cell value
|
||||||
*/
|
*/
|
||||||
getObject () {
|
getObject () {
|
||||||
return this.data[this.row]
|
return this.data[this.row]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the result iterator.
|
* Resets the result iterator.
|
||||||
*/
|
*/
|
||||||
reset () {
|
reset () {
|
||||||
this.row = -1
|
this.row = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the internal iterator to the next row.
|
* Moves the internal iterator to the next row.
|
||||||
*/
|
*/
|
||||||
next () {
|
next () {
|
||||||
this.row++
|
this.row++
|
||||||
|
|
||||||
if (this.row >= this.data.length) { return false }
|
if (this.row >= this.data.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import { VnObject } from './object'
|
import { VnObject } from './object'
|
||||||
import { JsonException } from './json-exception'
|
import { JsonException } from './json-exception'
|
||||||
|
|
||||||
|
@ -6,16 +5,16 @@ import { JsonException } from './json-exception'
|
||||||
* Handler for JSON rest connections.
|
* Handler for JSON rest connections.
|
||||||
*/
|
*/
|
||||||
export class JsonConnection extends VnObject {
|
export class JsonConnection extends VnObject {
|
||||||
_connected = false
|
_connected = false
|
||||||
_requestsCount = 0
|
_requestsCount = 0
|
||||||
token = null
|
token = null
|
||||||
interceptors = []
|
interceptors = []
|
||||||
|
|
||||||
use (fn) {
|
use (fn) {
|
||||||
this.interceptors.push(fn)
|
this.interceptors.push(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the specified REST service with the given params and calls
|
* Executes the specified REST service with the given params and calls
|
||||||
* the callback when response is received.
|
* the callback when response is received.
|
||||||
*
|
*
|
||||||
|
@ -23,164 +22,179 @@ export class JsonConnection extends VnObject {
|
||||||
* @param {Object} params The params to pass to the service
|
* @param {Object} params The params to pass to the service
|
||||||
* @return {Object} The parsed JSON response
|
* @return {Object} The parsed JSON response
|
||||||
*/
|
*/
|
||||||
async send (url, params) {
|
async send (url, params) {
|
||||||
if (!params) params = {}
|
if (!params) params = {}
|
||||||
params.srv = `json:${url}`
|
params.srv = `json:${url}`
|
||||||
return this.sendWithUrl('POST', '.', params)
|
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 sendForm (form) {
|
||||||
}
|
const params = {}
|
||||||
|
const elements = form.elements
|
||||||
|
|
||||||
async sendFormMultipart (form) {
|
for (let i = 0; i < elements.length; i++) {
|
||||||
return this.request({
|
if (elements[i].name) {
|
||||||
method: 'POST',
|
params[elements[i].name] = elements[i].value
|
||||||
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()
|
|
||||||
for (const key in params) {
|
|
||||||
if (params[key] != null) {
|
|
||||||
urlParams.set(key, params[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
for (const fn of this.interceptors) {
|
|
||||||
config = fn(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 err = new JsonException()
|
|
||||||
err.statusCode = request.status
|
|
||||||
|
|
||||||
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
|
|
||||||
} else {
|
|
||||||
err.message = request.statusText
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw err
|
return this.sendWithUrl('POST', form.action, params)
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
data = null
|
|
||||||
error = e
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
async sendFormMultipart (form) {
|
||||||
this.emit('error', error)
|
return this.request({
|
||||||
reject(error)
|
method: 'POST',
|
||||||
} else { resolve(data) }
|
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()
|
||||||
|
for (const key in params) {
|
||||||
|
if (params[key] != null) {
|
||||||
|
urlParams.set(key, params[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
for (const fn of this.interceptors) {
|
||||||
|
config = fn(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 err = new JsonException()
|
||||||
|
err.statusCode = request.status
|
||||||
|
|
||||||
|
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
|
||||||
|
} else {
|
||||||
|
err.message = request.statusText
|
||||||
|
}
|
||||||
|
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
data = null
|
||||||
|
error = e
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
this.emit('error', error)
|
||||||
|
reject(error)
|
||||||
|
} else {
|
||||||
|
resolve(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
* This class stores the database errors.
|
* This class stores the database errors.
|
||||||
*/
|
*/
|
||||||
export class JsonException {
|
export class JsonException {
|
||||||
constructor (exception, message, code, file, line, trace, statucCode) {
|
constructor (exception, message, code, file, line, trace, statucCode) {
|
||||||
this.name = 'JsonException'
|
this.name = 'JsonException'
|
||||||
this.exception = exception
|
this.exception = exception
|
||||||
this.message = message
|
this.message = message
|
||||||
this.code = code
|
this.code = code
|
||||||
this.file = file
|
this.file = file
|
||||||
this.line = line
|
this.line = line
|
||||||
this.trace = trace
|
this.trace = trace
|
||||||
this.statusCode = statucCode
|
this.statusCode = statucCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,248 +1,289 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main base class. Manages the signal system. Objects based on this class
|
* The main base class. Manages the signal system. Objects based on this class
|
||||||
* can be instantiated declaratively using XML.
|
* can be instantiated declaratively using XML.
|
||||||
*/
|
*/
|
||||||
export class VnObject {
|
export class VnObject {
|
||||||
/**
|
/**
|
||||||
* Tag to be used when the class instance is defined via XML. All classes
|
* Tag to be used when the class instance is defined via XML. All classes
|
||||||
* must define this attribute, even if it is not used.
|
* must define this attribute, even if it is not used.
|
||||||
*/
|
*/
|
||||||
static Tag = 'vn-object'
|
static Tag = 'vn-object'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class public properties.
|
* Class public properties.
|
||||||
*/
|
*/
|
||||||
static Properties = {}
|
static Properties = {}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reference count.
|
* Reference count.
|
||||||
*/
|
*/
|
||||||
_refCount = 1
|
_refCount = 1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Signal handlers data.
|
* Signal handlers data.
|
||||||
*/
|
*/
|
||||||
_thisArg = null
|
_thisArg = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the object and sets all properties passed to the class
|
* Initializes the object and sets all properties passed to the class
|
||||||
* constructor.
|
* constructor.
|
||||||
*
|
*
|
||||||
* @param {Object} props The properties passed to the contructor
|
* @param {Object} props The properties passed to the contructor
|
||||||
*/
|
*/
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
this.setProperties(props)
|
this.setProperties(props)
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize (props) {
|
initialize (props) {
|
||||||
this.setProperties(props)
|
this.setProperties(props)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a group of object properties.
|
* Sets a group of object properties.
|
||||||
*
|
*
|
||||||
* @param {Object} props Properties
|
* @param {Object} props Properties
|
||||||
*/
|
*/
|
||||||
setProperties (props) {
|
setProperties (props) {
|
||||||
for (const prop in props) { this[prop] = props[prop] }
|
for (const prop in props) {
|
||||||
}
|
this[prop] = props[prop]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increases the object reference count.
|
* Increases the object reference count.
|
||||||
*/
|
*/
|
||||||
ref () {
|
ref () {
|
||||||
this._refCount++
|
this._refCount++
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decreases the object reference count.
|
* Decreases the object reference count.
|
||||||
*/
|
*/
|
||||||
unref () {
|
unref () {
|
||||||
this._refCount--
|
this._refCount--
|
||||||
|
|
||||||
if (this._refCount === 0) { this._destroy() }
|
if (this._refCount === 0) {
|
||||||
}
|
this._destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from @Vn.Builder when it finds a custom tag as a child of the
|
* Called from @Vn.Builder when it finds a custom tag as a child of the
|
||||||
* element.
|
* element.
|
||||||
*
|
*
|
||||||
* @param {Vn.Scope} scope The scope instance
|
* @param {Vn.Scope} scope The scope instance
|
||||||
* @param {Node} node The custom tag child nodes
|
* @param {Node} node The custom tag child nodes
|
||||||
*/
|
*/
|
||||||
loadXml () {}
|
loadXml () {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from @Vn.Builder when it finds a a child tag that isn't
|
* Called from @Vn.Builder when it finds a a child tag that isn't
|
||||||
* associated to any property.
|
* associated to any property.
|
||||||
*
|
*
|
||||||
* @param {Object} child The child object instance
|
* @param {Object} child The child object instance
|
||||||
*/
|
*/
|
||||||
appendChild () {}
|
appendChild () {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conects a signal with a function.
|
* Conects a signal with a function.
|
||||||
*
|
*
|
||||||
* @param {string} id The signal identifier
|
* @param {string} id The signal identifier
|
||||||
* @param {function} callback The callback
|
* @param {function} callback The callback
|
||||||
* @param {Object} instance The instance
|
* @param {Object} instance The instance
|
||||||
*/
|
*/
|
||||||
on (id, callback, instance) {
|
on (id, callback, instance) {
|
||||||
if (!(callback instanceof Function)) {
|
if (!(callback instanceof Function)) {
|
||||||
console.warn('Vn.Object: Invalid callback for signal \'%s\'', id)
|
console.warn("Vn.Object: Invalid callback for signal '%s'", id)
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this._signalInit()
|
||||||
|
let callbacks = this._thisArg.signals[id]
|
||||||
|
|
||||||
|
if (!callbacks) {
|
||||||
|
callbacks = this._thisArg.signals[id] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks.push({
|
||||||
|
blocked: false,
|
||||||
|
callback,
|
||||||
|
instance
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
* Locks/Unlocks a signal emission to the specified object.
|
||||||
*
|
*
|
||||||
* @param {string} id The signal identifier
|
* @param {string} id The signal identifier
|
||||||
* @param {function} callback The callback
|
* @param {function} callback The callback
|
||||||
* @param {boolean} block %true for lock the signal, %false for unlock
|
* @param {boolean} block %true for lock the signal, %false for unlock
|
||||||
*/
|
*/
|
||||||
blockSignal (id, callback, block, instance) {
|
blockSignal (id, callback, block, instance) {
|
||||||
if (!this._thisArg) { return }
|
if (!this._thisArg) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const callbacks = this._thisArg.signals[id]
|
const callbacks = this._thisArg.signals[id]
|
||||||
|
|
||||||
if (!callbacks) { return }
|
if (!callbacks) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < callbacks.length; i++) {
|
for (let i = 0; i < callbacks.length; i++) {
|
||||||
if (callbacks[i].callback === callback &&
|
if (
|
||||||
callbacks[i].instance === instance) { callbacks[i].blocked = block }
|
callbacks[i].callback === callback &&
|
||||||
|
callbacks[i].instance === instance
|
||||||
|
) {
|
||||||
|
callbacks[i].blocked = block
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits a signal in the object.
|
* Emits a signal in the object.
|
||||||
*
|
*
|
||||||
* @param {string} id The signal identifier
|
* @param {string} id The signal identifier
|
||||||
*/
|
*/
|
||||||
emit (id) {
|
emit (id) {
|
||||||
if (!this._thisArg) { return }
|
if (!this._thisArg) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const callbacks = this._thisArg.signals[id]
|
const callbacks = this._thisArg.signals[id]
|
||||||
|
|
||||||
if (!callbacks) { return }
|
if (!callbacks) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const callbackArgs = []
|
const callbackArgs = []
|
||||||
callbackArgs.push(this)
|
callbackArgs.push(this)
|
||||||
|
|
||||||
for (let i = 1; i < arguments.length; i++) { callbackArgs.push(arguments[i]) }
|
for (let i = 1; i < arguments.length; i++) {
|
||||||
|
callbackArgs.push(arguments[i])
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < callbacks.length; i++) {
|
for (let i = 0; i < callbacks.length; i++) {
|
||||||
if (!callbacks[i].blocked) { callbacks[i].callback.apply(callbacks[i].instance, callbackArgs) }
|
if (!callbacks[i].blocked) {
|
||||||
|
callbacks[i].callback.apply(callbacks[i].instance, callbackArgs)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects a signal from current object.
|
* Disconnects a signal from current object.
|
||||||
*
|
*
|
||||||
* @param {string} id The signal identifier
|
* @param {string} id The signal identifier
|
||||||
* @param {function} callback The connected callback
|
* @param {function} callback The connected callback
|
||||||
* @param {Object} instance The instance
|
* @param {Object} instance The instance
|
||||||
*/
|
*/
|
||||||
disconnect (id, callback, instance) {
|
disconnect (id, callback, instance) {
|
||||||
if (!this._thisArg) { return }
|
if (!this._thisArg) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const callbacks = this._thisArg.signals[id]
|
const callbacks = this._thisArg.signals[id]
|
||||||
|
|
||||||
if (callbacks) {
|
if (callbacks) {
|
||||||
for (let i = callbacks.length; i--;) {
|
for (let i = callbacks.length; i--;) {
|
||||||
if (callbacks[i].callback === callback &&
|
if (
|
||||||
callbacks[i].instance === instance) { callbacks.splice(i, 1) }
|
callbacks[i].callback === callback &&
|
||||||
}
|
callbacks[i].instance === instance
|
||||||
|
) {
|
||||||
|
callbacks.splice(i, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects all signals for the given instance.
|
* Disconnects all signals for the given instance.
|
||||||
*
|
*
|
||||||
* @param {Object} instance The instance
|
* @param {Object} instance The instance
|
||||||
*/
|
*/
|
||||||
disconnectByInstance (instance) {
|
disconnectByInstance (instance) {
|
||||||
if (!this._thisArg) { return }
|
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) }
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
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
|
* Destroys the object, this method should only be called before losing
|
||||||
* the last reference to the object. It can be overwritten by child classes
|
* the last reference to the object. It can be overwritten by child classes
|
||||||
* but should always call the parent method.
|
* but should always call the parent method.
|
||||||
*/
|
*/
|
||||||
_destroy () {
|
_destroy () {
|
||||||
if (!this._thisArg) { return }
|
if (!this._thisArg) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const links = this._thisArg.links
|
const links = this._thisArg.links
|
||||||
|
|
||||||
for (const key in links) { this._unlink(links[key]) }
|
for (const key in links) {
|
||||||
|
this._unlink(links[key])
|
||||||
|
}
|
||||||
|
|
||||||
this._thisArg = null
|
this._thisArg = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Links the object with another object.
|
* Links the object with another object.
|
||||||
*
|
*
|
||||||
* @param {Object} prop The linked property
|
* @param {Object} prop The linked property
|
||||||
* @param {Object} handlers The object events to listen with
|
* @param {Object} handlers The object events to listen with
|
||||||
*/
|
*/
|
||||||
link (prop, handlers) {
|
link (prop, handlers) {
|
||||||
this._signalInit()
|
this._signalInit()
|
||||||
const links = this._thisArg.links
|
const links = this._thisArg.links
|
||||||
|
|
||||||
for (const key in prop) {
|
for (const key in prop) {
|
||||||
const newObject = prop[key]
|
const newObject = prop[key]
|
||||||
const oldObject = this[key]
|
const oldObject = this[key]
|
||||||
|
|
||||||
if (oldObject) { this._unlink(oldObject) }
|
if (oldObject) {
|
||||||
|
this._unlink(oldObject)
|
||||||
|
}
|
||||||
|
|
||||||
this[key] = newObject
|
this[key] = newObject
|
||||||
|
|
||||||
if (newObject) {
|
if (newObject) {
|
||||||
links[key] = newObject.ref()
|
links[key] = newObject.ref()
|
||||||
|
|
||||||
for (const signal in handlers) { newObject.on(signal, handlers[signal], this) }
|
for (const signal in handlers) {
|
||||||
} else if (oldObject) { links[key] = undefined }
|
newObject.on(signal, handlers[signal], this)
|
||||||
|
}
|
||||||
|
} else if (oldObject) {
|
||||||
|
links[key] = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_unlink (object) {
|
_unlink (object) {
|
||||||
if (!object) return
|
if (!object) return
|
||||||
object.disconnectByInstance(this)
|
object.disconnectByInstance(this)
|
||||||
object.unref()
|
object.unref()
|
||||||
}
|
}
|
||||||
|
|
||||||
_signalInit () {
|
_signalInit () {
|
||||||
if (!this._thisArg) {
|
if (!this._thisArg) {
|
||||||
this._thisArg = {
|
this._thisArg = {
|
||||||
signals: {},
|
signals: {},
|
||||||
links: {}
|
links: {}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,34 @@
|
||||||
<template>
|
<template>
|
||||||
<q-layout id="bg" class="fullscreen row justify-center items-center layout-view scroll">
|
<QLayout
|
||||||
<div class="column q-pa-md row items-center justify-center">
|
id="bg"
|
||||||
<router-view v-slot="{ Component }">
|
class="fullscreen row justify-center items-center layout-view scroll"
|
||||||
<transition>
|
>
|
||||||
<component :is="Component" />
|
<div class="column q-pa-md row items-center justify-center">
|
||||||
</transition>
|
<router-view v-slot="{ Component }">
|
||||||
</router-view>
|
<transition>
|
||||||
</div>
|
<component :is="Component" />
|
||||||
</q-layout>
|
</transition>
|
||||||
|
</router-view>
|
||||||
|
</div>
|
||||||
|
</QLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
#bg {
|
#bg {
|
||||||
background: white;
|
background: white;
|
||||||
}
|
}
|
||||||
.column {
|
.column {
|
||||||
width: 270px;
|
width: 270px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'LoginLayout'
|
name: 'LoginLayout'
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,241 +1,239 @@
|
||||||
<template>
|
<template>
|
||||||
<q-layout view="lHh Lpr lFf">
|
<QLayout view="lHh Lpr lFf">
|
||||||
<q-header reveal>
|
<QHeader>
|
||||||
<q-toolbar>
|
<QToolbar>
|
||||||
<q-btn
|
<QBtn
|
||||||
flat
|
flat
|
||||||
dense
|
dense
|
||||||
round
|
round
|
||||||
icon="menu"
|
icon="menu"
|
||||||
aria-label="Menu"
|
aria-label="Menu"
|
||||||
@click="toggleLeftDrawer"/>
|
@click="toggleLeftDrawer"
|
||||||
<q-toolbar-title>
|
/>
|
||||||
{{$app.title}}
|
<QToolbarTitle>
|
||||||
<div
|
{{ $app.title }}
|
||||||
v-if="$app.subtitle"
|
<div v-if="$app.subtitle" class="subtitle text-caption">
|
||||||
class="subtitle text-caption">
|
{{ $app.subtitle }}
|
||||||
{{$app.subtitle}}
|
</div>
|
||||||
</div>
|
</QToolbarTitle>
|
||||||
</q-toolbar-title>
|
<div id="actions" ref="actions"></div>
|
||||||
<div id="actions" ref="actions">
|
<QBtn
|
||||||
</div>
|
v-if="$app.useRightDrawer"
|
||||||
<q-btn
|
@click="$app.rightDrawerOpen = !$app.rightDrawerOpen"
|
||||||
v-if="$app.useRightDrawer"
|
aria-label="Menu"
|
||||||
@click="$app.rightDrawerOpen = !$app.rightDrawerOpen"
|
flat
|
||||||
aria-label="Menu"
|
dense
|
||||||
flat
|
round
|
||||||
dense
|
>
|
||||||
round>
|
<QIcon name="menu" />
|
||||||
<q-icon name="menu"/>
|
</QBtn>
|
||||||
</q-btn>
|
</QToolbar>
|
||||||
</q-toolbar>
|
</QHeader>
|
||||||
</q-header>
|
<QDrawer v-model="leftDrawerOpen" :width="250" show-if-above>
|
||||||
<q-drawer
|
<QToolbar class="logo">
|
||||||
v-model="leftDrawerOpen"
|
<img src="statics/logo-dark.svg" />
|
||||||
:width="250"
|
</QToolbar>
|
||||||
show-if-above>
|
<div class="user-info">
|
||||||
<q-toolbar class="logo">
|
<div>
|
||||||
<img src="statics/logo-dark.svg">
|
<span id="user-name">{{ user.nickname }}</span>
|
||||||
</q-toolbar>
|
<QBtn flat icon="logout" alt="_Exit" @click="logout()" />
|
||||||
<div class="user-info">
|
</div>
|
||||||
<div>
|
<div id="supplant" class="supplant">
|
||||||
<span id="user-name">{{(user.nickname)}}</span>
|
<span id="supplanted">{{ supplantedUser }}</span>
|
||||||
<q-btn flat icon="logout" alt="_Exit" @click="logout()"/>
|
<QBtn flat icon="logout" alt="_Exit" />
|
||||||
</div>
|
</div>
|
||||||
<div id="supplant" class="supplant">
|
</div>
|
||||||
<span id="supplanted">{{supplantedUser}}</span>
|
<QList v-for="item in essentialLinks" :key="item.id">
|
||||||
<q-btn flat icon="logout" alt="_Exit"/>
|
<QItem v-if="!item.childs" :to="`/${item.path}`">
|
||||||
</div>
|
<QItemSection>
|
||||||
</div>
|
<QItemLabel>{{ item.description }}</QItemLabel>
|
||||||
<q-list
|
</QItemSection>
|
||||||
v-for="item in essentialLinks"
|
</QItem>
|
||||||
:key="item.id">
|
<QExpansionItem
|
||||||
<q-item
|
v-if="item.childs"
|
||||||
v-if="!item.childs"
|
:label="item.description"
|
||||||
:to="`/${item.path}`">
|
expand-separator
|
||||||
<q-item-section>
|
>
|
||||||
<q-item-label>{{item.description}}</q-item-label>
|
<QList>
|
||||||
</q-item-section>
|
<QItem
|
||||||
</q-item>
|
v-for="subitem in item.childs"
|
||||||
<q-expansion-item
|
:key="subitem.id"
|
||||||
v-if="item.childs"
|
:to="`/${subitem.path}`"
|
||||||
:label="item.description"
|
class="q-pl-lg"
|
||||||
expand-separator>
|
>
|
||||||
<q-list>
|
<QItemSection>
|
||||||
<q-item
|
<QItemLabel>
|
||||||
v-for="subitem in item.childs"
|
{{ subitem.description }}
|
||||||
:key="subitem.id"
|
</QItemLabel>
|
||||||
:to="`/${subitem.path}`"
|
</QItemSection>
|
||||||
class="q-pl-lg">
|
</QItem>
|
||||||
<q-item-section>
|
</QList>
|
||||||
<q-item-label>{{subitem.description}}</q-item-label>
|
</QExpansionItem>
|
||||||
</q-item-section>
|
</QList>
|
||||||
</q-item>
|
</QDrawer>
|
||||||
</q-list>
|
<QPageContainer>
|
||||||
</q-expansion-item>
|
<router-view />
|
||||||
</q-list>
|
</QPageContainer>
|
||||||
</q-drawer>
|
</QLayout>
|
||||||
<q-page-container>
|
|
||||||
<router-view />
|
|
||||||
</q-page-container>
|
|
||||||
</q-layout>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.q-toolbar {
|
.q-toolbar {
|
||||||
min-height: 64px;
|
min-height: 64px;
|
||||||
}
|
}
|
||||||
.logo {
|
.logo {
|
||||||
background-color: $primary;
|
background-color: $primary;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
& > img {
|
& > img {
|
||||||
width: 160px;
|
width: 160px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.user-info {
|
.user-info {
|
||||||
margin: 25px;
|
margin: 25px;
|
||||||
|
|
||||||
& > div {
|
& > 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;
|
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>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import "src/css/responsive";
|
@import 'src/css/responsive';
|
||||||
|
|
||||||
.q-drawer {
|
.q-drawer {
|
||||||
.q-item {
|
.q-item {
|
||||||
padding-left: 38px;
|
padding-left: 38px;
|
||||||
}
|
}
|
||||||
.q-list .q-list .q-item{
|
.q-list .q-list .q-item {
|
||||||
padding-left: 50px;
|
padding-left: 50px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.q-page-container > * {
|
.q-page-container > * {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
#actions > div {
|
#actions > div {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
@include mobile {
|
@include mobile {
|
||||||
#actions > div {
|
#actions > div {
|
||||||
.q-btn {
|
.q-btn {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
& > .q-icon {
|
& > .q-icon {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
& > .block {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
& > .block {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent, ref } from 'vue'
|
import { defineComponent, ref } from 'vue';
|
||||||
import { userStore } from 'stores/user'
|
import { userStore } from 'stores/user';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'MainLayout',
|
name: 'MainLayout',
|
||||||
props: {},
|
props: {},
|
||||||
|
|
||||||
setup () {
|
setup() {
|
||||||
const leftDrawerOpen = ref(false)
|
const leftDrawerOpen = ref(false);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user: userStore(),
|
user: userStore(),
|
||||||
supplantedUser: ref(''),
|
supplantedUser: ref(''),
|
||||||
essentialLinks: ref(null),
|
essentialLinks: ref(null),
|
||||||
leftDrawerOpen,
|
leftDrawerOpen,
|
||||||
toggleLeftDrawer () {
|
toggleLeftDrawer() {
|
||||||
leftDrawerOpen.value = !leftDrawerOpen.value
|
leftDrawerOpen.value = !leftDrawerOpen.value;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
},
|
|
||||||
|
|
||||||
async mounted () {
|
|
||||||
this.$refs.actions.appendChild(this.$actions)
|
|
||||||
await this.user.loadData()
|
|
||||||
await this.$app.loadConfig()
|
|
||||||
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 () {
|
async mounted() {
|
||||||
this.user.logout()
|
this.$refs.actions.appendChild(this.$actions);
|
||||||
this.$router.push('/login')
|
await this.user.loadData();
|
||||||
|
await this.$app.loadConfig();
|
||||||
|
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>
|
||||||
|
|
||||||
<i18n lang="yaml">
|
<i18n lang="yaml">
|
||||||
en-US:
|
en-US:
|
||||||
visitor: Visitor
|
visitor: Visitor
|
||||||
es-ES:
|
es-ES:
|
||||||
visitor: Visitante
|
visitor: Visitante
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -1,74 +1,73 @@
|
||||||
|
|
||||||
import { date as qdate, format } from 'quasar'
|
import { date as qdate, format } from 'quasar'
|
||||||
const { pad } = format
|
const { pad } = format
|
||||||
|
|
||||||
export function currency (val) {
|
export function currency (val) {
|
||||||
return typeof val === 'number' ? val.toFixed(2) + '€' : val
|
return typeof val === 'number' ? val.toFixed(2) + '€' : val
|
||||||
}
|
}
|
||||||
|
|
||||||
export function date (val, format) {
|
export function date (val, format) {
|
||||||
if (val == null) return val
|
if (val == null) return val
|
||||||
if (!(val instanceof Date)) {
|
if (!(val instanceof Date)) {
|
||||||
val = new Date(val)
|
val = new Date(val)
|
||||||
}
|
}
|
||||||
return qdate.formatDate(val, format, window.i18n.tm('date'))
|
return qdate.formatDate(val, format, window.i18n.tm('date'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function relDate (val) {
|
export function relDate (val) {
|
||||||
if (val == null) return val
|
if (val == null) return val
|
||||||
if (!(val instanceof Date)) {
|
if (!(val instanceof Date)) {
|
||||||
val = new Date(val)
|
val = new Date(val)
|
||||||
}
|
|
||||||
|
|
||||||
const dif = qdate.getDateDiff(new Date(), val, 'days')
|
|
||||||
let day
|
|
||||||
|
|
||||||
switch (dif) {
|
|
||||||
case 0:
|
|
||||||
day = 'today'
|
|
||||||
break
|
|
||||||
case 1:
|
|
||||||
day = 'yesterday'
|
|
||||||
break
|
|
||||||
case -1:
|
|
||||||
day = 'tomorrow'
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if (day) {
|
|
||||||
day = window.i18n.t(day)
|
|
||||||
} else {
|
|
||||||
if (dif > 0 && dif <= 7) {
|
|
||||||
day = qdate.formatDate(val, 'ddd', window.i18n.tm('date'))
|
|
||||||
} else {
|
|
||||||
day = qdate.formatDate(val, 'ddd, MMMM Do', window.i18n.tm('date'))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return day
|
const dif = qdate.getDateDiff(new Date(), val, 'days')
|
||||||
|
let day
|
||||||
|
|
||||||
|
switch (dif) {
|
||||||
|
case 0:
|
||||||
|
day = 'today'
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
day = 'yesterday'
|
||||||
|
break
|
||||||
|
case -1:
|
||||||
|
day = 'tomorrow'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (day) {
|
||||||
|
day = window.i18n.t(day)
|
||||||
|
} else {
|
||||||
|
if (dif > 0 && dif <= 7) {
|
||||||
|
day = qdate.formatDate(val, 'ddd', window.i18n.tm('date'))
|
||||||
|
} else {
|
||||||
|
day = qdate.formatDate(val, 'ddd, MMMM Do', window.i18n.tm('date'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return day
|
||||||
}
|
}
|
||||||
|
|
||||||
export function relTime (val) {
|
export function relTime (val) {
|
||||||
if (val == null) return val
|
if (val == null) return val
|
||||||
if (!(val instanceof Date)) {
|
if (!(val instanceof Date)) {
|
||||||
val = new Date(val)
|
val = new Date(val)
|
||||||
}
|
}
|
||||||
return relDate(val) + ' ' + qdate.formatDate(val, 'H:mm:ss')
|
return relDate(val) + ' ' + qdate.formatDate(val, 'H:mm:ss')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function elapsedTime (val) {
|
export function elapsedTime (val) {
|
||||||
if (val == null) return val
|
if (val == null) return val
|
||||||
if (!(val instanceof Date)) {
|
if (!(val instanceof Date)) {
|
||||||
val = new Date(val)
|
val = new Date(val)
|
||||||
}
|
}
|
||||||
const now = (new Date()).getTime()
|
const now = new Date().getTime()
|
||||||
val = Math.floor((now - val.getTime()) / 1000)
|
val = Math.floor((now - val.getTime()) / 1000)
|
||||||
|
|
||||||
const hours = Math.floor(val / 3600)
|
const hours = Math.floor(val / 3600)
|
||||||
val -= hours * 3600
|
val -= hours * 3600
|
||||||
const minutes = Math.floor(val / 60)
|
const minutes = Math.floor(val / 60)
|
||||||
val -= minutes * 60
|
val -= minutes * 60
|
||||||
const seconds = val
|
const seconds = val
|
||||||
|
|
||||||
return `${pad(hours, 2)}:${pad(minutes, 2)}:${pad(seconds, 2)}`
|
return `${pad(hours, 2)}:${pad(minutes, 2)}:${pad(seconds, 2)}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,80 +1,77 @@
|
||||||
<template>
|
<template>
|
||||||
<div style="padding: 0;">
|
<div style="padding: 0">
|
||||||
<div class="q-pa-sm row items-start">
|
<div class="q-pa-sm row items-start">
|
||||||
<div
|
<div class="new-card q-pa-sm" v-for="myNew in news" :key="myNew.id">
|
||||||
class="new-card q-pa-sm"
|
<QCard>
|
||||||
v-for="myNew in news"
|
<QImg :src="`${$app.imageUrl}/news/full/${myNew.image}`">
|
||||||
:key="myNew.id">
|
</QImg>
|
||||||
<q-card>
|
<QCardSection>
|
||||||
<q-img :src="`${$app.imageUrl}/news/full/${myNew.image}`">
|
<div class="text-h5">{{ myNew.title }}</div>
|
||||||
</q-img>
|
</QCardSection>
|
||||||
<q-card-section>
|
<QCardSection class="new-body">
|
||||||
<div class="text-h5">{{ myNew.title }}</div>
|
<div v-html="myNew.text" />
|
||||||
</q-card-section>
|
</QCardSection>
|
||||||
<q-card-section class="new-body">
|
</QCard>
|
||||||
<div v-html="myNew.text"/>
|
</div>
|
||||||
</q-card-section>
|
</div>
|
||||||
</q-card>
|
<QPageSticky>
|
||||||
</div>
|
<QBtn
|
||||||
|
fab
|
||||||
|
icon="add_shopping_cart"
|
||||||
|
color="accent"
|
||||||
|
to="/ecomerce/catalog"
|
||||||
|
:title="$t('startOrder')"
|
||||||
|
/>
|
||||||
|
</QPageSticky>
|
||||||
</div>
|
</div>
|
||||||
<q-page-sticky>
|
|
||||||
<q-btn
|
|
||||||
fab
|
|
||||||
icon="add_shopping_cart"
|
|
||||||
color="accent"
|
|
||||||
to="/ecomerce/catalog"
|
|
||||||
:title="$t('startOrder')"
|
|
||||||
/>
|
|
||||||
</q-page-sticky>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.new-card {
|
.new-card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
@media screen and (min-width: 800px) and (max-width: 1400px) {
|
@media screen and (min-width: 800px) and (max-width: 1400px) {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 1401px) and (max-width: 1920px) {
|
@media screen and (min-width: 1401px) and (max-width: 1920px) {
|
||||||
width: 33.33%;
|
width: 33.33%;
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 19021) {
|
@media screen and (min-width: 19021) {
|
||||||
width: 25%;
|
width: 25%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.new-body {
|
.new-body {
|
||||||
font-family: 'Open Sans';
|
font-family: 'Open Sans';
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'PageIndex',
|
name: 'PageIndex',
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
news: []
|
news: []
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
async mounted () {
|
async mounted() {
|
||||||
this.news = await this.$jApi.query(
|
this.news = await this.$jApi.query(
|
||||||
`SELECT title, text, image, id
|
`SELECT title, text, image, id
|
||||||
FROM news
|
FROM news
|
||||||
ORDER BY priority, created DESC`
|
ORDER BY priority, created DESC`
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<i18n lang="yaml">
|
<i18n lang="yaml">
|
||||||
en-US:
|
en-US:
|
||||||
startOrder: Start order
|
startOrder: Start order
|
||||||
es-ES:
|
es-ES:
|
||||||
startOrder: Empezar pedido
|
startOrder: Empezar pedido
|
||||||
ca-ES:
|
ca-ES:
|
||||||
startOrder: Començar comanda
|
startOrder: Començar comanda
|
||||||
fr-FR:
|
fr-FR:
|
||||||
startOrder: Lancer commande
|
startOrder: Lancer commande
|
||||||
pt-PT:
|
pt-PT:
|
||||||
startOrder: Comece uma encomenda
|
startOrder: Comece uma encomenda
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,161 +1,179 @@
|
||||||
<template>
|
<template>
|
||||||
<Teleport :to="$actions">
|
<Teleport :to="$actions">
|
||||||
<q-select
|
<QSelect
|
||||||
v-model="year"
|
v-model="year"
|
||||||
:options="years"
|
:options="years"
|
||||||
color="white"
|
color="white"
|
||||||
dark
|
dark
|
||||||
standout
|
standout
|
||||||
dense
|
dense
|
||||||
rounded />
|
rounded
|
||||||
</Teleport>
|
/>
|
||||||
<div class="vn-w-sm">
|
</Teleport>
|
||||||
<div
|
<div class="vn-w-sm">
|
||||||
v-if="!invoices?.length"
|
<div
|
||||||
class="text-subtitle1 text-center text-grey-7 q-pa-md">
|
v-if="!invoices?.length"
|
||||||
{{$t('noInvoicesFound')}}
|
class="text-subtitle1 text-center text-grey-7 q-pa-md"
|
||||||
|
>
|
||||||
|
{{ $t('noInvoicesFound') }}
|
||||||
|
</div>
|
||||||
|
<QCard v-if="invoices?.length">
|
||||||
|
<QTable
|
||||||
|
:columns="columns"
|
||||||
|
:pagination="pagination"
|
||||||
|
:rows="invoices"
|
||||||
|
row-key="id"
|
||||||
|
hide-header
|
||||||
|
hide-bottom
|
||||||
|
>
|
||||||
|
<template v-slot:body="props">
|
||||||
|
<QTr :props="props">
|
||||||
|
<QTd key="ref" :props="props">
|
||||||
|
{{ props.row.ref }}
|
||||||
|
</QTd>
|
||||||
|
<QTd key="issued" :props="props">
|
||||||
|
{{ date(props.row.issued, 'ddd, MMMM Do') }}
|
||||||
|
</QTd>
|
||||||
|
<QTd key="amount" :props="props">
|
||||||
|
{{ currency(props.row.amount) }}
|
||||||
|
</QTd>
|
||||||
|
<QTd key="hasPdf" :props="props">
|
||||||
|
<QBtn
|
||||||
|
v-if="props.row.hasPdf"
|
||||||
|
icon="download"
|
||||||
|
:title="$t('downloadInvoicePdf')"
|
||||||
|
:href="invoiceUrl(props.row.id)"
|
||||||
|
target="_blank"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
/>
|
||||||
|
<QIcon
|
||||||
|
v-else
|
||||||
|
name="warning"
|
||||||
|
:title="$t('notDownloadable')"
|
||||||
|
color="warning"
|
||||||
|
size="24px"
|
||||||
|
/>
|
||||||
|
</QTd>
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</QCard>
|
||||||
</div>
|
</div>
|
||||||
<q-card v-if="invoices?.length">
|
|
||||||
<q-table
|
|
||||||
:columns="columns"
|
|
||||||
:pagination="pagination"
|
|
||||||
:rows="invoices"
|
|
||||||
row-key="id"
|
|
||||||
hide-header
|
|
||||||
hide-bottom>
|
|
||||||
<template v-slot:body="props">
|
|
||||||
<q-tr :props="props">
|
|
||||||
<q-td key="ref" :props="props">
|
|
||||||
{{ props.row.ref }}
|
|
||||||
</q-td>
|
|
||||||
<q-td key="issued" :props="props">
|
|
||||||
{{ date(props.row.issued, 'ddd, MMMM Do') }}
|
|
||||||
</q-td>
|
|
||||||
<q-td key="amount" :props="props">
|
|
||||||
{{ currency(props.row.amount) }}
|
|
||||||
</q-td>
|
|
||||||
<q-td key="hasPdf" :props="props">
|
|
||||||
<q-btn
|
|
||||||
v-if="props.row.hasPdf"
|
|
||||||
icon="download"
|
|
||||||
:title="$t('downloadInvoicePdf')"
|
|
||||||
:href="invoiceUrl(props.row.id)"
|
|
||||||
target="_blank"
|
|
||||||
flat
|
|
||||||
round/>
|
|
||||||
<q-icon
|
|
||||||
v-else
|
|
||||||
name="warning"
|
|
||||||
:title="$t('notDownloadable')"
|
|
||||||
color="warning"
|
|
||||||
size="24px"/>
|
|
||||||
</q-td>
|
|
||||||
</q-tr>
|
|
||||||
</template>
|
|
||||||
</q-table>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { date, currency } from 'src/lib/filters.js'
|
import { date, currency } from 'src/lib/filters.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'OrdersPendingIndex',
|
name: 'OrdersPendingIndex',
|
||||||
data () {
|
data() {
|
||||||
const curYear = (new Date()).getFullYear()
|
const curYear = new Date().getFullYear();
|
||||||
const years = []
|
const years = [];
|
||||||
|
|
||||||
for (let year = curYear - 5; year <= curYear; year++) {
|
for (let year = curYear - 5; year <= curYear; year++) {
|
||||||
years.push(year)
|
years.push(year);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
columns: [
|
columns: [
|
||||||
{ name: 'ref', label: 'serial', field: 'ref', align: 'left' },
|
{ name: 'ref', label: 'serial', field: 'ref', align: 'left' },
|
||||||
{ name: 'issued', label: 'issued', field: 'issued', align: 'left' },
|
{
|
||||||
{ name: 'amount', label: 'amount', field: 'amount' },
|
name: 'issued',
|
||||||
{ name: 'hasPdf', label: 'download', field: 'hasPdf', align: 'center' }
|
label: 'issued',
|
||||||
],
|
field: 'issued',
|
||||||
pagination: {
|
align: 'left'
|
||||||
rowsPerPage: 0
|
},
|
||||||
},
|
{ name: 'amount', label: 'amount', field: 'amount' },
|
||||||
year: curYear,
|
{
|
||||||
years,
|
name: 'hasPdf',
|
||||||
invoices: null
|
label: 'download',
|
||||||
}
|
field: 'hasPdf',
|
||||||
},
|
align: 'center'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
pagination: {
|
||||||
|
rowsPerPage: 0
|
||||||
|
},
|
||||||
|
year: curYear,
|
||||||
|
years,
|
||||||
|
invoices: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
async mounted () {
|
async mounted() {
|
||||||
await this.loadData()
|
await this.loadData();
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
async year () {
|
async year() {
|
||||||
await this.loadData()
|
await this.loadData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
date,
|
date,
|
||||||
currency,
|
currency,
|
||||||
|
|
||||||
async loadData () {
|
async loadData() {
|
||||||
const params = {
|
const params = {
|
||||||
from: new Date(this.year, 0),
|
from: new Date(this.year, 0),
|
||||||
to: new Date(this.year, 11, 31, 23, 59, 59)
|
to: new Date(this.year, 11, 31, 23, 59, 59)
|
||||||
}
|
};
|
||||||
this._invoices = await this.$jApi.query(
|
this._invoices = await this.$jApi.query(
|
||||||
`SELECT id, ref, issued, amount, hasPdf
|
`SELECT id, ref, issued, amount, hasPdf
|
||||||
FROM myInvoice
|
FROM myInvoice
|
||||||
WHERE issued BETWEEN #from AND #to
|
WHERE issued BETWEEN #from AND #to
|
||||||
ORDER BY issued DESC
|
ORDER BY issued DESC
|
||||||
LIMIT 500`,
|
LIMIT 500`,
|
||||||
params
|
params
|
||||||
)
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
invoiceUrl (id) {
|
invoiceUrl(id) {
|
||||||
return '?' + new URLSearchParams({
|
return (
|
||||||
srv: 'rest:dms/invoice',
|
'?' +
|
||||||
invoice: id,
|
new URLSearchParams({
|
||||||
access_token: this.$user.token
|
srv: 'rest:dms/invoice',
|
||||||
}).toString()
|
invoice: id,
|
||||||
|
access_token: this.$user.token
|
||||||
|
}).toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<i18n lang="yaml">
|
<i18n lang="yaml">
|
||||||
en-US:
|
en-US:
|
||||||
noInvoicesFound: No invoices found
|
noInvoicesFound: No invoices found
|
||||||
serial: Serial
|
serial: Serial
|
||||||
issued: Date
|
issued: Date
|
||||||
amount: Import
|
amount: Import
|
||||||
downloadInvoicePdf: Download invoice PDF
|
downloadInvoicePdf: Download invoice PDF
|
||||||
notDownloadable: Not available for download, request the invoice to your salesperson
|
notDownloadable: Not available for download, request the invoice to your salesperson
|
||||||
es-ES:
|
es-ES:
|
||||||
noInvoicesFound: No se han encontrado facturas
|
noInvoicesFound: No se han encontrado facturas
|
||||||
serial: Serie
|
serial: Serie
|
||||||
issued: Fecha
|
issued: Fecha
|
||||||
amount: Importe
|
amount: Importe
|
||||||
downloadInvoicePdf: Descargar factura en PDF
|
downloadInvoicePdf: Descargar factura en PDF
|
||||||
notDownloadable: No disponible para descarga, solicita la factura a tu comercial
|
notDownloadable: No disponible para descarga, solicita la factura a tu comercial
|
||||||
ca-ES:
|
ca-ES:
|
||||||
noInvoicesFound: No s'han trobat factures
|
noInvoicesFound: No s'han trobat factures
|
||||||
serial: Sèrie
|
serial: Sèrie
|
||||||
issued: Data
|
issued: Data
|
||||||
amount: Import
|
amount: Import
|
||||||
downloadInvoicePdf: Descarregar PDF
|
downloadInvoicePdf: Descarregar PDF
|
||||||
notDownloadable: No disponible per cescarrega, sol·licita la factura al teu comercial
|
notDownloadable: No disponible per cescarrega, sol·licita la factura al teu comercial
|
||||||
fr-FR:
|
fr-FR:
|
||||||
noInvoicesFound: Aucune facture trouvée
|
noInvoicesFound: Aucune facture trouvée
|
||||||
serial: Série
|
serial: Série
|
||||||
issued: Date
|
issued: Date
|
||||||
amount: Montant
|
amount: Montant
|
||||||
downloadInvoicePdf: Télécharger le PDF
|
downloadInvoicePdf: Télécharger le PDF
|
||||||
notDownloadable: Non disponible en téléchargement, demander la facture à votre commercial
|
notDownloadable: Non disponible en téléchargement, demander la facture à votre commercial
|
||||||
pt-PT:
|
pt-PT:
|
||||||
noInvoicesFound: Nenhuma fatura encontrada
|
noInvoicesFound: Nenhuma fatura encontrada
|
||||||
serial: Serie
|
serial: Serie
|
||||||
issued: Data
|
issued: Data
|
||||||
|
|
|
@ -1,201 +1,199 @@
|
||||||
<template>
|
<template>
|
||||||
<Teleport :to="$actions">
|
<Teleport :to="$actions">
|
||||||
<div class="balance">
|
<div class="balance">
|
||||||
<span class="label">{{$t('balance')}}</span>
|
<span class="label">{{ $t('balance') }}</span>
|
||||||
<span
|
<span class="amount" :class="{ negative: debt < 0 }">
|
||||||
class="amount"
|
{{ currency(debt || 0) }}
|
||||||
:class="{negative: debt < 0}">
|
</span>
|
||||||
{{currency(debt || 0)}}
|
<QIcon
|
||||||
</span>
|
name="info"
|
||||||
<q-icon
|
:title="$t('paymentInfo')"
|
||||||
name="info"
|
class="info"
|
||||||
:title="$t('paymentInfo')"
|
size="24px"
|
||||||
class="info"
|
/>
|
||||||
size="24px"/>
|
</div>
|
||||||
|
<QBtn
|
||||||
|
icon="payments"
|
||||||
|
:label="$t('makePayment')"
|
||||||
|
@click="onPayClick()"
|
||||||
|
rounded
|
||||||
|
no-caps
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
to="/ecomerce/basket"
|
||||||
|
icon="shopping_cart"
|
||||||
|
:label="$t('shoppingCart')"
|
||||||
|
rounded
|
||||||
|
no-caps
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
|
<div class="vn-w-sm">
|
||||||
|
<div
|
||||||
|
v-if="!orders?.length"
|
||||||
|
class="text-subtitle1 text-center text-grey-7 q-pa-md"
|
||||||
|
>
|
||||||
|
{{ $t('noOrdersFound') }}
|
||||||
|
</div>
|
||||||
|
<QCard v-if="orders?.length">
|
||||||
|
<QList bordered separator padding>
|
||||||
|
<QItem
|
||||||
|
v-for="order in orders"
|
||||||
|
:key="order.id"
|
||||||
|
:to="`ticket/${order.id}`"
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
>
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>
|
||||||
|
{{ date(order.landed, 'ddd, MMMM Do') }}
|
||||||
|
</QItemLabel>
|
||||||
|
<QItemLabel caption>#{{ order.id }}</QItemLabel>
|
||||||
|
<QItemLabel caption>{{ order.nickname }}</QItemLabel>
|
||||||
|
<QItemLabel caption>{{ order.agency }}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection side top> {{ order.total }}€ </QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QCard>
|
||||||
|
<QPageSticky>
|
||||||
|
<QBtn
|
||||||
|
fab
|
||||||
|
icon="add_shopping_cart"
|
||||||
|
color="accent"
|
||||||
|
to="/ecomerce/catalog"
|
||||||
|
:title="$t('startOrder')"
|
||||||
|
/>
|
||||||
|
</QPageSticky>
|
||||||
</div>
|
</div>
|
||||||
<q-btn
|
|
||||||
icon="payments"
|
|
||||||
:label="$t('makePayment')"
|
|
||||||
@click="onPayClick()"
|
|
||||||
rounded
|
|
||||||
no-caps/>
|
|
||||||
<q-btn
|
|
||||||
to="/ecomerce/basket"
|
|
||||||
icon="shopping_cart"
|
|
||||||
:label="$t('shoppingCart')"
|
|
||||||
rounded
|
|
||||||
no-caps/>
|
|
||||||
</Teleport>
|
|
||||||
<div class="vn-w-sm">
|
|
||||||
<div
|
|
||||||
v-if="!orders?.length"
|
|
||||||
class="text-subtitle1 text-center text-grey-7 q-pa-md">
|
|
||||||
{{$t('noOrdersFound')}}
|
|
||||||
</div>
|
|
||||||
<q-card v-if="orders?.length">
|
|
||||||
<q-list bordered separator padding >
|
|
||||||
<q-item
|
|
||||||
v-for="order in orders"
|
|
||||||
:key="order.id"
|
|
||||||
:to="`ticket/${order.id}`"
|
|
||||||
clickable
|
|
||||||
v-ripple>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>
|
|
||||||
{{date(order.landed, 'ddd, MMMM Do')}}
|
|
||||||
</q-item-label>
|
|
||||||
<q-item-label caption>#{{order.id}}</q-item-label>
|
|
||||||
<q-item-label caption>{{order.nickname}}</q-item-label>
|
|
||||||
<q-item-label caption>{{order.agency}}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section side top>
|
|
||||||
{{order.total}}€
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
</q-card>
|
|
||||||
<q-page-sticky>
|
|
||||||
<q-btn
|
|
||||||
fab
|
|
||||||
icon="add_shopping_cart"
|
|
||||||
color="accent"
|
|
||||||
to="/ecomerce/catalog"
|
|
||||||
:title="$t('startOrder')"/>
|
|
||||||
</q-page-sticky>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.balance {
|
.balance {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
& > .amount {
|
& > .amount {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
|
|
||||||
&.negative {
|
&.negative {
|
||||||
background-color: #e55;
|
background-color: #e55;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
box-shadow: 0 0 5px #333;
|
box-shadow: 0 0 5px #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > .info {
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
& > .info {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { date, currency } from 'src/lib/filters.js'
|
import { date, currency } from 'src/lib/filters.js';
|
||||||
import { tpvStore } from 'stores/tpv'
|
import { tpvStore } from 'stores/tpv';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'OrdersPendingIndex',
|
name: 'OrdersPendingIndex',
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
orders: null,
|
orders: null,
|
||||||
debt: 0,
|
debt: 0,
|
||||||
tpv: tpvStore()
|
tpv: tpvStore()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
await this.tpv.check(this.$route);
|
||||||
|
|
||||||
|
this.orders = await this.$jApi.query('CALL myTicket_list(NULL, NULL)');
|
||||||
|
this.debt = await this.$jApi.getValue('SELECT -myClient_getDebt(NULL)');
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
date,
|
||||||
|
currency,
|
||||||
|
|
||||||
|
async onPayClick() {
|
||||||
|
let amount = -this.debt;
|
||||||
|
amount = amount <= 0 ? null : amount;
|
||||||
|
|
||||||
|
let defaultAmountStr = '';
|
||||||
|
if (amount !== null) {
|
||||||
|
defaultAmountStr = amount;
|
||||||
|
}
|
||||||
|
amount = prompt(this.$t('amountToPay'), defaultAmountStr);
|
||||||
|
|
||||||
|
if (amount != null) {
|
||||||
|
amount = parseFloat(amount.replace(',', '.'));
|
||||||
|
await this.tpv.pay(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
async mounted () {
|
|
||||||
await this.tpv.check(this.$route)
|
|
||||||
|
|
||||||
this.orders = await this.$jApi.query(
|
|
||||||
'CALL myTicket_list(NULL, NULL)'
|
|
||||||
)
|
|
||||||
this.debt = await this.$jApi.getValue(
|
|
||||||
'SELECT -myClient_getDebt(NULL)'
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
date,
|
|
||||||
currency,
|
|
||||||
|
|
||||||
async onPayClick () {
|
|
||||||
let amount = -this.debt
|
|
||||||
amount = amount <= 0 ? null : amount
|
|
||||||
|
|
||||||
let defaultAmountStr = ''
|
|
||||||
if (amount !== null) {
|
|
||||||
defaultAmountStr = amount
|
|
||||||
}
|
|
||||||
amount = prompt(this.$t('amountToPay'), defaultAmountStr)
|
|
||||||
|
|
||||||
if (amount != null) {
|
|
||||||
amount = parseFloat(amount.replace(',', '.'))
|
|
||||||
await this.tpv.pay(amount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<i18n lang="yaml">
|
<i18n lang="yaml">
|
||||||
en-US:
|
en-US:
|
||||||
startOrder: Start order
|
startOrder: Start order
|
||||||
noOrdersFound: No orders found
|
noOrdersFound: No orders found
|
||||||
makePayment: Make payment
|
makePayment: Make payment
|
||||||
shoppingCart: Shopping cart
|
shoppingCart: Shopping cart
|
||||||
balance: 'Balance:'
|
balance: 'Balance:'
|
||||||
paymentInfo: >-
|
paymentInfo: >-
|
||||||
The amount shown is your slope (negative) or favorable balance today, it
|
The amount shown is your slope (negative) or favorable balance today, it
|
||||||
disregards future orders. For get your order shipped, this amount must be
|
disregards future orders. For get your order shipped, this amount must be
|
||||||
equal to or greater than 0. If you want to make a down payment, click the
|
equal to or greater than 0. If you want to make a down payment, click the
|
||||||
payment button, delete the suggested amount and enter the amount you want.
|
payment button, delete the suggested amount and enter the amount you want.
|
||||||
es-ES:
|
es-ES:
|
||||||
startOrder: Empezar pedido
|
startOrder: Empezar pedido
|
||||||
noOrdersFound: No se encontrado pedidos
|
noOrdersFound: No se encontrado pedidos
|
||||||
makePayment: Realizar pago
|
makePayment: Realizar pago
|
||||||
shoppingCart: Cesta de la compra
|
shoppingCart: Cesta de la compra
|
||||||
balance: 'Saldo:'
|
balance: 'Saldo:'
|
||||||
paymentInfo: >-
|
paymentInfo: >-
|
||||||
La cantidad mostrada es tu saldo pendiente (negativa) o favorable a día de
|
La cantidad mostrada es tu saldo pendiente (negativa) o favorable a día de
|
||||||
hoy, no tiene en cuenta pedidos del futuro. Para que tu pedido sea enviado,
|
hoy, no tiene en cuenta pedidos del futuro. Para que tu pedido sea enviado,
|
||||||
esta cantidad debe ser igual o mayor que 0. Si quieres realizar una entrega a
|
esta cantidad debe ser igual o mayor que 0. Si quieres realizar una entrega a
|
||||||
cuenta, pulsa el botón de pago, borra la cantidad sugerida e introduce la
|
cuenta, pulsa el botón de pago, borra la cantidad sugerida e introduce la
|
||||||
cantidad que desees.
|
cantidad que desees.
|
||||||
ca-ES:
|
ca-ES:
|
||||||
startOrder: Començar encàrrec
|
startOrder: Començar encàrrec
|
||||||
noOrdersFound: No s'han trobat comandes
|
noOrdersFound: No s'han trobat comandes
|
||||||
makePayment: Realitzar pagament
|
makePayment: Realitzar pagament
|
||||||
shoppingCart: Cistella de la compra
|
shoppingCart: Cistella de la compra
|
||||||
balance: 'Saldo:'
|
balance: 'Saldo:'
|
||||||
paymentInfo: >-
|
paymentInfo: >-
|
||||||
La quantitat mostrada és el teu saldo pendent (negatiu) o favorable a dia
|
La quantitat mostrada és el teu saldo pendent (negatiu) o favorable a dia
|
||||||
d'avui, no té en compte comandes del futur. Perquè la teva comanda sigui
|
d'avui, no té en compte comandes del futur. Perquè la teva comanda sigui
|
||||||
enviat, aquesta quantitat ha de ser igual o més gran que 0. Si vols fer un
|
enviat, aquesta quantitat ha de ser igual o més gran que 0. Si vols fer un
|
||||||
lliurament a compte, prem el botó de pagament, esborra la quantitat suggerida
|
lliurament a compte, prem el botó de pagament, esborra la quantitat suggerida
|
||||||
e introdueix la quantitat que vulguis.
|
e introdueix la quantitat que vulguis.
|
||||||
fr-FR:
|
fr-FR:
|
||||||
startOrder: Acheter
|
startOrder: Acheter
|
||||||
noOrdersFound: Aucune commande trouvée
|
noOrdersFound: Aucune commande trouvée
|
||||||
makePayment: Effectuer un paiement
|
makePayment: Effectuer un paiement
|
||||||
shoppingCart: Panier
|
shoppingCart: Panier
|
||||||
balance: 'Balance:'
|
balance: 'Balance:'
|
||||||
paymentInfo: >-
|
paymentInfo: >-
|
||||||
Le montant indiqué est votre pente (négative) ou balance favorable
|
Le montant indiqué est votre pente (négative) ou balance favorable
|
||||||
aujourd'hui, ne tient pas compte pour les commandes futures. Obtenir votre
|
aujourd'hui, ne tient pas compte pour les commandes futures. Obtenir votre
|
||||||
commande est expédiée, ce montant doit être égal ou supérieur à 0. Si vous
|
commande est expédiée, ce montant doit être égal ou supérieur à 0. Si vous
|
||||||
voulez faire un versement, le montant suggéré effacé et entrez le montant que
|
voulez faire un versement, le montant suggéré effacé et entrez le montant que
|
||||||
vous souhaitez.
|
vous souhaitez.
|
||||||
pt-PT:
|
pt-PT:
|
||||||
startOrder: Iniciar encomenda
|
startOrder: Iniciar encomenda
|
||||||
noOrdersFound: Nenhum pedido encontrado
|
noOrdersFound: Nenhum pedido encontrado
|
||||||
makePayment: Realizar pagamento
|
makePayment: Realizar pagamento
|
||||||
shoppingCart: Cesta da compra
|
shoppingCart: Cesta da compra
|
||||||
balance: 'Saldo:'
|
balance: 'Saldo:'
|
||||||
paymentInfo: >-
|
paymentInfo: >-
|
||||||
A quantidade mostrada é seu saldo pendente (negativo) ou favorável a dia de
|
A quantidade mostrada é seu saldo pendente (negativo) ou favorável a dia de
|
||||||
hoje, não se vincula a pedidos futuros. Para que seu pedido seja enviado, esta
|
hoje, não se vincula a pedidos futuros. Para que seu pedido seja enviado, esta
|
||||||
quantidade deve ser igual ou superior a 0. Se queres realizar um depósito à
|
quantidade deve ser igual ou superior a 0. Se queres realizar um depósito à
|
||||||
conta, clique no botão de pagamento, apague a quantidade sugerida e introduza
|
conta, clique no botão de pagamento, apague a quantidade sugerida e introduza
|
||||||
a quantidade que deseje.
|
a quantidade que deseje.
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -1,127 +1,145 @@
|
||||||
<template>
|
<template>
|
||||||
<Teleport :to="$actions">
|
<Teleport :to="$actions">
|
||||||
<q-btn
|
<QBtn
|
||||||
icon="print"
|
icon="print"
|
||||||
:label="$t('printDeliveryNote')"
|
:label="$t('printDeliveryNote')"
|
||||||
@click="onPrintClick()"
|
@click="onPrintClick()"
|
||||||
rounded
|
rounded
|
||||||
no-caps/>
|
no-caps
|
||||||
</Teleport>
|
/>
|
||||||
<div>
|
</Teleport>
|
||||||
<q-card class="vn-w-sm">
|
<div>
|
||||||
<q-card-section>
|
<QCard class="vn-w-sm">
|
||||||
<div class="text-h6">#{{ticket.id}}</div>
|
<QCardSection>
|
||||||
</q-card-section>
|
<div class="text-h6">#{{ ticket.id }}</div>
|
||||||
<q-card-section>
|
</QCardSection>
|
||||||
<div class="text-h6">{{$t('shippingInformation')}}</div>
|
<QCardSection>
|
||||||
<div>{{$t('preparation')}} {{date(ticket.shipped, 'ddd, MMMM Do')}}</div>
|
<div class="text-h6">{{ $t('shippingInformation') }}</div>
|
||||||
<div>{{$t('delivery')}} {{date(ticket.shipped, 'ddd, MMMM Do')}}</div>
|
<div>
|
||||||
<div>{{$t(ticket.method != 'PICKUP' ? 'agency' : 'warehouse')}} {{ticket.agency}}</div>
|
{{ $t('preparation') }}
|
||||||
</q-card-section>
|
{{ date(ticket.shipped, 'ddd, MMMM Do') }}
|
||||||
<q-card-section>
|
</div>
|
||||||
<div class="text-h6">{{$t('deliveryAddress')}}</div>
|
<div>
|
||||||
<div>{{ticket.nickname}}</div>
|
{{ $t('delivery') }}
|
||||||
<div>{{ticket.street}}</div>
|
{{ date(ticket.shipped, 'ddd, MMMM Do') }}
|
||||||
<div>{{ticket.postalCode}} {{ticket.city}} ({{ticket.province}})</div>
|
</div>
|
||||||
</q-card-section>
|
<div>
|
||||||
<q-separator inset />
|
{{ $t(ticket.method != 'PICKUP' ? 'agency' : 'warehouse') }}
|
||||||
<q-list v-for="row in rows" :key="row.itemFk">
|
{{ ticket.agency }}
|
||||||
<q-item>
|
</div>
|
||||||
<q-item-section avatar>
|
</QCardSection>
|
||||||
<q-avatar size="68px">
|
<QCardSection>
|
||||||
<img :src="`${$app.imageUrl}/catalog/200x200/${row.image}`">
|
<div class="text-h6">{{ $t('deliveryAddress') }}</div>
|
||||||
</q-avatar>
|
<div>{{ ticket.nickname }}</div>
|
||||||
</q-item-section>
|
<div>{{ ticket.street }}</div>
|
||||||
<q-item-section>
|
<div>
|
||||||
<q-item-label lines="1">
|
{{ ticket.postalCode }} {{ ticket.city }} ({{
|
||||||
{{row.concept}}
|
ticket.province
|
||||||
</q-item-label>
|
}})
|
||||||
<q-item-label lines="1" caption>
|
</div>
|
||||||
{{row.value5}} {{row.value6}} {{row.value7}}
|
</QCardSection>
|
||||||
</q-item-label>
|
<QSeparator inset />
|
||||||
<q-item-label lines="1">
|
<QList v-for="row in rows" :key="row.itemFk">
|
||||||
{{row.quantity}} x {{currency(row.price)}}
|
<QItem>
|
||||||
</q-item-label>
|
<QItemSection avatar>
|
||||||
</q-item-section>
|
<QAvatar size="68px">
|
||||||
<q-item-section side class="total">
|
<img
|
||||||
<q-item-label>
|
:src="`${$app.imageUrl}/catalog/200x200/${row.image}`"
|
||||||
<span class="discount" v-if="row.discount">
|
/>
|
||||||
{{currency(discountSubtotal(row))}} -
|
</QAvatar>
|
||||||
{{currency(row.discount)}} =
|
</QItemSection>
|
||||||
</span>
|
<QItemSection>
|
||||||
{{currency(subtotal(row))}}
|
<QItemLabel lines="1">
|
||||||
</q-item-label>
|
{{ row.concept }}
|
||||||
</q-item-section>
|
</QItemLabel>
|
||||||
</q-item>
|
<QItemLabel lines="1" caption>
|
||||||
</q-list>
|
{{ row.value5 }} {{ row.value6 }} {{ row.value7 }}
|
||||||
</q-card>
|
</QItemLabel>
|
||||||
</div>
|
<QItemLabel lines="1">
|
||||||
|
{{ row.quantity }} x {{ currency(row.price) }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection side class="total">
|
||||||
|
<QItemLabel>
|
||||||
|
<span class="discount" v-if="row.discount">
|
||||||
|
{{ currency(discountSubtotal(row)) }} -
|
||||||
|
{{ currency(row.discount) }} =
|
||||||
|
</span>
|
||||||
|
{{ currency(subtotal(row)) }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QCard>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.total {
|
.total {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { date, currency } from 'src/lib/filters.js'
|
import { date, currency } from 'src/lib/filters.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'OrdersConfirmedView',
|
name: 'OrdersConfirmedView',
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
ticket: {},
|
ticket: {},
|
||||||
rows: null,
|
rows: null,
|
||||||
services: null,
|
services: null,
|
||||||
packages: null
|
packages: null
|
||||||
}
|
};
|
||||||
},
|
|
||||||
|
|
||||||
async mounted () {
|
|
||||||
const params = {
|
|
||||||
ticket: parseInt(this.$route.params.id)
|
|
||||||
}
|
|
||||||
this.ticket = await this.$jApi.getObject(
|
|
||||||
'CALL myTicket_get(#ticket)',
|
|
||||||
params
|
|
||||||
)
|
|
||||||
this.rows = await this.$jApi.query(
|
|
||||||
'CALL myTicket_getRows(#ticket)',
|
|
||||||
params
|
|
||||||
)
|
|
||||||
this.services = await this.$jApi.query(
|
|
||||||
'CALL myTicket_getServices(#ticket)',
|
|
||||||
params
|
|
||||||
)
|
|
||||||
this.packages = await this.$jApi.query(
|
|
||||||
'CALL myTicket_getPackages(#ticket)',
|
|
||||||
params
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
date,
|
|
||||||
currency,
|
|
||||||
|
|
||||||
discountSubtotal (line) {
|
|
||||||
return line.quantity * line.price
|
|
||||||
},
|
},
|
||||||
|
|
||||||
subtotal (line) {
|
async mounted() {
|
||||||
const discount = line.discount
|
const params = {
|
||||||
return this.discountSubtotal(line) * ((100 - discount) / 100)
|
ticket: parseInt(this.$route.params.id)
|
||||||
|
};
|
||||||
|
this.ticket = await this.$jApi.getObject(
|
||||||
|
'CALL myTicket_get(#ticket)',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
this.rows = await this.$jApi.query(
|
||||||
|
'CALL myTicket_getRows(#ticket)',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
this.services = await this.$jApi.query(
|
||||||
|
'CALL myTicket_getServices(#ticket)',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
this.packages = await this.$jApi.query(
|
||||||
|
'CALL myTicket_getPackages(#ticket)',
|
||||||
|
params
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
onPrintClick () {
|
methods: {
|
||||||
const params = new URLSearchParams({
|
date,
|
||||||
access_token: this.$user.token,
|
currency,
|
||||||
recipientId: this.$user.id,
|
|
||||||
type: 'deliveryNote'
|
discountSubtotal(line) {
|
||||||
})
|
return line.quantity * line.price;
|
||||||
window.open(`/api/Tickets/${this.ticket.id}/delivery-note-pdf?${params.toString()}`)
|
},
|
||||||
|
|
||||||
|
subtotal(line) {
|
||||||
|
const discount = line.discount;
|
||||||
|
return this.discountSubtotal(line) * ((100 - discount) / 100);
|
||||||
|
},
|
||||||
|
|
||||||
|
onPrintClick() {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
access_token: this.$user.token,
|
||||||
|
recipientId: this.$user.id,
|
||||||
|
type: 'deliveryNote'
|
||||||
|
});
|
||||||
|
window.open(
|
||||||
|
`/api/Tickets/${this.ticket.id}/delivery-note-pdf?${params.toString()}`
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="fullscreen bg-accent text-white text-center q-pa-md flex flex-center">
|
<div
|
||||||
<div>
|
class="fullscreen bg-accent text-white text-center q-pa-md flex flex-center"
|
||||||
<div style="font-size: 30vh">
|
>
|
||||||
404
|
<div>
|
||||||
</div>
|
<div style="font-size: 30vh">404</div>
|
||||||
|
|
||||||
<div class="text-h2" style="opacity:.4">
|
<div class="text-h2" style="opacity: 0.4">
|
||||||
Oops. Nothing here...
|
Oops. Nothing here...
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<q-btn
|
<QBtn
|
||||||
class="q-mt-xl"
|
class="q-mt-xl"
|
||||||
color="white"
|
color="white"
|
||||||
text-color="accent"
|
text-color="accent"
|
||||||
unelevated
|
unelevated
|
||||||
to="/"
|
to="/"
|
||||||
label="Go Home"
|
label="Go Home"
|
||||||
no-caps
|
no-caps
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ErrorNotFound'
|
name: 'ErrorNotFound'
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<q-page class="flex flex-center">
|
<QPage class="flex flex-center">
|
||||||
<img
|
<img
|
||||||
alt="Quasar logo"
|
alt="Quasar logo"
|
||||||
src="~assets/quasar-logo-vertical.svg"
|
src="~assets/quasar-logo-vertical.svg"
|
||||||
style="width: 200px; height: 200px"
|
style="width: 200px; height: 200px"
|
||||||
>
|
/>
|
||||||
</q-page>
|
</QPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'IndexPage'
|
name: 'IndexPage'
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,81 +1,78 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<router-link to="/" class="block">
|
<router-link to="/" class="block">
|
||||||
<img
|
<img src="statics/logo.svg" alt="Verdnatura" class="block" />
|
||||||
src="statics/logo.svg"
|
</router-link>
|
||||||
alt="Verdnatura"
|
</div>
|
||||||
class="block"
|
<QForm @submit="onLogin" class="q-gutter-y-md">
|
||||||
/>
|
<div class="q-gutter-y-sm">
|
||||||
</router-link>
|
<QInput v-model="email" :label="$t('user')" autofocus />
|
||||||
|
<QInput
|
||||||
|
v-model="password"
|
||||||
|
ref="password"
|
||||||
|
:label="$t('password')"
|
||||||
|
:type="showPwd ? 'password' : 'text'"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<QIcon
|
||||||
|
:name="showPwd ? 'visibility_off' : 'visibility'"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="showPwd = !showPwd"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</QInput>
|
||||||
|
<QCheckbox
|
||||||
|
v-model="remember"
|
||||||
|
:label="$t('remindMe')"
|
||||||
|
class="remember"
|
||||||
|
dense
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="justify-center">
|
||||||
|
<QBtn
|
||||||
|
type="submit"
|
||||||
|
:label="$t('logIn')"
|
||||||
|
class="full-width"
|
||||||
|
color="primary"
|
||||||
|
rounded
|
||||||
|
no-caps
|
||||||
|
unelevated
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="justify-center">
|
||||||
|
<QBtn
|
||||||
|
to="/"
|
||||||
|
:label="$t('logInAsGuest')"
|
||||||
|
class="full-width"
|
||||||
|
color="primary"
|
||||||
|
rounded
|
||||||
|
no-caps
|
||||||
|
outline
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p class="password-forgotten text-center q-mt-lg">
|
||||||
|
<router-link to="/remember-password" class="link">
|
||||||
|
{{ $t('haveForgottenPassword') }}
|
||||||
|
</router-link>
|
||||||
|
</p>
|
||||||
|
</QForm>
|
||||||
|
<div class="footer text-center">
|
||||||
|
<p>
|
||||||
|
{{ $t('notACustomerYet') }}
|
||||||
|
<a
|
||||||
|
href="//verdnatura.es/register/"
|
||||||
|
target="_blank"
|
||||||
|
class="link"
|
||||||
|
>
|
||||||
|
{{ $t('signUp') }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p class="contact">
|
||||||
|
{{ $t('loginPhone') }} · {{ $t('loginMail') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-form @submit="onLogin" class="q-gutter-y-md">
|
|
||||||
<div class="q-gutter-y-sm">
|
|
||||||
<q-input
|
|
||||||
v-model="email"
|
|
||||||
:label="$t('user')"
|
|
||||||
autofocus
|
|
||||||
/>
|
|
||||||
<q-input
|
|
||||||
v-model="password"
|
|
||||||
ref="password"
|
|
||||||
:label="$t('password')"
|
|
||||||
:type="showPwd ? 'password' : 'text'">
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon
|
|
||||||
:name="showPwd ? 'visibility_off' : 'visibility'"
|
|
||||||
class="cursor-pointer"
|
|
||||||
@click="showPwd = !showPwd"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
<q-checkbox
|
|
||||||
v-model="remember"
|
|
||||||
:label="$t('remindMe')"
|
|
||||||
class="remember"
|
|
||||||
dense
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="justify-center">
|
|
||||||
<q-btn
|
|
||||||
type="submit"
|
|
||||||
:label="$t('logIn')"
|
|
||||||
class="full-width"
|
|
||||||
color="primary"
|
|
||||||
rounded
|
|
||||||
no-caps
|
|
||||||
unelevated
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="justify-center">
|
|
||||||
<q-btn
|
|
||||||
to="/"
|
|
||||||
:label="$t('logInAsGuest')"
|
|
||||||
class="full-width"
|
|
||||||
color="primary"
|
|
||||||
rounded
|
|
||||||
no-caps
|
|
||||||
outline
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p class="password-forgotten text-center q-mt-lg">
|
|
||||||
<router-link to="/remember-password" class="link">
|
|
||||||
{{$t('haveForgottenPassword')}}
|
|
||||||
</router-link>
|
|
||||||
</p>
|
|
||||||
</q-form>
|
|
||||||
<div class="footer text-center">
|
|
||||||
<p>
|
|
||||||
{{$t('notACustomerYet')}}
|
|
||||||
<a href="//verdnatura.es/register/" target="_blank" class="link">
|
|
||||||
{{$t('signUp')}}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<p class="contact">
|
|
||||||
{{$t('loginPhone')}} · {{$t('loginMail')}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -83,106 +80,106 @@ $login-margin-top: 50px;
|
||||||
$login-margin-between: 55px;
|
$login-margin-between: 55px;
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
max-width: 280px;
|
max-width: 280px;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
.header {
|
.header {
|
||||||
margin-top: $login-margin-top;
|
margin-top: $login-margin-top;
|
||||||
margin-bottom: $login-margin-between;
|
margin-bottom: $login-margin-between;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.remember {
|
.remember {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
}
|
}
|
||||||
.q-btn {
|
.q-btn {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
.password-forgotten {
|
.password-forgotten {
|
||||||
font-size: .8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
.footer {
|
.footer {
|
||||||
margin-bottom: $login-margin-top;
|
margin-bottom: $login-margin-top;
|
||||||
margin-top: $login-margin-between;
|
margin-top: $login-margin-between;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: .8rem;
|
font-size: 0.8rem;
|
||||||
|
|
||||||
.contact {
|
.contact {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
color: grey;
|
color: grey;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { userStore } from 'stores/user'
|
import { userStore } from 'stores/user';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'VnLogin',
|
name: 'VnLogin',
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
user: userStore(),
|
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({
|
||||||
message: this.$t('emailConfirmedSuccessfully'),
|
message: this.$t('emailConfirmedSuccessfully'),
|
||||||
type: 'positive'
|
type: 'positive'
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (this.$route.params.email) {
|
if (this.$route.params.email) {
|
||||||
this.email = this.$route.params.email
|
this.email = this.$route.params.email;
|
||||||
this.$refs.password.focus()
|
this.$refs.password.focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async onLogin () {
|
async onLogin() {
|
||||||
await this.user.login(this.email, this.password, this.remember)
|
await this.user.login(this.email, this.password, this.remember);
|
||||||
this.$router.push('/')
|
this.$router.push('/');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<i18n lang="yaml">
|
<i18n lang="yaml">
|
||||||
en-US:
|
en-US:
|
||||||
user: User
|
user: User
|
||||||
password: Password
|
password: Password
|
||||||
remindMe: Remind me
|
remindMe: Remind me
|
||||||
logInAsGuest: Log in as guest
|
logInAsGuest: Log in as guest
|
||||||
logIn: Log in
|
logIn: Log in
|
||||||
loginMail: infoverdnatura.es
|
loginMail: infoverdnatura.es
|
||||||
loginPhone: +34 607 562 391
|
loginPhone: +34 607 562 391
|
||||||
haveForgottenPassword: Have you forgotten your password?
|
haveForgottenPassword: Have you forgotten your password?
|
||||||
notACustomerYet: Not a customer yet?
|
notACustomerYet: Not a customer yet?
|
||||||
signUp: Register
|
signUp: Register
|
||||||
es-ES:
|
es-ES:
|
||||||
user: Usuario
|
user: Usuario
|
||||||
password: Contraseña
|
password: Contraseña
|
||||||
remindMe: Recuérdame
|
remindMe: Recuérdame
|
||||||
logInAsGuest: Entrar como invitado
|
logInAsGuest: Entrar como invitado
|
||||||
logIn: Iniciar sesión
|
logIn: Iniciar sesión
|
||||||
loginMail: infoverdnatura.es
|
loginMail: infoverdnatura.es
|
||||||
loginPhone: +34 963 242 100
|
loginPhone: +34 963 242 100
|
||||||
haveForgottenPassword: ¿Has olvidado tu contraseña?
|
haveForgottenPassword: ¿Has olvidado tu contraseña?
|
||||||
notACustomerYet: ¿Todavía no eres cliente?
|
notACustomerYet: ¿Todavía no eres cliente?
|
||||||
signUp: Registrarme
|
signUp: Registrarme
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -1,91 +1,91 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div>
|
<div>
|
||||||
<q-icon
|
<QIcon
|
||||||
name="contact_support"
|
name="contact_support"
|
||||||
class="block q-mx-auto text-accent"
|
class="block q-mx-auto text-accent"
|
||||||
style="font-size: 120px;"
|
style="font-size: 120px"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<q-form @submit="onSend" class="q-gutter-y-md text-grey-8">
|
|
||||||
<div class="text-h5">
|
|
||||||
<div>
|
|
||||||
{{$t('dontWorry')}}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{{$t('fillData')}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<q-input
|
|
||||||
v-model="email"
|
|
||||||
:label="$t('user')"
|
|
||||||
:rules="[ val => !!val || $t('inputEmail')]"
|
|
||||||
autofocus
|
|
||||||
/>
|
|
||||||
<div class="q-mt-lg">
|
|
||||||
{{$t('weSendEmail')}}
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<q-btn
|
<QForm @submit="onSend" class="q-gutter-y-md text-grey-8">
|
||||||
type="submit"
|
<div class="text-h5">
|
||||||
:label="$t('send')"
|
<div>
|
||||||
class="full-width q-mt-md"
|
{{ $t('dontWorry') }}
|
||||||
color="primary"
|
</div>
|
||||||
rounded
|
<div>
|
||||||
no-caps
|
{{ $t('fillData') }}
|
||||||
unelevated
|
</div>
|
||||||
/>
|
</div>
|
||||||
<div class="text-center q-mt-md">
|
<QInput
|
||||||
<router-link to="/login" class="link">
|
v-model="email"
|
||||||
{{$t('return')}}
|
:label="$t('user')"
|
||||||
</router-link>
|
:rules="[val => !!val || $t('inputEmail')]"
|
||||||
</div>
|
autofocus
|
||||||
|
/>
|
||||||
|
<div class="q-mt-lg">
|
||||||
|
{{ $t('weSendEmail') }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<QBtn
|
||||||
|
type="submit"
|
||||||
|
:label="$t('send')"
|
||||||
|
class="full-width q-mt-md"
|
||||||
|
color="primary"
|
||||||
|
rounded
|
||||||
|
no-caps
|
||||||
|
unelevated
|
||||||
|
/>
|
||||||
|
<div class="text-center q-mt-md">
|
||||||
|
<router-link to="/login" class="link">
|
||||||
|
{{ $t('return') }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</QForm>
|
||||||
</div>
|
</div>
|
||||||
</q-form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
#image {
|
#image {
|
||||||
height: 190px;
|
height: 190px;
|
||||||
}
|
}
|
||||||
.q-btn {
|
.q-btn {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
font-size: .8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'VnRememberPasword',
|
name: 'VnRememberPasword',
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
email: ''
|
email: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async onSend() {
|
||||||
|
const params = {
|
||||||
|
email: this.email
|
||||||
|
};
|
||||||
|
await this.$axios.post('Users/reset', params);
|
||||||
|
this.$q.notify({
|
||||||
|
message: this.$t('weHaveSentEmailToRecover'),
|
||||||
|
type: 'positive'
|
||||||
|
});
|
||||||
|
this.$router.push('/login');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
methods: {
|
|
||||||
async onSend () {
|
|
||||||
const params = {
|
|
||||||
email: this.email
|
|
||||||
}
|
|
||||||
await this.$axios.post('Users/reset', params)
|
|
||||||
this.$q.notify({
|
|
||||||
message: this.$t('weHaveSentEmailToRecover'),
|
|
||||||
type: 'positive'
|
|
||||||
})
|
|
||||||
this.$router.push('/login')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<i18n lang="yaml">
|
<i18n lang="yaml">
|
||||||
en-US:
|
en-US:
|
||||||
user: User
|
user: User
|
||||||
inputEmail: Input email
|
inputEmail: Input email
|
||||||
rememberPassword: Rememeber password
|
rememberPassword: Rememeber password
|
||||||
|
@ -95,7 +95,7 @@ export default {
|
||||||
weHaveSentEmailToRecover: We've sent you an email where you can recover your password
|
weHaveSentEmailToRecover: We've sent you an email where you can recover your password
|
||||||
send: Send
|
send: Send
|
||||||
return: Return
|
return: Return
|
||||||
es-ES:
|
es-ES:
|
||||||
user: Usuario
|
user: Usuario
|
||||||
inputEmail: Introduce el correo electrónico
|
inputEmail: Introduce el correo electrónico
|
||||||
rememberPassword: Recordar contraseña
|
rememberPassword: Recordar contraseña
|
||||||
|
|
|
@ -1,93 +1,106 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<q-card-section>
|
<QCard-section>
|
||||||
<q-icon
|
<QIcon
|
||||||
name="check"
|
name="check"
|
||||||
class="block q-mx-auto text-accent"
|
class="block q-mx-auto text-accent"
|
||||||
style="font-size: 120px;"
|
style="font-size: 120px"
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
</QCard-section>
|
||||||
<q-card-section>
|
<QCard-section>
|
||||||
<q-form @submit="onRegister" ref="form" class="q-gutter-y-md">
|
<QForm @submit="onRegister" ref="form" class="q-gutter-y-md">
|
||||||
<div class="text-grey-8 text-h5 text-center">
|
<div class="text-grey-8 text-h5 text-center">
|
||||||
{{$t('fillData')}}
|
{{ $t('fillData') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="q-gutter-y-sm">
|
<div class="q-gutter-y-sm">
|
||||||
<q-input
|
<QInput
|
||||||
v-model="password"
|
v-model="password"
|
||||||
:label="$t('password')"
|
:label="$t('password')"
|
||||||
:type="showPwd ? 'password' : 'text'"
|
:type="showPwd ? 'password' : 'text'"
|
||||||
autofocus
|
autofocus
|
||||||
hint=""
|
hint=""
|
||||||
filled>
|
filled
|
||||||
<template v-slot:append>
|
>
|
||||||
<q-icon
|
<template v-slot:append>
|
||||||
:name="showPwd ? 'visibility_off' : 'visibility'"
|
<QIcon
|
||||||
class="cursor-pointer"
|
:name="
|
||||||
@click="showPwd = !showPwd"
|
showPwd ? 'visibility_off' : 'visibility'
|
||||||
/>
|
"
|
||||||
</template>
|
class="cursor-pointer"
|
||||||
</q-input>
|
@click="showPwd = !showPwd"
|
||||||
<q-input
|
/>
|
||||||
v-model="repeatPassword"
|
</template>
|
||||||
:label="$t('repeatPassword')"
|
</QInput>
|
||||||
:type="showRpPwd ? 'password' : 'text'"
|
<QInput
|
||||||
:rules="[value => value == password || $t('repeatPasswordError')]"
|
v-model="repeatPassword"
|
||||||
hint=""
|
:label="$t('repeatPassword')"
|
||||||
filled>
|
:type="showRpPwd ? 'password' : 'text'"
|
||||||
<template v-slot:append>
|
:rules="[
|
||||||
<q-icon
|
value =>
|
||||||
:name="showRpPwd ? 'visibility_off' : 'visibility'"
|
value == password || $t('repeatPasswordError')
|
||||||
class="cursor-pointer"
|
]"
|
||||||
@click="showRpPwd = !showRpPwd"
|
hint=""
|
||||||
/>
|
filled
|
||||||
</template>
|
>
|
||||||
</q-input>
|
<template v-slot:append>
|
||||||
</div>
|
<QIcon
|
||||||
<div>
|
:name="
|
||||||
<q-btn
|
showRpPwd ? 'visibility_off' : 'visibility'
|
||||||
type="submit"
|
"
|
||||||
:label="$t('resetPassword')"
|
class="cursor-pointer"
|
||||||
class="full-width"
|
@click="showRpPwd = !showRpPwd"
|
||||||
color="primary"
|
/>
|
||||||
/>
|
</template>
|
||||||
<div class="text-center q-mt-xs">
|
</QInput>
|
||||||
<router-link to="/login" class="link">
|
</div>
|
||||||
{{$t('return')}}
|
<div>
|
||||||
</router-link>
|
<QBtn
|
||||||
</div>
|
type="submit"
|
||||||
</div>
|
:label="$t('resetPassword')"
|
||||||
</q-form>
|
class="full-width"
|
||||||
</q-card-section>
|
color="primary"
|
||||||
</div>
|
/>
|
||||||
|
<div class="text-center q-mt-xs">
|
||||||
|
<router-link to="/login" class="link">
|
||||||
|
{{ $t('return') }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</QForm>
|
||||||
|
</QCard-section>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'VnRegister',
|
name: 'VnRegister',
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
password: '',
|
password: '',
|
||||||
repeatPassword: '',
|
repeatPassword: '',
|
||||||
showPwd: true,
|
showPwd: true,
|
||||||
showRpPwd: true
|
showRpPwd: true
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async onRegister () {
|
async onRegister() {
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: this.$route.query.access_token
|
Authorization: this.$route.query.access_token
|
||||||
}
|
};
|
||||||
await this.$axios.post('users/reset-password', {
|
await this.$axios.post(
|
||||||
newPassword: this.password
|
'users/reset-password',
|
||||||
}, { headers })
|
{
|
||||||
|
newPassword: this.password
|
||||||
|
},
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
message: this.$t('passwordResetSuccessfully'),
|
message: this.$t('passwordResetSuccessfully'),
|
||||||
type: 'positive'
|
type: 'positive'
|
||||||
})
|
});
|
||||||
this.$router.push('/login')
|
this.$router.push('/login');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { route } from 'quasar/wrappers'
|
import { route } from 'quasar/wrappers'
|
||||||
import { appStore } from 'stores/app'
|
import { appStore } from 'stores/app'
|
||||||
import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router'
|
import {
|
||||||
|
createRouter,
|
||||||
|
createMemoryHistory,
|
||||||
|
createWebHistory,
|
||||||
|
createWebHashHistory
|
||||||
|
} from 'vue-router'
|
||||||
import routes from './routes'
|
import routes from './routes'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -13,30 +18,34 @@ import routes from './routes'
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default route(function (/* { store, ssrContext } */) {
|
export default route(function (/* { store, ssrContext } */) {
|
||||||
const createHistory = process.env.SERVER
|
const createHistory = process.env.SERVER
|
||||||
? createMemoryHistory
|
? createMemoryHistory
|
||||||
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
|
: process.env.VUE_ROUTER_MODE === 'history'
|
||||||
|
? createWebHistory
|
||||||
|
: createWebHashHistory
|
||||||
|
|
||||||
const Router = createRouter({
|
const Router = createRouter({
|
||||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||||
routes,
|
routes,
|
||||||
|
|
||||||
// Leave this as is and make changes in quasar.conf.js instead!
|
// Leave this as is and make changes in quasar.conf.js instead!
|
||||||
// quasar.conf.js -> build -> vueRouterMode
|
// quasar.conf.js -> build -> vueRouterMode
|
||||||
// quasar.conf.js -> build -> publicPath
|
// quasar.conf.js -> build -> publicPath
|
||||||
history: createHistory(process.env.MODE === 'ssr' ? void 0 : process.env.VUE_ROUTER_BASE)
|
history: createHistory(
|
||||||
})
|
process.env.MODE === 'ssr' ? void 0 : process.env.VUE_ROUTER_BASE
|
||||||
|
)
|
||||||
Router.afterEach((to, from) => {
|
|
||||||
if (from.name === to.name) return
|
|
||||||
const app = appStore()
|
|
||||||
app.$patch({
|
|
||||||
title: window.i18n.t(to.name || 'home'),
|
|
||||||
subtitle: null,
|
|
||||||
useRightDrawer: false,
|
|
||||||
rightDrawerOpen: true
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
return Router
|
Router.afterEach((to, from) => {
|
||||||
|
if (from.name === to.name) return
|
||||||
|
const app = appStore()
|
||||||
|
app.$patch({
|
||||||
|
title: window.i18n.t(to.name || 'home'),
|
||||||
|
subtitle: null,
|
||||||
|
useRightDrawer: false,
|
||||||
|
rightDrawerOpen: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return Router
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,61 +1,68 @@
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
component: () => import('layouts/LoginLayout.vue'),
|
component: () => import('layouts/LoginLayout.vue'),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
name: 'login',
|
name: 'login',
|
||||||
path: '/login/:email?',
|
path: '/login/:email?',
|
||||||
component: () => import('pages/Login/Login.vue')
|
component: () => import('pages/Login/Login.vue')
|
||||||
}, {
|
},
|
||||||
name: 'rememberPassword',
|
{
|
||||||
path: '/remember-password',
|
name: 'rememberPassword',
|
||||||
component: () => import('pages/Login/RememberPassword.vue')
|
path: '/remember-password',
|
||||||
}, {
|
component: () => import('pages/Login/RememberPassword.vue')
|
||||||
name: 'resetPassword',
|
},
|
||||||
path: '/reset-password',
|
{
|
||||||
component: () => import('pages/Login/ResetPassword.vue')
|
name: 'resetPassword',
|
||||||
}
|
path: '/reset-password',
|
||||||
]
|
component: () => import('pages/Login/ResetPassword.vue')
|
||||||
}, {
|
}
|
||||||
path: '/',
|
]
|
||||||
component: () => import('layouts/MainLayout.vue'),
|
},
|
||||||
children: [
|
{
|
||||||
{
|
path: '/',
|
||||||
name: '',
|
component: () => import('layouts/MainLayout.vue'),
|
||||||
path: '',
|
children: [
|
||||||
component: () => import('src/pages/Cms/Home.vue')
|
{
|
||||||
}, {
|
name: '',
|
||||||
name: 'home',
|
path: '',
|
||||||
path: '/cms/home',
|
component: () => import('src/pages/Cms/Home.vue')
|
||||||
component: () => import('src/pages/Cms/Home.vue')
|
},
|
||||||
}, {
|
{
|
||||||
name: 'orders',
|
name: 'home',
|
||||||
path: '/ecomerce/orders',
|
path: '/cms/home',
|
||||||
component: () => import('pages/Ecomerce/Orders.vue')
|
component: () => import('src/pages/Cms/Home.vue')
|
||||||
}, {
|
},
|
||||||
name: 'ticket',
|
{
|
||||||
path: '/ecomerce/ticket/:id',
|
name: 'orders',
|
||||||
component: () => import('pages/Ecomerce/Ticket.vue')
|
path: '/ecomerce/orders',
|
||||||
}, {
|
component: () => import('pages/Ecomerce/Orders.vue')
|
||||||
name: 'invoices',
|
},
|
||||||
path: '/ecomerce/invoices',
|
{
|
||||||
component: () => import('pages/Ecomerce/Invoices.vue')
|
name: 'ticket',
|
||||||
}, {
|
path: '/ecomerce/ticket/:id',
|
||||||
name: 'catalog',
|
component: () => import('pages/Ecomerce/Ticket.vue')
|
||||||
path: '/ecomerce/catalog/:category?/:type?',
|
},
|
||||||
component: () => import('pages/Ecomerce/Catalog.vue')
|
{
|
||||||
}
|
name: 'invoices',
|
||||||
]
|
path: '/ecomerce/invoices',
|
||||||
},
|
component: () => import('pages/Ecomerce/Invoices.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'catalog',
|
||||||
|
path: '/ecomerce/catalog/:category?/:type?',
|
||||||
|
component: () => import('pages/Ecomerce/Catalog.vue')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
// Always leave this as last one,
|
// Always leave this as last one,
|
||||||
// but you can also remove it
|
// but you can also remove it
|
||||||
{
|
{
|
||||||
path: '/:catchAll(.*)*',
|
path: '/:catchAll(.*)*',
|
||||||
component: () => import('pages/ErrorNotFound.vue')
|
component: () => import('pages/ErrorNotFound.vue')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export default routes
|
export default routes
|
||||||
|
|
|
@ -2,20 +2,18 @@ import { defineStore } from 'pinia'
|
||||||
import { jApi } from 'boot/axios'
|
import { jApi } from 'boot/axios'
|
||||||
|
|
||||||
export const appStore = defineStore('hedera', {
|
export const appStore = defineStore('hedera', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
title: null,
|
title: null,
|
||||||
subtitle: null,
|
subtitle: null,
|
||||||
imageUrl: '',
|
imageUrl: '',
|
||||||
useRightDrawer: false,
|
useRightDrawer: false,
|
||||||
rightDrawerOpen: false
|
rightDrawerOpen: false
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async loadConfig () {
|
async loadConfig () {
|
||||||
const imageUrl = await jApi.getValue(
|
const imageUrl = await jApi.getValue('SELECT url FROM imageConfig')
|
||||||
'SELECT url FROM imageConfig'
|
this.$patch({ imageUrl })
|
||||||
)
|
}
|
||||||
this.$patch({ imageUrl })
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,10 +11,10 @@ import { createPinia } from 'pinia'
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default store((/* { ssrContext } */) => {
|
export default store((/* { ssrContext } */) => {
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
|
|
||||||
// You can add Pinia plugins here
|
// You can add Pinia plugins here
|
||||||
// pinia.use(SomePiniaPlugin)
|
// pinia.use(SomePiniaPlugin)
|
||||||
|
|
||||||
return pinia
|
return pinia
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
|
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
|
||||||
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
|
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
|
||||||
import "quasar/dist/types/feature-flag";
|
import 'quasar/dist/types/feature-flag'
|
||||||
|
|
||||||
declare module "quasar/dist/types/feature-flag" {
|
declare module 'quasar/dist/types/feature-flag' {
|
||||||
interface QuasarFeatureFlags {
|
interface QuasarFeatureFlags {
|
||||||
store: true;
|
store: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,86 +2,92 @@ import { defineStore } from 'pinia'
|
||||||
import { jApi } from 'boot/axios'
|
import { jApi } from 'boot/axios'
|
||||||
|
|
||||||
export const tpvStore = defineStore('tpv', {
|
export const tpvStore = defineStore('tpv', {
|
||||||
actions: {
|
actions: {
|
||||||
async check (route) {
|
async check (route) {
|
||||||
const order = route.query.tpvOrder
|
const order = route.query.tpvOrder
|
||||||
const status = route.query.tpvStatus
|
const status = route.query.tpvStatus
|
||||||
if (!(order && status)) return null
|
if (!(order && status)) return null
|
||||||
|
|
||||||
await jApi.execQuery(
|
await jApi.execQuery('CALL myTpvTransaction_end(#order, #status)', {
|
||||||
'CALL myTpvTransaction_end(#order, #status)',
|
order,
|
||||||
{ order, status }
|
status
|
||||||
)
|
})
|
||||||
|
|
||||||
if (status === 'ko') {
|
if (status === 'ko') {
|
||||||
const retry = confirm('retryPayQuestion')
|
const retry = confirm('retryPayQuestion')
|
||||||
if (retry) { this.retryPay(order) }
|
if (retry) {
|
||||||
}
|
this.retryPay(order)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return status
|
return status
|
||||||
},
|
},
|
||||||
|
|
||||||
async pay (amount, company) {
|
async pay (amount, company) {
|
||||||
await this.realPay(amount * 100, company)
|
await this.realPay(amount * 100, company)
|
||||||
},
|
},
|
||||||
|
|
||||||
async retryPay (order) {
|
async retryPay (order) {
|
||||||
const payment = await jApi.getObject(
|
const payment = await jApi.getObject(
|
||||||
`SELECT t.amount, m.companyFk
|
`SELECT t.amount, m.companyFk
|
||||||
FROM myTpvTransaction t
|
FROM myTpvTransaction t
|
||||||
JOIN tpvMerchant m ON m.id = t.merchantFk
|
JOIN tpvMerchant m ON m.id = t.merchantFk
|
||||||
WHERE t.id = #order`,
|
WHERE t.id = #order`,
|
||||||
{ order }
|
{ order }
|
||||||
)
|
)
|
||||||
await this.realPay(payment.amount, payment.companyFk)
|
await this.realPay(payment.amount, payment.companyFk)
|
||||||
},
|
},
|
||||||
|
|
||||||
async realPay (amount, company) {
|
async realPay (amount, company) {
|
||||||
if (!isNumeric(amount) || amount <= 0) {
|
if (!isNumeric(amount) || amount <= 0) {
|
||||||
throw new Error('payAmountError')
|
throw new Error('payAmountError')
|
||||||
}
|
}
|
||||||
|
|
||||||
const json = await jApi.send('tpv/transaction', {
|
const json = await jApi.send('tpv/transaction', {
|
||||||
amount: parseInt(amount),
|
amount: parseInt(amount),
|
||||||
urlOk: this.makeUrl('ok'),
|
urlOk: this.makeUrl('ok'),
|
||||||
urlKo: this.makeUrl('ko'),
|
urlKo: this.makeUrl('ko'),
|
||||||
company
|
company
|
||||||
})
|
})
|
||||||
|
|
||||||
const postValues = json.postValues
|
const postValues = json.postValues
|
||||||
|
|
||||||
const form = document.createElement('form')
|
const form = document.createElement('form')
|
||||||
form.method = 'POST'
|
form.method = 'POST'
|
||||||
form.action = json.url
|
form.action = json.url
|
||||||
document.body.appendChild(form)
|
document.body.appendChild(form)
|
||||||
|
|
||||||
for (const field in postValues) {
|
for (const field in postValues) {
|
||||||
const input = document.createElement('input')
|
const input = document.createElement('input')
|
||||||
input.type = 'hidden'
|
input.type = 'hidden'
|
||||||
input.name = field
|
input.name = field
|
||||||
form.appendChild(input)
|
form.appendChild(input)
|
||||||
|
|
||||||
if (postValues[field]) { input.value = postValues[field] }
|
if (postValues[field]) {
|
||||||
}
|
input.value = postValues[field]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
form.submit()
|
form.submit()
|
||||||
},
|
},
|
||||||
|
|
||||||
makeUrl (status) {
|
makeUrl (status) {
|
||||||
let path = location.protocol + '//' + location.hostname
|
let path = location.protocol + '//' + location.hostname
|
||||||
path += location.port ? ':' + location.port : ''
|
path += location.port ? ':' + location.port : ''
|
||||||
path += location.pathname
|
path += location.pathname
|
||||||
path += location.search ? location.search : ''
|
path += location.search ? location.search : ''
|
||||||
path += '#/ecomerce/orders'
|
path += '#/ecomerce/orders'
|
||||||
path += '?' + new URLSearchParams({
|
path +=
|
||||||
tpvStatus: status,
|
'?' +
|
||||||
tpvOrder: '_transactionId_'
|
new URLSearchParams({
|
||||||
}).toString()
|
tpvStatus: status,
|
||||||
return path
|
tpvOrder: '_transactionId_'
|
||||||
|
}).toString()
|
||||||
|
return path
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function isNumeric (n) {
|
function isNumeric (n) {
|
||||||
return !isNaN(parseFloat(n)) && isFinite(n)
|
return !isNaN(parseFloat(n)) && isFinite(n)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,61 +1,62 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia';
|
||||||
import { api, jApi } from 'boot/axios'
|
import { api, jApi } from 'boot/axios';
|
||||||
|
|
||||||
export const userStore = defineStore('user', {
|
export const userStore = defineStore('user', {
|
||||||
state: () => {
|
state: () => {
|
||||||
const token =
|
const token =
|
||||||
sessionStorage.getItem('vnToken') ||
|
sessionStorage.getItem('vnToken') ||
|
||||||
localStorage.getItem('vnToken')
|
localStorage.getItem('vnToken');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
id: null,
|
id: null,
|
||||||
name: null,
|
name: null,
|
||||||
nickname: null
|
nickname: null,
|
||||||
}
|
isGuest: false
|
||||||
},
|
};
|
||||||
|
|
||||||
getters: {
|
|
||||||
loggedIn: state => state.token != null
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
async login (user, password, remember) {
|
|
||||||
const params = { user, password }
|
|
||||||
const res = await api.post('Accounts/login', params)
|
|
||||||
|
|
||||||
if (remember) {
|
|
||||||
localStorage.setItem('vnToken', res.data.token)
|
|
||||||
} else {
|
|
||||||
sessionStorage.setItem('vnToken', res.data.token)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$patch({
|
|
||||||
token: res.data.token,
|
|
||||||
name: user
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async logout () {
|
getters: {
|
||||||
if (this.token != null) {
|
loggedIn: state => state.token != null
|
||||||
try {
|
|
||||||
await api.post('Accounts/logout')
|
|
||||||
} catch (e) {}
|
|
||||||
localStorage.removeItem('vnToken')
|
|
||||||
sessionStorage.removeItem('vnToken')
|
|
||||||
}
|
|
||||||
this.$reset()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async loadData () {
|
actions: {
|
||||||
const userData = await jApi.getObject(
|
async login(user, password, remember) {
|
||||||
'SELECT id, nickname FROM account.myUser'
|
const params = { user, password };
|
||||||
)
|
const res = await api.post('Accounts/login', params);
|
||||||
|
|
||||||
this.$patch({
|
if (remember) {
|
||||||
id: userData.id,
|
localStorage.setItem('vnToken', res.data.token);
|
||||||
nickname: userData.nickname
|
} else {
|
||||||
})
|
sessionStorage.setItem('vnToken', res.data.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$patch({
|
||||||
|
token: res.data.token,
|
||||||
|
name: user
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async logout() {
|
||||||
|
if (this.token != null) {
|
||||||
|
try {
|
||||||
|
await api.post('Accounts/logout');
|
||||||
|
} catch (e) {}
|
||||||
|
localStorage.removeItem('vnToken');
|
||||||
|
sessionStorage.removeItem('vnToken');
|
||||||
|
}
|
||||||
|
this.$reset();
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadData() {
|
||||||
|
const userData = await jApi.getObject(
|
||||||
|
'SELECT id, nickname FROM account.myUser'
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$patch({
|
||||||
|
id: userData.id,
|
||||||
|
nickname: userData.nickname
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
})
|
|
||||||
|
|
|
@ -11,126 +11,145 @@ const outputPath = path.join(__dirname, wpConfig.buildDir);
|
||||||
const publicPath = '/' + wpConfig.buildDir + '/';
|
const publicPath = '/' + wpConfig.buildDir + '/';
|
||||||
|
|
||||||
const baseConfig = {
|
const baseConfig = {
|
||||||
entry: wpConfig.entry,
|
entry: wpConfig.entry,
|
||||||
mode: devMode ? 'development' : 'production',
|
mode: devMode ? 'development' : 'production',
|
||||||
output: {
|
output: {
|
||||||
path: outputPath,
|
path: outputPath,
|
||||||
publicPath: publicPath
|
publicPath: publicPath
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.m?js$/,
|
test: /\.m?js$/,
|
||||||
exclude: /(node_modules|bower_components)/,
|
exclude: /(node_modules|bower_components)/,
|
||||||
use: {
|
use: {
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
presets: ['@babel/preset-env']
|
presets: ['@babel/preset-env']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
test: /tinymce\/.*\/skin\.css$/i,
|
{
|
||||||
use: [MiniCssExtractPlugin.loader, 'css-loader']
|
test: /tinymce\/.*\/skin\.css$/i,
|
||||||
}, {
|
use: [MiniCssExtractPlugin.loader, 'css-loader']
|
||||||
test: /tinymce\/.*\/content\.css$/i,
|
},
|
||||||
loader: 'css-loader',
|
{
|
||||||
options: {esModule: false}
|
test: /tinymce\/.*\/content\.css$/i,
|
||||||
}, {
|
loader: 'css-loader',
|
||||||
test: /\.css$/,
|
options: { esModule: false }
|
||||||
use: ['style-loader', 'css-loader'],
|
},
|
||||||
exclude: [/node_modules/]
|
{
|
||||||
}, {
|
test: /\.css$/,
|
||||||
test: /\.yml$/,
|
use: ['style-loader', 'css-loader'],
|
||||||
use: ['json-loader', 'yaml-loader']
|
exclude: [/node_modules/]
|
||||||
}, {
|
},
|
||||||
test: /\.xml$/,
|
{
|
||||||
use: 'raw-loader'
|
test: /\.yml$/,
|
||||||
}, {
|
use: ['json-loader', 'yaml-loader']
|
||||||
test: /\.ttf$/,
|
},
|
||||||
type: 'asset/resource'
|
{
|
||||||
}, {
|
test: /\.xml$/,
|
||||||
test: /\.scss$/,
|
use: 'raw-loader'
|
||||||
use: [
|
},
|
||||||
'style-loader',
|
{
|
||||||
'css-loader',
|
test: /\.ttf$/,
|
||||||
{
|
type: 'asset/resource'
|
||||||
loader: 'sass-loader',
|
},
|
||||||
options: {
|
{
|
||||||
sourceMap: true
|
test: /\.scss$/,
|
||||||
}
|
use: [
|
||||||
}
|
'style-loader',
|
||||||
]
|
'css-loader',
|
||||||
}
|
{
|
||||||
]
|
loader: 'sass-loader',
|
||||||
},
|
options: {
|
||||||
resolve: {
|
sourceMap: true
|
||||||
modules: [
|
}
|
||||||
__dirname +'/js',
|
}
|
||||||
__dirname,
|
]
|
||||||
__dirname +'/forms',
|
},
|
||||||
'node_modules',
|
{
|
||||||
'/usr/lib/node_modules'
|
test: /\.(woff|woff2)$/,
|
||||||
]
|
use: [
|
||||||
},
|
{
|
||||||
node: {
|
loader: 'file-loader',
|
||||||
__dirname: true
|
options: {
|
||||||
},
|
name: '[name].[ext]',
|
||||||
plugins: [
|
outputPath: 'fonts/'
|
||||||
new AssetsWebpackPlugin({
|
}
|
||||||
path: outputPath
|
}
|
||||||
}),
|
]
|
||||||
new webpack.DefinePlugin({
|
}
|
||||||
_DEV_MODE: devMode,
|
]
|
||||||
_DEV_SERVER_PORT: wpConfig.devServerPort,
|
},
|
||||||
_PUBLIC_PATH: JSON.stringify(publicPath)
|
resolve: {
|
||||||
}),
|
modules: [
|
||||||
new MiniCssExtractPlugin()
|
__dirname + '/js',
|
||||||
],
|
__dirname,
|
||||||
optimization: {
|
__dirname + '/forms',
|
||||||
runtimeChunk: true,
|
'node_modules',
|
||||||
splitChunks: {
|
'/usr/lib/node_modules'
|
||||||
chunks: 'all',
|
]
|
||||||
}
|
},
|
||||||
},
|
node: {
|
||||||
watchOptions: {
|
__dirname: true
|
||||||
ignored: /node_modules/
|
},
|
||||||
}
|
plugins: [
|
||||||
|
new AssetsWebpackPlugin({
|
||||||
|
path: outputPath
|
||||||
|
}),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
_DEV_MODE: devMode,
|
||||||
|
_DEV_SERVER_PORT: wpConfig.devServerPort,
|
||||||
|
_PUBLIC_PATH: JSON.stringify(publicPath)
|
||||||
|
}),
|
||||||
|
new MiniCssExtractPlugin()
|
||||||
|
],
|
||||||
|
optimization: {
|
||||||
|
runtimeChunk: true,
|
||||||
|
splitChunks: {
|
||||||
|
chunks: 'all'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watchOptions: {
|
||||||
|
ignored: /node_modules/
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const prodConfig = {
|
const prodConfig = {
|
||||||
output: {
|
output: {
|
||||||
filename: '[name].[chunkhash].js',
|
filename: '[name].[chunkhash].js',
|
||||||
chunkFilename: 'chunk.[id].[chunkhash].js'
|
chunkFilename: 'chunk.[id].[chunkhash].js'
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
moduleIds: 'deterministic'
|
moduleIds: 'deterministic'
|
||||||
},
|
},
|
||||||
devtool: 'source-map'
|
devtool: 'source-map'
|
||||||
};
|
};
|
||||||
|
|
||||||
const devConfig = {
|
const devConfig = {
|
||||||
output: {
|
output: {
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
chunkFilename: 'chunk.[id].js'
|
chunkFilename: 'chunk.[id].js'
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
moduleIds: 'named'
|
moduleIds: 'named'
|
||||||
},
|
},
|
||||||
devtool: 'eval',
|
devtool: 'eval',
|
||||||
devServer: {
|
devServer: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
static: __dirname,
|
static: __dirname,
|
||||||
port: wpConfig.devServerPort,
|
port: wpConfig.devServerPort,
|
||||||
headers: {'Access-Control-Allow-Origin': '*'},
|
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||||
//stats: { chunks: false },
|
//stats: { chunks: false },
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': 'http://localhost:3000',
|
'/api': 'http://localhost:3000',
|
||||||
'/': {
|
'/': {
|
||||||
target: 'http://localhost/projects/hedera-web',
|
target: 'http://localhost/projects/hedera-web',
|
||||||
bypass: (req) => req.path !== '/' ? req.path : null
|
bypass: req => (req.path !== '/' ? req.path : null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const mrgConfig = devMode ? devConfig : prodConfig;
|
const mrgConfig = devMode ? devConfig : prodConfig;
|
||||||
|
|
Loading…
Reference in New Issue