forked from verdnatura/salix-front
Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix-front into 4834-create-worker-module
This commit is contained in:
commit
cfadaa8e38
|
@ -57,6 +57,7 @@ module.exports = {
|
||||||
// add your custom rules here
|
// add your custom rules here
|
||||||
rules: {
|
rules: {
|
||||||
'prefer-promise-reject-errors': 'off',
|
'prefer-promise-reject-errors': 'off',
|
||||||
|
'no-unused-vars': 'warn',
|
||||||
|
|
||||||
// allow debugger during development only
|
// allow debugger during development only
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
singleQuote: true,
|
singleQuote: true,
|
||||||
printWidth: 120,
|
printWidth: 90,
|
||||||
tabWidth: 4,
|
tabWidth: 4,
|
||||||
semi: true,
|
semi: true,
|
||||||
endOfLine: 'auto',
|
endOfLine: 'auto',
|
||||||
|
|
|
@ -31,8 +31,7 @@ pipeline {
|
||||||
NODE_ENV = ""
|
NODE_ENV = ""
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
nodejs('node-v14') {
|
nodejs('node-v18') {
|
||||||
sh 'npm install -g @quasar/cli'
|
|
||||||
sh 'npm install --no-audit --prefer-offline'
|
sh 'npm install --no-audit --prefer-offline'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +47,7 @@ pipeline {
|
||||||
parallel {
|
parallel {
|
||||||
stage('Frontend') {
|
stage('Frontend') {
|
||||||
steps {
|
steps {
|
||||||
nodejs('node-v14') {
|
nodejs('node-v18') {
|
||||||
sh 'npm run test:unit:ci'
|
sh 'npm run test:unit:ci'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +63,7 @@ pipeline {
|
||||||
CREDENTIALS = credentials('docker-registry')
|
CREDENTIALS = credentials('docker-registry')
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
nodejs('node-v14') {
|
nodejs('node-v18') {
|
||||||
sh 'quasar build'
|
sh 'quasar build'
|
||||||
}
|
}
|
||||||
dockerBuild()
|
dockerBuild()
|
||||||
|
|
|
@ -2,7 +2,7 @@ const { defineConfig } = require('cypress');
|
||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
e2e: {
|
e2e: {
|
||||||
baseUrl: 'http://localhost:8080/',
|
baseUrl: 'http://localhost:9000/',
|
||||||
fixturesFolder: 'test/cypress/fixtures',
|
fixturesFolder: 'test/cypress/fixtures',
|
||||||
screenshotsFolder: 'test/cypress/screenshots',
|
screenshotsFolder: 'test/cypress/screenshots',
|
||||||
supportFile: 'test/cypress/support/index.js',
|
supportFile: 'test/cypress/support/index.js',
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
"name": "salix-front",
|
"name": "salix-front",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quasar/extras": "^1.15.8",
|
"@quasar/extras": "^1.15.11",
|
||||||
"axios": "^1.2.1",
|
"axios": "^1.2.1",
|
||||||
"pinia": "^2.0.28",
|
"pinia": "^2.0.28",
|
||||||
"quasar": "^2.11.1",
|
"quasar": "^2.11.7",
|
||||||
"validator": "^13.7.0",
|
"validator": "^13.7.0",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
"vue-i18n": "^9.2.2",
|
"vue-i18n": "^9.2.2",
|
||||||
|
@ -21,8 +21,8 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
||||||
"@pinia/testing": "^0.0.14",
|
"@pinia/testing": "^0.0.14",
|
||||||
"@quasar/app-vite": "^1.1.3",
|
"@quasar/app-vite": "^1.2.1",
|
||||||
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.1.2",
|
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.2.1",
|
||||||
"@vue/test-utils": "^2.0.0",
|
"@vue/test-utils": "^2.0.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"cypress": "^12.2.0",
|
"cypress": "^12.2.0",
|
||||||
|
@ -770,33 +770,33 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@quasar/app-vite": {
|
"node_modules/@quasar/app-vite": {
|
||||||
"version": "1.1.3",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@quasar/app-vite/-/app-vite-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@quasar/app-vite/-/app-vite-1.2.1.tgz",
|
||||||
"integrity": "sha512-YX6lVkjRFNDbYcORiMtNlDz3jlSf7ldF4zGZk8zaW/Q1CfjaLqpSqCmBP4eta6QXz7To0IdabROYKa55D6IDgA==",
|
"integrity": "sha512-iKvpucrnt5pQuuQxqkLLWuI7Wm9Xx9fcBK1miMHWBgdcbHOXhc/OrlxeLvRwxm7GMd91cp3nhslo5j3a+zMCOg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quasar/fastclick": "1.1.5",
|
"@quasar/fastclick": "1.1.5",
|
||||||
"@quasar/vite-plugin": "^1.2.1",
|
"@quasar/vite-plugin": "^1.3.0",
|
||||||
"@rollup/pluginutils": "^4.1.2",
|
"@rollup/pluginutils": "^4.1.2",
|
||||||
"@types/chrome": "^0.0.191",
|
"@types/chrome": "^0.0.208",
|
||||||
"@types/compression": "^1.7.2",
|
"@types/compression": "^1.7.2",
|
||||||
"@types/cordova": "0.0.34",
|
"@types/cordova": "0.0.34",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@vitejs/plugin-vue": "^2.2.0",
|
"@vitejs/plugin-vue": "^2.2.0",
|
||||||
"archiver": "^5.3.0",
|
"archiver": "^5.3.0",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"ci-info": "^3.3.0",
|
"ci-info": "^3.7.1",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"cross-spawn": "^7.0.3",
|
"cross-spawn": "^7.0.3",
|
||||||
"dot-prop": "6.0.1",
|
"dot-prop": "6.0.1",
|
||||||
"elementtree": "0.1.7",
|
"elementtree": "0.1.7",
|
||||||
"esbuild": "0.14.51",
|
"esbuild": "0.14.51",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"fast-glob": "3.2.11",
|
"fast-glob": "3.2.12",
|
||||||
"fs-extra": "^10.0.1",
|
"fs-extra": "^11.1.0",
|
||||||
"html-minifier": "^4.0.0",
|
"html-minifier": "^4.0.0",
|
||||||
"inquirer": "^8.2.1",
|
"inquirer": "^8.2.1",
|
||||||
"isbinaryfile": "^4.0.8",
|
"isbinaryfile": "^5.0.0",
|
||||||
"kolorist": "^1.5.1",
|
"kolorist": "^1.5.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"minimist": "^1.2.6",
|
"minimist": "^1.2.6",
|
||||||
|
@ -815,7 +815,7 @@
|
||||||
"quasar": "bin/quasar"
|
"quasar": "bin/quasar"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18 || ^16 || ^14.19",
|
"node": "^24 || ^22 || ^20 || ^18 || ^16 || ^14.19",
|
||||||
"npm": ">= 6.14.12",
|
"npm": ">= 6.14.12",
|
||||||
"yarn": ">= 1.17.3"
|
"yarn": ">= 1.17.3"
|
||||||
},
|
},
|
||||||
|
@ -855,26 +855,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@quasar/app-vite/node_modules/fast-glob": {
|
|
||||||
"version": "3.2.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
|
||||||
"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@nodelib/fs.stat": "^2.0.2",
|
|
||||||
"@nodelib/fs.walk": "^1.2.3",
|
|
||||||
"glob-parent": "^5.1.2",
|
|
||||||
"merge2": "^1.3.0",
|
|
||||||
"micromatch": "^4.0.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@quasar/extras": {
|
"node_modules/@quasar/extras": {
|
||||||
"version": "1.15.8",
|
"version": "1.15.11",
|
||||||
"resolved": "https://registry.npmjs.org/@quasar/extras/-/extras-1.15.8.tgz",
|
"resolved": "https://registry.npmjs.org/@quasar/extras/-/extras-1.15.11.tgz",
|
||||||
"integrity": "sha512-UR6Snu7DSYdDOGqcNjUr0FJtKNfPn2Jc2hKTC+y/Y7Gf+vWu0RYUl49cguD33nn+wpbgs28+cvmjx7u3NNogoQ==",
|
"integrity": "sha512-EEXL10EJQmL9jNs5fp0Kd/nyonG8hTODolj+qpYNPG3qEEoy3txr05Pdmp9qJYaXIzP9rOS9FGhFe9Mnq6MBBg==",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://donate.quasar.dev"
|
"url": "https://donate.quasar.dev"
|
||||||
|
@ -891,12 +875,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@quasar/quasar-app-extension-testing-unit-vitest": {
|
"node_modules/@quasar/quasar-app-extension-testing-unit-vitest": {
|
||||||
"version": "0.1.2",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@quasar/quasar-app-extension-testing-unit-vitest/-/quasar-app-extension-testing-unit-vitest-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@quasar/quasar-app-extension-testing-unit-vitest/-/quasar-app-extension-testing-unit-vitest-0.2.1.tgz",
|
||||||
"integrity": "sha512-yqpXu0J8Gttr00UCzEAMptZX8aggqsiG14cgeKLt0Hm3pU9PtwXqjX/EYifgQyldw0b7LbAzpdHhsXU2BU9vpA==",
|
"integrity": "sha512-jR7KgZfujDdfhZe0dcgiA+W2bfm8/NcEbpYuEyr1161Ckn+dL6Y9KZUDEZMUuRYzS+pn1SXsRPMcv8tgXToB4Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"happy-dom": "^5.0.0",
|
"happy-dom": "^8.9.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"vite-jsconfig-paths": "^2.0.1",
|
"vite-jsconfig-paths": "^2.0.1",
|
||||||
"vite-tsconfig-paths": "^3.5.0"
|
"vite-tsconfig-paths": "^3.5.0"
|
||||||
|
@ -907,10 +891,10 @@
|
||||||
"yarn": ">= 1.17.3"
|
"yarn": ">= 1.17.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@vitest/ui": "^0.15.0",
|
"@vitest/ui": "^0.29.1",
|
||||||
"@vue/test-utils": "^2.0.0",
|
"@vue/test-utils": "^2.0.0",
|
||||||
"quasar": "^2.0.0",
|
"quasar": "^2.10.2",
|
||||||
"vitest": "^0.15.0",
|
"vitest": "^0.29.1",
|
||||||
"vue": "^3.2.0"
|
"vue": "^3.2.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
|
@ -977,9 +961,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/chrome": {
|
"node_modules/@types/chrome": {
|
||||||
"version": "0.0.191",
|
"version": "0.0.208",
|
||||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.191.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.208.tgz",
|
||||||
"integrity": "sha512-hXYHJJ1Y265xKCw0o2Kz4CnR8aUhOMdyxK1AinET4EDr3fhpEMvOFDwdqz9LUX4syfTVYWb8w7vfC12s112ehg==",
|
"integrity": "sha512-VDU/JnXkF5qaI7WBz14Azpa2VseZTgML0ia/g/B1sr9OfdOnHiH/zZ7P7qCDqxSlkqJh76/bPc8jLFcx8rHJmw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/filesystem": "*",
|
"@types/filesystem": "*",
|
||||||
|
@ -995,15 +979,6 @@
|
||||||
"@types/express": "*"
|
"@types/express": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/concat-stream": {
|
|
||||||
"version": "1.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz",
|
|
||||||
"integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/connect": {
|
"node_modules/@types/connect": {
|
||||||
"version": "3.4.35",
|
"version": "3.4.35",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
||||||
|
@ -1057,15 +1032,6 @@
|
||||||
"integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==",
|
"integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/form-data": {
|
|
||||||
"version": "0.0.33",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz",
|
|
||||||
"integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/har-format": {
|
"node_modules/@types/har-format": {
|
||||||
"version": "1.2.10",
|
"version": "1.2.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.10.tgz",
|
||||||
|
@ -1509,12 +1475,6 @@
|
||||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/asap": {
|
|
||||||
"version": "2.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
|
||||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/asn1": {
|
"node_modules/asn1": {
|
||||||
"version": "0.2.6",
|
"version": "0.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||||
|
@ -1997,10 +1957,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ci-info": {
|
"node_modules/ci-info": {
|
||||||
"version": "3.7.0",
|
"version": "3.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
|
||||||
"integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==",
|
"integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/sibiraj-s"
|
||||||
|
}
|
||||||
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
|
@ -2257,45 +2223,6 @@
|
||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/concat-stream": {
|
|
||||||
"version": "1.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
|
||||||
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": [
|
|
||||||
"node >= 0.8"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"buffer-from": "^1.0.0",
|
|
||||||
"inherits": "^2.0.3",
|
|
||||||
"readable-stream": "^2.2.2",
|
|
||||||
"typedarray": "^0.0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/concat-stream/node_modules/readable-stream": {
|
|
||||||
"version": "2.3.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
|
||||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"core-util-is": "~1.0.0",
|
|
||||||
"inherits": "~2.0.3",
|
|
||||||
"isarray": "~1.0.0",
|
|
||||||
"process-nextick-args": "~2.0.0",
|
|
||||||
"safe-buffer": "~5.1.1",
|
|
||||||
"string_decoder": "~1.1.1",
|
|
||||||
"util-deprecate": "~1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/concat-stream/node_modules/string_decoder": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": "~5.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
|
@ -3823,9 +3750,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/fs-extra": {
|
"node_modules/fs-extra": {
|
||||||
"version": "10.1.0",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz",
|
||||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
"integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
|
@ -3833,7 +3760,7 @@
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=14.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
|
@ -3894,15 +3821,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/get-port": {
|
|
||||||
"version": "3.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz",
|
|
||||||
"integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-stream": {
|
"node_modules/get-stream": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||||
|
@ -4023,20 +3941,32 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/happy-dom": {
|
"node_modules/happy-dom": {
|
||||||
"version": "5.4.0",
|
"version": "8.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-8.9.0.tgz",
|
||||||
"integrity": "sha512-JxXpBvEUdyCqfRHzHJKtiJ+6+WzTIL6kFCteAOEy13QEnHMD/D5uUIVVw3a4TmQroJriz0gnll4Uv1qZeSz/rA==",
|
"integrity": "sha512-JZwJuGdR7ko8L61136YzmrLv7LgTh5b8XaEM3P709mLjyQuXJ3zHTDXvUtBBahRjGlcYW0zGjIiEWizoTUGKfA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"css.escape": "^1.5.1",
|
"css.escape": "^1.5.1",
|
||||||
"he": "^1.2.0",
|
"he": "^1.2.0",
|
||||||
|
"iconv-lite": "^0.6.3",
|
||||||
"node-fetch": "^2.x.x",
|
"node-fetch": "^2.x.x",
|
||||||
"sync-request": "^6.1.0",
|
|
||||||
"webidl-conversions": "^7.0.0",
|
"webidl-conversions": "^7.0.0",
|
||||||
"whatwg-encoding": "^2.0.0",
|
"whatwg-encoding": "^2.0.0",
|
||||||
"whatwg-mimetype": "^3.0.0"
|
"whatwg-mimetype": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/happy-dom/node_modules/iconv-lite": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/has": {
|
"node_modules/has": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
|
@ -4100,21 +4030,6 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/http-basic": {
|
|
||||||
"version": "8.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz",
|
|
||||||
"integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"caseless": "^0.12.0",
|
|
||||||
"concat-stream": "^1.6.2",
|
|
||||||
"http-response-object": "^3.0.1",
|
|
||||||
"parse-cache-control": "^1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/http-errors": {
|
"node_modules/http-errors": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||||
|
@ -4131,21 +4046,6 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/http-response-object": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "^10.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/http-response-object/node_modules/@types/node": {
|
|
||||||
"version": "10.17.60",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
|
|
||||||
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/http-signature": {
|
"node_modules/http-signature": {
|
||||||
"version": "1.3.6",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
|
||||||
|
@ -4498,12 +4398,12 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/isbinaryfile": {
|
"node_modules/isbinaryfile": {
|
||||||
"version": "4.0.10",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.0.tgz",
|
||||||
"integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
|
"integrity": "sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8.0.0"
|
"node": ">= 14.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/gjtorikian/"
|
"url": "https://github.com/sponsors/gjtorikian/"
|
||||||
|
@ -5469,12 +5369,6 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/parse-cache-control": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/parseurl": {
|
"node_modules/parseurl": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
@ -5736,15 +5630,6 @@
|
||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/promise": {
|
|
||||||
"version": "8.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz",
|
|
||||||
"integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"asap": "~2.0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
|
@ -5804,9 +5689,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/quasar": {
|
"node_modules/quasar": {
|
||||||
"version": "2.11.1",
|
"version": "2.11.7",
|
||||||
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.11.7.tgz",
|
||||||
"integrity": "sha512-3whiJaOXbectd2R6Vfjr/vHKeY92zdDpx41zfZ5vrG+Yx16Q76euUQsdKlYr1PD7q33yvHvjegxuILgKJg5GqA==",
|
"integrity": "sha512-OzDwYfoSwZbwqfNWz9Bfn1mhGE8YMxpTLhPvVkQU87ePF6qFj4aWttcTUifXITKldOAZziN1Mmv8VLQyITHwiw==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.18.1",
|
"node": ">= 10.18.1",
|
||||||
"npm": ">= 6.13.4",
|
"npm": ">= 6.13.4",
|
||||||
|
@ -6608,29 +6493,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sync-request": {
|
|
||||||
"version": "6.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz",
|
|
||||||
"integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"http-response-object": "^3.0.1",
|
|
||||||
"sync-rpc": "^1.2.1",
|
|
||||||
"then-request": "^6.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sync-rpc": {
|
|
||||||
"version": "1.3.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz",
|
|
||||||
"integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"get-port": "^3.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/table": {
|
"node_modules/table": {
|
||||||
"version": "6.8.1",
|
"version": "6.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
|
||||||
|
@ -6691,48 +6553,6 @@
|
||||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/then-request": {
|
|
||||||
"version": "6.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz",
|
|
||||||
"integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/concat-stream": "^1.6.0",
|
|
||||||
"@types/form-data": "0.0.33",
|
|
||||||
"@types/node": "^8.0.0",
|
|
||||||
"@types/qs": "^6.2.31",
|
|
||||||
"caseless": "~0.12.0",
|
|
||||||
"concat-stream": "^1.6.0",
|
|
||||||
"form-data": "^2.2.0",
|
|
||||||
"http-basic": "^8.1.1",
|
|
||||||
"http-response-object": "^3.0.1",
|
|
||||||
"promise": "^8.0.0",
|
|
||||||
"qs": "^6.4.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/then-request/node_modules/@types/node": {
|
|
||||||
"version": "8.10.66",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz",
|
|
||||||
"integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/then-request/node_modules/form-data": {
|
|
||||||
"version": "2.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
|
|
||||||
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"asynckit": "^0.4.0",
|
|
||||||
"combined-stream": "^1.0.6",
|
|
||||||
"mime-types": "^2.1.12"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/thenify": {
|
"node_modules/thenify": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||||
|
@ -6930,12 +6750,6 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typedarray": {
|
|
||||||
"version": "0.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
|
||||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/ufo": {
|
"node_modules/ufo": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz",
|
||||||
|
@ -8076,33 +7890,33 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@quasar/app-vite": {
|
"@quasar/app-vite": {
|
||||||
"version": "1.1.3",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@quasar/app-vite/-/app-vite-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@quasar/app-vite/-/app-vite-1.2.1.tgz",
|
||||||
"integrity": "sha512-YX6lVkjRFNDbYcORiMtNlDz3jlSf7ldF4zGZk8zaW/Q1CfjaLqpSqCmBP4eta6QXz7To0IdabROYKa55D6IDgA==",
|
"integrity": "sha512-iKvpucrnt5pQuuQxqkLLWuI7Wm9Xx9fcBK1miMHWBgdcbHOXhc/OrlxeLvRwxm7GMd91cp3nhslo5j3a+zMCOg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@quasar/fastclick": "1.1.5",
|
"@quasar/fastclick": "1.1.5",
|
||||||
"@quasar/vite-plugin": "^1.2.1",
|
"@quasar/vite-plugin": "^1.3.0",
|
||||||
"@rollup/pluginutils": "^4.1.2",
|
"@rollup/pluginutils": "^4.1.2",
|
||||||
"@types/chrome": "^0.0.191",
|
"@types/chrome": "^0.0.208",
|
||||||
"@types/compression": "^1.7.2",
|
"@types/compression": "^1.7.2",
|
||||||
"@types/cordova": "0.0.34",
|
"@types/cordova": "0.0.34",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
"archiver": "^5.3.0",
|
"archiver": "^5.3.0",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"ci-info": "^3.3.0",
|
"ci-info": "^3.7.1",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"cross-spawn": "^7.0.3",
|
"cross-spawn": "^7.0.3",
|
||||||
"dot-prop": "6.0.1",
|
"dot-prop": "6.0.1",
|
||||||
"elementtree": "0.1.7",
|
"elementtree": "0.1.7",
|
||||||
"esbuild": "0.14.51",
|
"esbuild": "0.14.51",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"fast-glob": "3.2.11",
|
"fast-glob": "3.2.12",
|
||||||
"fs-extra": "^10.0.1",
|
"fs-extra": "^11.1.0",
|
||||||
"html-minifier": "^4.0.0",
|
"html-minifier": "^4.0.0",
|
||||||
"inquirer": "^8.2.1",
|
"inquirer": "^8.2.1",
|
||||||
"isbinaryfile": "^4.0.8",
|
"isbinaryfile": "^5.0.0",
|
||||||
"kolorist": "^1.5.1",
|
"kolorist": "^1.5.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"minimist": "^1.2.6",
|
"minimist": "^1.2.6",
|
||||||
|
@ -8116,27 +7930,12 @@
|
||||||
"table": "^6.8.0",
|
"table": "^6.8.0",
|
||||||
"vite": "^4.0.3",
|
"vite": "^4.0.3",
|
||||||
"webpack-merge": "^5.8.0"
|
"webpack-merge": "^5.8.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"fast-glob": {
|
|
||||||
"version": "3.2.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
|
||||||
"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@nodelib/fs.stat": "^2.0.2",
|
|
||||||
"@nodelib/fs.walk": "^1.2.3",
|
|
||||||
"glob-parent": "^5.1.2",
|
|
||||||
"merge2": "^1.3.0",
|
|
||||||
"micromatch": "^4.0.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@quasar/extras": {
|
"@quasar/extras": {
|
||||||
"version": "1.15.8",
|
"version": "1.15.11",
|
||||||
"resolved": "https://registry.npmjs.org/@quasar/extras/-/extras-1.15.8.tgz",
|
"resolved": "https://registry.npmjs.org/@quasar/extras/-/extras-1.15.11.tgz",
|
||||||
"integrity": "sha512-UR6Snu7DSYdDOGqcNjUr0FJtKNfPn2Jc2hKTC+y/Y7Gf+vWu0RYUl49cguD33nn+wpbgs28+cvmjx7u3NNogoQ=="
|
"integrity": "sha512-EEXL10EJQmL9jNs5fp0Kd/nyonG8hTODolj+qpYNPG3qEEoy3txr05Pdmp9qJYaXIzP9rOS9FGhFe9Mnq6MBBg=="
|
||||||
},
|
},
|
||||||
"@quasar/fastclick": {
|
"@quasar/fastclick": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
|
@ -8145,12 +7944,12 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@quasar/quasar-app-extension-testing-unit-vitest": {
|
"@quasar/quasar-app-extension-testing-unit-vitest": {
|
||||||
"version": "0.1.2",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@quasar/quasar-app-extension-testing-unit-vitest/-/quasar-app-extension-testing-unit-vitest-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@quasar/quasar-app-extension-testing-unit-vitest/-/quasar-app-extension-testing-unit-vitest-0.2.1.tgz",
|
||||||
"integrity": "sha512-yqpXu0J8Gttr00UCzEAMptZX8aggqsiG14cgeKLt0Hm3pU9PtwXqjX/EYifgQyldw0b7LbAzpdHhsXU2BU9vpA==",
|
"integrity": "sha512-jR7KgZfujDdfhZe0dcgiA+W2bfm8/NcEbpYuEyr1161Ckn+dL6Y9KZUDEZMUuRYzS+pn1SXsRPMcv8tgXToB4Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"happy-dom": "^5.0.0",
|
"happy-dom": "^8.9.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"vite-jsconfig-paths": "^2.0.1",
|
"vite-jsconfig-paths": "^2.0.1",
|
||||||
"vite-tsconfig-paths": "^3.5.0"
|
"vite-tsconfig-paths": "^3.5.0"
|
||||||
|
@ -8199,9 +7998,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/chrome": {
|
"@types/chrome": {
|
||||||
"version": "0.0.191",
|
"version": "0.0.208",
|
||||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.191.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.208.tgz",
|
||||||
"integrity": "sha512-hXYHJJ1Y265xKCw0o2Kz4CnR8aUhOMdyxK1AinET4EDr3fhpEMvOFDwdqz9LUX4syfTVYWb8w7vfC12s112ehg==",
|
"integrity": "sha512-VDU/JnXkF5qaI7WBz14Azpa2VseZTgML0ia/g/B1sr9OfdOnHiH/zZ7P7qCDqxSlkqJh76/bPc8jLFcx8rHJmw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/filesystem": "*",
|
"@types/filesystem": "*",
|
||||||
|
@ -8217,15 +8016,6 @@
|
||||||
"@types/express": "*"
|
"@types/express": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/concat-stream": {
|
|
||||||
"version": "1.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz",
|
|
||||||
"integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/connect": {
|
"@types/connect": {
|
||||||
"version": "3.4.35",
|
"version": "3.4.35",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
||||||
|
@ -8279,15 +8069,6 @@
|
||||||
"integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==",
|
"integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/form-data": {
|
|
||||||
"version": "0.0.33",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz",
|
|
||||||
"integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/har-format": {
|
"@types/har-format": {
|
||||||
"version": "1.2.10",
|
"version": "1.2.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.10.tgz",
|
||||||
|
@ -8656,12 +8437,6 @@
|
||||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"asap": {
|
|
||||||
"version": "2.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
|
||||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"asn1": {
|
"asn1": {
|
||||||
"version": "0.2.6",
|
"version": "0.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||||
|
@ -9004,9 +8779,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ci-info": {
|
"ci-info": {
|
||||||
"version": "3.7.0",
|
"version": "3.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
|
||||||
"integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==",
|
"integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"clean-css": {
|
"clean-css": {
|
||||||
|
@ -9206,44 +8981,6 @@
|
||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"concat-stream": {
|
|
||||||
"version": "1.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
|
||||||
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"buffer-from": "^1.0.0",
|
|
||||||
"inherits": "^2.0.3",
|
|
||||||
"readable-stream": "^2.2.2",
|
|
||||||
"typedarray": "^0.0.6"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"readable-stream": {
|
|
||||||
"version": "2.3.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
|
||||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"core-util-is": "~1.0.0",
|
|
||||||
"inherits": "~2.0.3",
|
|
||||||
"isarray": "~1.0.0",
|
|
||||||
"process-nextick-args": "~2.0.0",
|
|
||||||
"safe-buffer": "~5.1.1",
|
|
||||||
"string_decoder": "~1.1.1",
|
|
||||||
"util-deprecate": "~1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"string_decoder": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"safe-buffer": "~5.1.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"content-disposition": {
|
"content-disposition": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
|
@ -10302,9 +10039,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "10.1.0",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz",
|
||||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
"integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
|
@ -10354,12 +10091,6 @@
|
||||||
"has-symbols": "^1.0.3"
|
"has-symbols": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"get-port": {
|
|
||||||
"version": "3.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz",
|
|
||||||
"integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"get-stream": {
|
"get-stream": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||||
|
@ -10453,18 +10184,29 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"happy-dom": {
|
"happy-dom": {
|
||||||
"version": "5.4.0",
|
"version": "8.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-8.9.0.tgz",
|
||||||
"integrity": "sha512-JxXpBvEUdyCqfRHzHJKtiJ+6+WzTIL6kFCteAOEy13QEnHMD/D5uUIVVw3a4TmQroJriz0gnll4Uv1qZeSz/rA==",
|
"integrity": "sha512-JZwJuGdR7ko8L61136YzmrLv7LgTh5b8XaEM3P709mLjyQuXJ3zHTDXvUtBBahRjGlcYW0zGjIiEWizoTUGKfA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"css.escape": "^1.5.1",
|
"css.escape": "^1.5.1",
|
||||||
"he": "^1.2.0",
|
"he": "^1.2.0",
|
||||||
|
"iconv-lite": "^0.6.3",
|
||||||
"node-fetch": "^2.x.x",
|
"node-fetch": "^2.x.x",
|
||||||
"sync-request": "^6.1.0",
|
|
||||||
"webidl-conversions": "^7.0.0",
|
"webidl-conversions": "^7.0.0",
|
||||||
"whatwg-encoding": "^2.0.0",
|
"whatwg-encoding": "^2.0.0",
|
||||||
"whatwg-mimetype": "^3.0.0"
|
"whatwg-mimetype": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"has": {
|
"has": {
|
||||||
|
@ -10509,18 +10251,6 @@
|
||||||
"uglify-js": "^3.5.1"
|
"uglify-js": "^3.5.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"http-basic": {
|
|
||||||
"version": "8.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz",
|
|
||||||
"integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"caseless": "^0.12.0",
|
|
||||||
"concat-stream": "^1.6.2",
|
|
||||||
"http-response-object": "^3.0.1",
|
|
||||||
"parse-cache-control": "^1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http-errors": {
|
"http-errors": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||||
|
@ -10534,23 +10264,6 @@
|
||||||
"toidentifier": "1.0.1"
|
"toidentifier": "1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"http-response-object": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/node": "^10.0.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": {
|
|
||||||
"version": "10.17.60",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
|
|
||||||
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http-signature": {
|
"http-signature": {
|
||||||
"version": "1.3.6",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
|
||||||
|
@ -10793,9 +10506,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"isbinaryfile": {
|
"isbinaryfile": {
|
||||||
"version": "4.0.10",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.0.tgz",
|
||||||
"integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
|
"integrity": "sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"isexe": {
|
"isexe": {
|
||||||
|
@ -11540,12 +11253,6 @@
|
||||||
"callsites": "^3.0.0"
|
"callsites": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"parse-cache-control": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"parseurl": {
|
"parseurl": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
@ -11715,15 +11422,6 @@
|
||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"promise": {
|
|
||||||
"version": "8.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz",
|
|
||||||
"integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"asap": "~2.0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"proxy-addr": {
|
"proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
|
@ -11771,9 +11469,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"quasar": {
|
"quasar": {
|
||||||
"version": "2.11.1",
|
"version": "2.11.7",
|
||||||
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.11.7.tgz",
|
||||||
"integrity": "sha512-3whiJaOXbectd2R6Vfjr/vHKeY92zdDpx41zfZ5vrG+Yx16Q76euUQsdKlYr1PD7q33yvHvjegxuILgKJg5GqA=="
|
"integrity": "sha512-OzDwYfoSwZbwqfNWz9Bfn1mhGE8YMxpTLhPvVkQU87ePF6qFj4aWttcTUifXITKldOAZziN1Mmv8VLQyITHwiw=="
|
||||||
},
|
},
|
||||||
"queue-microtask": {
|
"queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
|
@ -12355,26 +12053,6 @@
|
||||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"sync-request": {
|
|
||||||
"version": "6.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz",
|
|
||||||
"integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"http-response-object": "^3.0.1",
|
|
||||||
"sync-rpc": "^1.2.1",
|
|
||||||
"then-request": "^6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sync-rpc": {
|
|
||||||
"version": "1.3.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz",
|
|
||||||
"integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"get-port": "^3.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"table": {
|
"table": {
|
||||||
"version": "6.8.1",
|
"version": "6.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
|
||||||
|
@ -12427,44 +12105,6 @@
|
||||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"then-request": {
|
|
||||||
"version": "6.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz",
|
|
||||||
"integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/concat-stream": "^1.6.0",
|
|
||||||
"@types/form-data": "0.0.33",
|
|
||||||
"@types/node": "^8.0.0",
|
|
||||||
"@types/qs": "^6.2.31",
|
|
||||||
"caseless": "~0.12.0",
|
|
||||||
"concat-stream": "^1.6.0",
|
|
||||||
"form-data": "^2.2.0",
|
|
||||||
"http-basic": "^8.1.1",
|
|
||||||
"http-response-object": "^3.0.1",
|
|
||||||
"promise": "^8.0.0",
|
|
||||||
"qs": "^6.4.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": {
|
|
||||||
"version": "8.10.66",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz",
|
|
||||||
"integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"form-data": {
|
|
||||||
"version": "2.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
|
|
||||||
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"asynckit": "^0.4.0",
|
|
||||||
"combined-stream": "^1.0.6",
|
|
||||||
"mime-types": "^2.1.12"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"thenify": {
|
"thenify": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||||
|
@ -12623,12 +12263,6 @@
|
||||||
"mime-types": "~2.1.24"
|
"mime-types": "~2.1.24"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typedarray": {
|
|
||||||
"version": "0.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
|
||||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"ufo": {
|
"ufo": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz",
|
||||||
|
|
|
@ -15,10 +15,10 @@
|
||||||
"test:unit:ci": "vitest run"
|
"test:unit:ci": "vitest run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quasar/extras": "^1.15.8",
|
"@quasar/extras": "^1.15.11",
|
||||||
"axios": "^1.2.1",
|
"axios": "^1.2.1",
|
||||||
"pinia": "^2.0.28",
|
"pinia": "^2.0.28",
|
||||||
"quasar": "^2.11.1",
|
"quasar": "^2.11.7",
|
||||||
"validator": "^13.7.0",
|
"validator": "^13.7.0",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
"vue-i18n": "^9.2.2",
|
"vue-i18n": "^9.2.2",
|
||||||
|
@ -28,8 +28,8 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
||||||
"@pinia/testing": "^0.0.14",
|
"@pinia/testing": "^0.0.14",
|
||||||
"@quasar/app-vite": "^1.1.3",
|
"@quasar/app-vite": "^1.2.1",
|
||||||
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.1.2",
|
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.2.1",
|
||||||
"@vue/test-utils": "^2.0.0",
|
"@vue/test-utils": "^2.0.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"cypress": "^12.2.0",
|
"cypress": "^12.2.0",
|
||||||
|
|
|
@ -30,12 +30,12 @@ quasar.iconMapFn = (iconName) => {
|
||||||
const name = iconName.substring(3);
|
const name = iconName.substring(3);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cls: `icon-${name}`,
|
cls: `icon-${name} notranslate`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cls: 'material-symbols-outlined',
|
cls: 'material-symbols-outlined notranslate',
|
||||||
content: iconName,
|
content: iconName,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,11 +3,12 @@ import { createI18n } from 'vue-i18n';
|
||||||
import messages from 'src/i18n';
|
import messages from 'src/i18n';
|
||||||
|
|
||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
||||||
locale: 'en',
|
locale: navigator.language || navigator.userLanguage,
|
||||||
fallbackLocale: 'en',
|
fallbackLocale: 'en',
|
||||||
globalInjection: true,
|
globalInjection: true,
|
||||||
messages,
|
messages,
|
||||||
missingWarn: false,
|
missingWarn: false,
|
||||||
|
fallbackWarn: false,
|
||||||
legacy: false,
|
legacy: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import axios from 'axios';
|
||||||
import { onMounted, onUnmounted, computed, ref, watch } from 'vue';
|
import { onMounted, onUnmounted, computed, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
import { useValidator } from 'src/composables/useValidator';
|
import { useValidator } from 'src/composables/useValidator';
|
||||||
import SkeletonForm from 'components/ui/SkeletonForm.vue';
|
import SkeletonForm from 'components/ui/SkeletonForm.vue';
|
||||||
|
@ -44,6 +43,7 @@ const isLoading = ref(false);
|
||||||
const hasChanges = ref(false);
|
const hasChanges = ref(false);
|
||||||
const formData = computed(() => state.get($props.model));
|
const formData = computed(() => state.get($props.model));
|
||||||
const originalData = ref();
|
const originalData = ref();
|
||||||
|
const formUrl = computed(() => $props.url);
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
const { data } = await axios.get($props.url, {
|
const { data } = await axios.get($props.url, {
|
||||||
|
@ -91,6 +91,12 @@ function filter(value, update, filterOptions) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(formUrl, async () => {
|
||||||
|
originalData.value = null;
|
||||||
|
reset();
|
||||||
|
fetch();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<q-banner v-if="hasChanges" class="text-white bg-warning">
|
<q-banner v-if="hasChanges" class="text-white bg-warning">
|
||||||
|
|
|
@ -1,43 +1,49 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useState } from 'src/composables/useState';
|
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
import UserPanel from 'components/UserPanel.vue';
|
import UserPanel from 'components/UserPanel.vue';
|
||||||
|
import { useState } from 'src/composables/useState';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import PinnedModules from './PinnedModules.vue';
|
import PinnedModules from './PinnedModules.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
|
const stateStore = useStateStore();
|
||||||
const state = useState();
|
const state = useState();
|
||||||
const user = state.getUser();
|
const user = state.getUser();
|
||||||
const token = session.getToken();
|
const token = session.getToken();
|
||||||
|
const appName = 'Lilium';
|
||||||
|
|
||||||
onMounted(() => (state.headerMounted.value = true));
|
onMounted(() => stateStore.setMounted());
|
||||||
|
|
||||||
function onToggleDrawer() {
|
|
||||||
state.drawer.value = !state.drawer.value;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-header class="bg-dark" color="white" elevated>
|
<q-header class="bg-dark" color="white" elevated>
|
||||||
<q-toolbar class="q-py-sm q-px-md">
|
<q-toolbar class="q-py-sm q-px-md">
|
||||||
<q-btn flat @click="onToggleDrawer()" round dense icon="menu">
|
<q-btn flat @click="stateStore.toggleLeftDrawer()" round dense icon="menu">
|
||||||
<q-tooltip bottom anchor="bottom right">
|
<q-tooltip bottom anchor="bottom right">
|
||||||
{{ t('globals.collapseMenu') }}
|
{{ t('globals.collapseMenu') }}
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<router-link to="/">
|
<router-link to="/">
|
||||||
<q-btn flat round class="q-ml-xs" v-if="$q.screen.gt.xs">
|
<q-btn class="q-ml-xs" color="primary" v-if="$q.screen.gt.xs" flat round>
|
||||||
<q-avatar square size="md">
|
<q-avatar square size="md">
|
||||||
<q-img src="~/assets/logo_icon.svg" alt="Logo" />
|
<q-img
|
||||||
|
src="~/assets/logo_icon.svg"
|
||||||
|
:alt="appName"
|
||||||
|
spinner-color="primary"
|
||||||
|
/>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
<q-tooltip bottom>
|
<q-tooltip bottom>
|
||||||
{{ t('globals.backToDashboard') }}
|
{{ t('globals.backToDashboard') }}
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</router-link>
|
</router-link>
|
||||||
<q-toolbar-title shrink class="text-weight-bold">Salix</q-toolbar-title>
|
<q-toolbar-title shrink class="text-weight-bold" v-if="$q.screen.gt.xs">
|
||||||
|
{{ appName }}
|
||||||
|
<q-badge label="Beta" align="top" />
|
||||||
|
</q-toolbar-title>
|
||||||
<q-space></q-space>
|
<q-space></q-space>
|
||||||
<div id="searchbar"></div>
|
<div id="searchbar"></div>
|
||||||
<q-space></q-space>
|
<q-space></q-space>
|
||||||
|
@ -49,11 +55,22 @@ function onToggleDrawer() {
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
<PinnedModules />
|
<PinnedModules />
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
@click="stateStore.toggleRightDrawer()"
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="menu"
|
||||||
|
>
|
||||||
|
<q-tooltip bottom anchor="bottom right">
|
||||||
|
{{ t('globals.collapseMenu') }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
<q-btn rounded dense flat no-wrap id="user">
|
<q-btn rounded dense flat no-wrap id="user">
|
||||||
<q-avatar size="lg">
|
<q-avatar size="lg">
|
||||||
<q-img
|
<q-img
|
||||||
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
|
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
|
||||||
spinner-color="white"
|
spinner-color="primary"
|
||||||
>
|
>
|
||||||
</q-img>
|
</q-img>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import axios from 'axios';
|
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { onMounted, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const $props = defineProps({
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
autoLoad: {
|
autoLoad: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -26,15 +30,11 @@ const $props = defineProps({
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
sortBy: {
|
order: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
limit: {
|
limit: {
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
rowsPerPage: {
|
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 10,
|
default: 10,
|
||||||
},
|
},
|
||||||
|
@ -45,71 +45,55 @@ const $props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch', 'onPaginate']);
|
const emit = defineEmits(['onFetch', 'onPaginate']);
|
||||||
defineExpose({ refresh });
|
const isLoading = ref(false);
|
||||||
|
const pagination = ref({
|
||||||
|
sortBy: props.order,
|
||||||
|
rowsPerPage: props.limit,
|
||||||
|
page: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const arrayData = useArrayData(props.dataKey, {
|
||||||
|
url: props.url,
|
||||||
|
filter: props.filter,
|
||||||
|
where: props.where,
|
||||||
|
limit: props.limit,
|
||||||
|
order: props.order,
|
||||||
|
});
|
||||||
|
const store = arrayData.store;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if ($props.autoLoad) paginate();
|
if (props.autoLoad) fetch();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => $props.data,
|
() => props.data,
|
||||||
() => {
|
() => {
|
||||||
rows.value = $props.data;
|
store.data = props.data;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const isLoading = ref(false);
|
|
||||||
const hasMoreData = ref(false);
|
|
||||||
const pagination = ref({
|
|
||||||
sortBy: $props.sortBy,
|
|
||||||
rowsPerPage: $props.rowsPerPage,
|
|
||||||
page: 1,
|
|
||||||
});
|
|
||||||
const rows = ref(null);
|
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
const { page, rowsPerPage, sortBy } = pagination.value;
|
await arrayData.fetch({ append: false });
|
||||||
|
if (!arrayData.hasMoreData.value) {
|
||||||
if (!$props.url) return;
|
|
||||||
|
|
||||||
const filter = {
|
|
||||||
limit: rowsPerPage,
|
|
||||||
skip: rowsPerPage * (page - 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.assign(filter, $props.filter);
|
|
||||||
|
|
||||||
if ($props.where) filter.where = $props.where;
|
|
||||||
if ($props.sortBy) filter.order = $props.sortBy;
|
|
||||||
if ($props.limit) filter.limit = $props.limit;
|
|
||||||
|
|
||||||
if (sortBy) filter.order = sortBy;
|
|
||||||
|
|
||||||
const { data } = await axios.get($props.url, {
|
|
||||||
params: { filter },
|
|
||||||
});
|
|
||||||
|
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
emit('onFetch', store.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function paginate() {
|
async function paginate() {
|
||||||
const { page, rowsPerPage, sortBy, descending } = pagination.value;
|
const { page, rowsPerPage, sortBy, descending } = pagination.value;
|
||||||
|
|
||||||
const data = await fetch();
|
if (!props.url) return;
|
||||||
|
|
||||||
if (!data) {
|
await arrayData.loadMore();
|
||||||
|
|
||||||
|
if (!arrayData.hasMoreData.value) {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasMoreData.value = data.length === rowsPerPage;
|
pagination.value.rowsNumber = store.data.length;
|
||||||
|
|
||||||
if (!rows.value) rows.value = [];
|
|
||||||
for (const row of data) rows.value.push(row);
|
|
||||||
|
|
||||||
pagination.value.rowsNumber = rows.value.length;
|
|
||||||
pagination.value.page = page;
|
pagination.value.page = page;
|
||||||
pagination.value.rowsPerPage = rowsPerPage;
|
pagination.value.rowsPerPage = rowsPerPage;
|
||||||
pagination.value.sortBy = sortBy;
|
pagination.value.sortBy = sortBy;
|
||||||
|
@ -117,58 +101,37 @@ async function paginate() {
|
||||||
|
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
|
||||||
emit('onFetch', rows);
|
emit('onFetch', store.data);
|
||||||
emit('onPaginate', data);
|
emit('onPaginate');
|
||||||
}
|
|
||||||
|
|
||||||
async function refresh() {
|
|
||||||
const { rowsPerPage } = pagination.value;
|
|
||||||
|
|
||||||
const data = await fetch();
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
isLoading.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasMoreData.value = data.length === rowsPerPage;
|
|
||||||
|
|
||||||
if (!rows.value) rows.value = [];
|
|
||||||
rows.value = data;
|
|
||||||
|
|
||||||
isLoading.value = false;
|
|
||||||
|
|
||||||
emit('onFetch', rows);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onLoad(...params) {
|
async function onLoad(...params) {
|
||||||
|
if (!store.data) return;
|
||||||
|
|
||||||
const done = params[1];
|
const done = params[1];
|
||||||
if (!rows.value || rows.value.length === 0 || !$props.url) return done(false);
|
if (store.data.length === 0 || !props.url) return done(false);
|
||||||
|
|
||||||
pagination.value.page = pagination.value.page + 1;
|
pagination.value.page = pagination.value.page + 1;
|
||||||
|
|
||||||
await paginate();
|
await paginate();
|
||||||
|
|
||||||
const endOfPages = !hasMoreData.value;
|
const endOfPages = !arrayData.hasMoreData.value;
|
||||||
done(endOfPages);
|
done(endOfPages);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-infinite-scroll @load="onLoad" :offset="offset" class="column items-center">
|
<div class="column items-center">
|
||||||
<div v-if="rows" class="card-list q-gutter-y-md">
|
<div
|
||||||
<slot name="body" :rows="rows"></slot>
|
v-if="store.data && store.data.length === 0 && !isLoading"
|
||||||
<div v-if="!rows.length && !isLoading" class="info-row q-pa-md text-center">
|
class="info-row q-pa-md text-center"
|
||||||
|
>
|
||||||
<h5>
|
<h5>
|
||||||
{{ t('components.smartCard.noData') }}
|
{{ t('components.smartCard.noData') }}
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isLoading" class="info-row q-pa-md text-center">
|
<div v-if="props.autoLoad && !store.data" class="card-list q-gutter-y-md">
|
||||||
<q-spinner color="orange" size="md" />
|
<q-card class="card" v-for="$index in $props.limit" :key="$index">
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="!rows" class="card-list q-gutter-y-md">
|
|
||||||
<q-card class="card" v-for="$index in $props.rowsPerPage" :key="$index">
|
|
||||||
<q-item v-ripple class="q-pa-none items-start cursor-pointer q-hoverable">
|
<q-item v-ripple class="q-pa-none items-start cursor-pointer q-hoverable">
|
||||||
<q-item-section class="q-pa-md">
|
<q-item-section class="q-pa-md">
|
||||||
<q-skeleton type="rect" class="q-mb-md" square />
|
<q-skeleton type="rect" class="q-mb-md" square />
|
||||||
|
@ -186,6 +149,19 @@ async function onLoad(...params) {
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-infinite-scroll
|
||||||
|
v-if="store.data"
|
||||||
|
@load="onLoad"
|
||||||
|
:offset="offset"
|
||||||
|
class="column items-center"
|
||||||
|
>
|
||||||
|
<div v-if="store" class="card-list q-gutter-y-md">
|
||||||
|
<slot name="body" :rows="store.data"></slot>
|
||||||
|
<div v-if="isLoading" class="info-row q-pa-md text-center">
|
||||||
|
<q-spinner color="orange" size="md" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</q-infinite-scroll>
|
</q-infinite-scroll>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -34,19 +34,26 @@ async function confirm() {
|
||||||
<q-dialog ref="dialogRef" persistent>
|
<q-dialog ref="dialogRef" persistent>
|
||||||
<q-card class="q-pa-sm">
|
<q-card class="q-pa-sm">
|
||||||
<q-card-section class="row items-center q-pb-none">
|
<q-card-section class="row items-center q-pb-none">
|
||||||
<span class="text-h6 text-grey">{{ t('sendEmailNotification') }}</span>
|
<span class="text-h6 text-grey">{{
|
||||||
|
t('Send email notification: Send email notification')
|
||||||
|
}}</span>
|
||||||
<q-space />
|
<q-space />
|
||||||
<q-btn icon="close" flat round dense v-close-popup />
|
<q-btn icon="close" flat round dense v-close-popup />
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="row items-center">
|
<q-card-section class="row items-center">
|
||||||
{{ t('notifyAddress') }}
|
{{ t('The notification will be sent to the following address') }}
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="q-pt-none">
|
<q-card-section class="q-pt-none">
|
||||||
<q-input dense v-model="address" rounded outlined autofocus />
|
<q-input dense v-model="address" rounded outlined autofocus />
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-actions align="right">
|
<q-card-actions align="right">
|
||||||
<q-btn :label="t('globals.cancel')" color="primary" flat v-close-popup />
|
<q-btn :label="t('globals.cancel')" color="primary" flat v-close-popup />
|
||||||
<q-btn :label="t('globals.confirm')" color="primary" :loading="isLoading" @click="confirm" />
|
<q-btn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
:loading="isLoading"
|
||||||
|
@click="confirm"
|
||||||
|
/>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
@ -59,14 +66,7 @@ async function confirm() {
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
{
|
es:
|
||||||
"en": {
|
Send email notification: Enviar notificación por correo,
|
||||||
"sendEmailNotification": "Send email notification",
|
The notification will be sent to the following address: La notificación se enviará a la siguiente dirección
|
||||||
"notifyAddress": "The notification will be sent to the following address"
|
|
||||||
},
|
|
||||||
"es": {
|
|
||||||
"sendEmailNotification": "Enviar notificación por correo",
|
|
||||||
"notifyAddress": "La notificación se enviará a la siguiente dirección"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -1,81 +1,106 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useSlots } from 'vue';
|
import { onMounted, useSlots, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import axios from 'axios';
|
||||||
|
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
||||||
|
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
module: {
|
module: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
data: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
onMounted(() => fetch());
|
||||||
|
|
||||||
|
const entity = ref();
|
||||||
|
async function fetch() {
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
if (props.filter) params.filter = props.filter;
|
||||||
|
|
||||||
|
const { data } = await axios.get(props.url, { params });
|
||||||
|
entity.value = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(props, async () => {
|
||||||
|
entity.value = null;
|
||||||
|
await fetch();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="descriptor">
|
<div class="descriptor">
|
||||||
|
<template v-if="entity">
|
||||||
<div class="header bg-primary q-pa-sm">
|
<div class="header bg-primary q-pa-sm">
|
||||||
<router-link :to="{ name: `${module}List` }">
|
<router-link :to="{ name: `${module}List` }">
|
||||||
<q-btn round flat dense size="md" icon="view_list" color="white">
|
<q-btn round flat dense size="md" icon="view_list" color="white">
|
||||||
<q-tooltip>{{ t('components.cardDescriptor.mainList') }}</q-tooltip>
|
<q-tooltip>{{
|
||||||
|
t('components.cardDescriptor.mainList')
|
||||||
|
}}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link :to="{ name: `${module}Summary`, params: { id: data.id } }">
|
<router-link
|
||||||
|
:to="{ name: `${module}Summary`, params: { id: entity.id } }"
|
||||||
|
>
|
||||||
<q-btn round flat dense size="md" icon="launch" color="white">
|
<q-btn round flat dense size="md" icon="launch" color="white">
|
||||||
<q-tooltip>{{ t('components.cardDescriptor.summary') }}</q-tooltip>
|
<q-tooltip>{{
|
||||||
|
t('components.cardDescriptor.summary')
|
||||||
|
}}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<q-btn v-if="slots.menu" size="md" icon="more_vert" color="white" round flat dense>
|
<q-btn
|
||||||
<q-tooltip>{{ t('components.cardDescriptor.moreOptions') }}</q-tooltip>
|
v-if="slots.menu"
|
||||||
|
size="md"
|
||||||
|
icon="more_vert"
|
||||||
|
color="white"
|
||||||
|
round
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
{{ t('components.cardDescriptor.moreOptions') }}
|
||||||
|
</q-tooltip>
|
||||||
<q-menu>
|
<q-menu>
|
||||||
<q-list>
|
<q-list>
|
||||||
<slot name="menu" />
|
<slot name="menu" :entity="entity" />
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-menu>
|
</q-menu>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="body q-py-sm">
|
||||||
<div v-if="$props.data" class="body q-py-sm">
|
|
||||||
<slot name="before" />
|
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item-label header class="ellipsis text-h5" :lines="1">
|
<q-item-label header class="ellipsis text-h5" :lines="1">
|
||||||
{{ $props.description }}
|
<slot name="description" :entity="entity">
|
||||||
<q-tooltip>{{ $props.description }}</q-tooltip>
|
<span>
|
||||||
|
{{ entity.name }}
|
||||||
|
<q-tooltip>{{ entity.name }}</q-tooltip>
|
||||||
|
</span>
|
||||||
|
</slot>
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-item-label class="text-subtitle2" caption>#{{ data.id }}</q-item-label>
|
<q-item-label class="text-subtitle2" caption>
|
||||||
|
#{{ entity.id }}
|
||||||
|
</q-item-label>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
<slot name="body" />
|
<slot name="body" :entity="entity" />
|
||||||
<slot name="after" />
|
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
<!-- Skeleton -->
|
<!-- Skeleton -->
|
||||||
<div id="descriptor-skeleton" v-if="!$props.data">
|
<skeleton-descriptor v-if="!entity" />
|
||||||
<div class="col q-pl-sm q-pa-sm">
|
|
||||||
<q-skeleton type="text" square height="45px" />
|
|
||||||
<q-skeleton type="text" square height="18px" />
|
|
||||||
<q-skeleton type="text" square height="18px" />
|
|
||||||
<q-skeleton type="text" square height="18px" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<q-card-actions>
|
|
||||||
<q-skeleton size="40px" />
|
|
||||||
<q-skeleton size="40px" />
|
|
||||||
<q-skeleton size="40px" />
|
|
||||||
<q-skeleton size="40px" />
|
|
||||||
<q-skeleton size="40px" />
|
|
||||||
</q-card-actions>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref, watch } from 'vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
||||||
|
onMounted(() => fetch());
|
||||||
|
|
||||||
|
const entity = ref();
|
||||||
|
const props = defineProps({
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['onFetch']);
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
entity,
|
||||||
|
fetch,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetch() {
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
if (props.filter) params.filter = props.filter;
|
||||||
|
|
||||||
|
const { data } = await axios.get(props.url, { params });
|
||||||
|
entity.value = data;
|
||||||
|
|
||||||
|
emit('onFetch', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(props, async () => {
|
||||||
|
entity.value = null;
|
||||||
|
fetch();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="summary container">
|
||||||
|
<q-card>
|
||||||
|
<skeleton-summary v-if="!entity" />
|
||||||
|
<template v-if="entity">
|
||||||
|
<div class="header bg-primary q-pa-sm q-mb-md">
|
||||||
|
<slot name="header" :entity="entity">
|
||||||
|
{{ entity.id }} - {{ entity.name }}
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<div class="body q-pa-md q-mb-md">
|
||||||
|
<slot name="body" :entity="entity" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.summary.container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
.q-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.negative {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.q-list {
|
||||||
|
.q-item__label--header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.body > .q-card__section.row {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
& > .col {
|
||||||
|
min-width: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#slider-container {
|
||||||
|
max-width: 80%;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
.q-slider {
|
||||||
|
.q-slider__marker-labels:nth-child(1) {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
.q-slider__marker-labels:nth-child(2) {
|
||||||
|
transform: none;
|
||||||
|
left: auto !important;
|
||||||
|
right: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-dialog .summary {
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -3,30 +3,42 @@ import { ref } from 'vue';
|
||||||
import { useDialogPluginComponent } from 'quasar';
|
import { useDialogPluginComponent } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
const $props = defineProps({
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
question: {
|
question: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: null,
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
||||||
|
|
||||||
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const question = ref($props.question);
|
const question = props.question || t('question');
|
||||||
const message = ref($props.question);
|
const message = props.message || t('message');
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<q-dialog ref="dialogRef" persistent>
|
<q-dialog ref="dialogRef" persistent>
|
||||||
<q-card class="q-pa-sm">
|
<q-card class="q-pa-sm">
|
||||||
<q-card-section class="row items-center q-pb-none">
|
<q-card-section class="row items-center q-pb-none q-gutter-md">
|
||||||
|
<q-avatar
|
||||||
|
:icon="icon"
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
size="xl"
|
||||||
|
v-if="icon"
|
||||||
|
/>
|
||||||
<span class="text-h6 text-grey">{{ message }}</span>
|
<span class="text-h6 text-grey">{{ message }}</span>
|
||||||
<q-space />
|
<q-space />
|
||||||
<q-btn icon="close" flat round dense v-close-popup />
|
<q-btn icon="close" flat round dense v-close-popup />
|
||||||
|
@ -36,7 +48,12 @@ const isLoading = ref(false);
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-actions align="right">
|
<q-card-actions align="right">
|
||||||
<q-btn :label="t('globals.cancel')" color="primary" flat v-close-popup />
|
<q-btn :label="t('globals.cancel')" color="primary" flat v-close-popup />
|
||||||
<q-btn :label="t('globals.confirm')" color="primary" :loading="isLoading" @click="onDialogOK" />
|
<q-btn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
:loading="isLoading"
|
||||||
|
@click="onDialogOK()"
|
||||||
|
/>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
@ -47,3 +64,15 @@ const isLoading = ref(false);
|
||||||
min-width: 350px;
|
min-width: 350px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
"en": {
|
||||||
|
"question": "Are you sure you want to continue?",
|
||||||
|
"message": "Confirm"
|
||||||
|
}
|
||||||
|
|
||||||
|
"es": {
|
||||||
|
"question": "¿Seguro que quieres continuar?",
|
||||||
|
"message": "Confirmar"
|
||||||
|
}
|
||||||
|
</i18n>
|
|
@ -0,0 +1,198 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
import toDate from 'filters/toDate';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
searchButton: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh', 'clear']);
|
||||||
|
|
||||||
|
const arrayData = useArrayData(props.dataKey);
|
||||||
|
const store = arrayData.store;
|
||||||
|
const userParams = ref({});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const params = store.userParams;
|
||||||
|
if (params) {
|
||||||
|
userParams.value = Object.assign({}, params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const isLoading = ref(false);
|
||||||
|
async function search() {
|
||||||
|
const params = userParams.value;
|
||||||
|
for (const param in params) {
|
||||||
|
if (params[param] === '' || params[param] === null) {
|
||||||
|
delete userParams.value[param];
|
||||||
|
delete store.userParams[param];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = true;
|
||||||
|
await arrayData.addFilter({ params });
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reload() {
|
||||||
|
isLoading.value = true;
|
||||||
|
await arrayData.fetch({ append: false });
|
||||||
|
isLoading.value = false;
|
||||||
|
emit('refresh');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clearFilters() {
|
||||||
|
userParams.value = {};
|
||||||
|
isLoading.value = true;
|
||||||
|
await arrayData.applyFilter({ params: {} });
|
||||||
|
isLoading.value = false;
|
||||||
|
|
||||||
|
emit('clear');
|
||||||
|
}
|
||||||
|
|
||||||
|
const tags = computed(() => {
|
||||||
|
const params = [];
|
||||||
|
|
||||||
|
for (const param in store.userParams) {
|
||||||
|
params.push({
|
||||||
|
label: param,
|
||||||
|
value: store.userParams[param],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
});
|
||||||
|
|
||||||
|
async function remove(key) {
|
||||||
|
delete userParams.value[key];
|
||||||
|
delete store.userParams[key];
|
||||||
|
await search();
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatValue(value) {
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
return value ? t('Yes') : t('No');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(value) && !isNaN(Date.parse(value))) {
|
||||||
|
return toDate(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `"${value}"`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<q-form @submit="search">
|
||||||
|
<q-list dense>
|
||||||
|
<q-item class="q-mt-xs">
|
||||||
|
<q-item-section top>
|
||||||
|
<q-item-label
|
||||||
|
header
|
||||||
|
lines="1"
|
||||||
|
class="text-uppercase q-py-xs q-px-none"
|
||||||
|
>
|
||||||
|
{{ t('Applied filters') }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section top side>
|
||||||
|
<div class="q-gutter-xs">
|
||||||
|
<q-btn
|
||||||
|
@click="clearFilters"
|
||||||
|
icon="filter_list_off"
|
||||||
|
color="primary"
|
||||||
|
size="sm"
|
||||||
|
padding="none"
|
||||||
|
round
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
<q-tooltip>{{ t('Remove filters') }}</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
<q-btn
|
||||||
|
@click="reload"
|
||||||
|
icon="refresh"
|
||||||
|
color="primary"
|
||||||
|
size="sm"
|
||||||
|
padding="none"
|
||||||
|
round
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
<q-tooltip>{{ t('Refresh') }}</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<div v-if="tags.length === 0" class="text-grey centered font-xs">
|
||||||
|
{{ t(`You didn't enter any filter`) }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<q-chip
|
||||||
|
v-for="chip of tags"
|
||||||
|
:key="chip.label"
|
||||||
|
@remove="remove(chip.label)"
|
||||||
|
icon="label"
|
||||||
|
color="primary"
|
||||||
|
class="text-dark"
|
||||||
|
size="sm"
|
||||||
|
removable
|
||||||
|
>
|
||||||
|
<slot name="tags" :tag="chip" :format-fn="formatValue">
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ chip.label }}:</strong>
|
||||||
|
<span>"{{ chip.value }}"</span>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</q-chip>
|
||||||
|
</div>
|
||||||
|
</q-item>
|
||||||
|
<q-separator />
|
||||||
|
<template v-if="props.searchButton">
|
||||||
|
<q-item>
|
||||||
|
<q-item-section class="q-py-sm">
|
||||||
|
<q-btn
|
||||||
|
:label="t('Search')"
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
class="full-width"
|
||||||
|
icon="search"
|
||||||
|
unelevated
|
||||||
|
rounded
|
||||||
|
dense
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator />
|
||||||
|
</template>
|
||||||
|
</q-list>
|
||||||
|
<slot name="body" :params="userParams" :search-fn="search"></slot>
|
||||||
|
</q-form>
|
||||||
|
<q-inner-loading
|
||||||
|
:showing="isLoading"
|
||||||
|
:label="t('globals.pleaseWait')"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
You didn't enter any filter: No has introducido ningún filtro
|
||||||
|
Applied filters: Filtros aplicados
|
||||||
|
Remove filters: Eliminar filtros
|
||||||
|
Refresh: Refrescar
|
||||||
|
Search: Buscar
|
||||||
|
Yes: Si
|
||||||
|
No: No
|
||||||
|
</i18n>
|
|
@ -0,0 +1,105 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'Search',
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
redirect: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
const arrayData = useArrayData(props.dataKey);
|
||||||
|
const store = arrayData.store;
|
||||||
|
const searchText = ref('');
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const params = store.userParams;
|
||||||
|
if (params && params.search) {
|
||||||
|
searchText.value = params.search;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function search() {
|
||||||
|
await arrayData.applyFilter({
|
||||||
|
params: {
|
||||||
|
search: searchText.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!props.redirect) return;
|
||||||
|
|
||||||
|
const rows = store.data;
|
||||||
|
const module = route.matched[1];
|
||||||
|
if (rows.length === 1) {
|
||||||
|
const [firstRow] = rows;
|
||||||
|
await router.push({ path: `/${module.name}/${firstRow.id}` });
|
||||||
|
} else if (route.matched.length > 3) {
|
||||||
|
await router.push({ path: `/${module.name}` });
|
||||||
|
arrayData.updateStateParams();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-form @submit="search">
|
||||||
|
<q-input
|
||||||
|
id="searchbar"
|
||||||
|
v-model="searchText"
|
||||||
|
:placeholder="props.label"
|
||||||
|
dense
|
||||||
|
standout
|
||||||
|
autofocus
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="search" />
|
||||||
|
</template>
|
||||||
|
<template #append>
|
||||||
|
<q-icon
|
||||||
|
v-if="searchText !== ''"
|
||||||
|
name="close"
|
||||||
|
@click="searchText = ''"
|
||||||
|
class="cursor-pointer"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-icon v-if="props.info" name="info" class="cursor-info">
|
||||||
|
<q-tooltip>{{ props.info }}</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.cursor-info {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchbar .q-field {
|
||||||
|
min-width: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--light #searchbar {
|
||||||
|
.q-field--standout.q-field--highlighted .q-field__control {
|
||||||
|
background-color: $grey-7;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,149 @@
|
||||||
|
import { onMounted, ref, computed } from 'vue';
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useArrayDataStore } from 'stores/useArrayDataStore';
|
||||||
|
|
||||||
|
const arrayDataStore = useArrayDataStore();
|
||||||
|
|
||||||
|
export function useArrayData(key, userOptions) {
|
||||||
|
if (!key) throw new Error('ArrayData: A key is required to use this composable');
|
||||||
|
|
||||||
|
if (!arrayDataStore.get(key)) {
|
||||||
|
arrayDataStore.set(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = arrayDataStore.get(key);
|
||||||
|
const hasMoreData = ref(false);
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
let canceller = null;
|
||||||
|
|
||||||
|
const page = ref(1);
|
||||||
|
|
||||||
|
if (typeof userOptions === 'object') {
|
||||||
|
if (userOptions.filter) store.filter = userOptions.filter;
|
||||||
|
if (userOptions.url) store.url = userOptions.url;
|
||||||
|
if (userOptions.limit) store.limit = userOptions.limit;
|
||||||
|
if (userOptions.order) store.order = userOptions.order;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const query = route.query;
|
||||||
|
if (query.params) {
|
||||||
|
store.userParams = JSON.parse(query.params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetch({ append = false }) {
|
||||||
|
if (!store.url) return;
|
||||||
|
|
||||||
|
cancelRequest();
|
||||||
|
canceller = new AbortController();
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
order: store.order,
|
||||||
|
limit: store.limit,
|
||||||
|
skip: store.skip,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(filter, store.userFilter);
|
||||||
|
Object.assign(store.filter, filter);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
filter: JSON.stringify(filter),
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(params, store.userParams);
|
||||||
|
|
||||||
|
const response = await axios.get(store.url, {
|
||||||
|
signal: canceller.signal,
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { limit } = filter;
|
||||||
|
|
||||||
|
hasMoreData.value = response.data.length === limit;
|
||||||
|
|
||||||
|
if (append === true) {
|
||||||
|
if (!store.data) store.data = [];
|
||||||
|
for (const row of response.data) store.data.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (append === false) {
|
||||||
|
store.data = response.data;
|
||||||
|
|
||||||
|
updateStateParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
canceller = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroy() {
|
||||||
|
if (arrayDataStore.get(key)) {
|
||||||
|
arrayDataStore.clear(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelRequest() {
|
||||||
|
if (canceller) {
|
||||||
|
canceller.abort();
|
||||||
|
canceller = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyFilter({ filter, params }) {
|
||||||
|
if (filter) store.userFilter = filter;
|
||||||
|
if (params) store.userParams = Object.assign({}, params);
|
||||||
|
|
||||||
|
await fetch({ append: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addFilter({ filter, params }) {
|
||||||
|
if (filter) store.userFilter = Object.assign(store.userFilter, filter);
|
||||||
|
if (params) store.userParams = Object.assign(store.userParams, params);
|
||||||
|
|
||||||
|
await fetch({ append: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadMore() {
|
||||||
|
if (!hasMoreData.value) return;
|
||||||
|
|
||||||
|
store.skip = store.limit * page.value;
|
||||||
|
page.value += 1;
|
||||||
|
|
||||||
|
await fetch({ append: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refresh() {
|
||||||
|
await fetch({ append: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStateParams() {
|
||||||
|
const query = {};
|
||||||
|
if (store.order) query.order = store.order;
|
||||||
|
if (store.limit) query.limit = store.limit;
|
||||||
|
if (store.skip) query.skip = store.skip;
|
||||||
|
if (store.userParams && Object.keys(store.userParams).length !== 0)
|
||||||
|
query.params = JSON.stringify(store.userParams);
|
||||||
|
|
||||||
|
router.replace({
|
||||||
|
path: route.path,
|
||||||
|
query: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalRows = computed(() => store.data && store.data.length | 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
fetch,
|
||||||
|
applyFilter,
|
||||||
|
addFilter,
|
||||||
|
refresh,
|
||||||
|
destroy,
|
||||||
|
loadMore,
|
||||||
|
store,
|
||||||
|
hasMoreData,
|
||||||
|
totalRows,
|
||||||
|
updateStateParams,
|
||||||
|
};
|
||||||
|
}
|
|
@ -13,3 +13,20 @@ a {
|
||||||
.link:hover {
|
.link:hover {
|
||||||
color: $orange-4;
|
color: $orange-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Removes chrome autofill background
|
||||||
|
input:-webkit-autofill,
|
||||||
|
select:-webkit-autofill {
|
||||||
|
color: $input-text-color !important;
|
||||||
|
font-family: $typography-font-family;
|
||||||
|
-webkit-text-fill-color: $input-text-color !important;
|
||||||
|
-webkit-background-clip: text !important;
|
||||||
|
background-clip: text !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.body--light {
|
||||||
|
.q-header .q-toolbar {
|
||||||
|
background-color: white;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,8 +16,6 @@ $primary: #ff9800;
|
||||||
$secondary: #26a69a;
|
$secondary: #26a69a;
|
||||||
$accent: #9c27b0;
|
$accent: #9c27b0;
|
||||||
|
|
||||||
$dark: #1d1d1d;
|
|
||||||
|
|
||||||
$positive: #21ba45;
|
$positive: #21ba45;
|
||||||
$negative: #c10015;
|
$negative: #c10015;
|
||||||
$info: #31ccec;
|
$info: #31ccec;
|
||||||
|
@ -28,6 +26,6 @@ $color-spacer: rgba(255, 255, 255, 0.3);
|
||||||
$border-thin-light: 1px solid $color-spacer-light;
|
$border-thin-light: 1px solid $color-spacer-light;
|
||||||
|
|
||||||
$dark-shadow-color: #000;
|
$dark-shadow-color: #000;
|
||||||
|
$dark: #292929;
|
||||||
$layout-shadow-dark: 0 0 10px 2px rgba(0, 0, 0, 0.2), 0 0px 10px rgba(0, 0, 0, 0.24);
|
$layout-shadow-dark: 0 0 10px 2px rgba(0, 0, 0, 0.2), 0 0px 10px rgba(0, 0, 0, 0.24);
|
||||||
|
|
||||||
$spacing-md: 16px;
|
$spacing-md: 16px;
|
||||||
|
|
|
@ -3,10 +3,14 @@ import { useI18n } from 'vue-i18n';
|
||||||
export default function (value, options = {}) {
|
export default function (value, options = {}) {
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
|
|
||||||
if (!options.dateStyle) options.dateStyle = 'short';
|
if (!options.dateStyle && !options.timeStyle) {
|
||||||
|
options.day = '2-digit';
|
||||||
|
options.month = '2-digit';
|
||||||
|
options.year = 'numeric';
|
||||||
|
}
|
||||||
|
|
||||||
const { locale } = useI18n();
|
const { locale } = useI18n();
|
||||||
const date = new Date(value);
|
const date = new Date(value);
|
||||||
|
|
||||||
return new Intl.DateTimeFormat(locale.value, options).format(date)
|
return new Intl.DateTimeFormat(locale.value, options).format(date);
|
||||||
}
|
}
|
|
@ -241,6 +241,7 @@ export default {
|
||||||
summary: 'Summary',
|
summary: 'Summary',
|
||||||
basicData: 'Basic Data',
|
basicData: 'Basic Data',
|
||||||
rma: 'RMA',
|
rma: 'RMA',
|
||||||
|
photos: 'Photos',
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
customer: 'Customer',
|
customer: 'Customer',
|
||||||
|
@ -294,6 +295,11 @@ export default {
|
||||||
picked: 'Picked',
|
picked: 'Picked',
|
||||||
returnOfMaterial: 'Return of material authorization (RMA)',
|
returnOfMaterial: 'Return of material authorization (RMA)',
|
||||||
},
|
},
|
||||||
|
photo: {
|
||||||
|
fileDescription: 'Claim id {claimId} from client {clientName} id {clientId}',
|
||||||
|
noData: 'There are no images/videos, click here or drag and drop the file',
|
||||||
|
dragDrop: 'Drag and drop it here',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
invoiceOut: {
|
invoiceOut: {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
|
|
|
@ -13,7 +13,7 @@ export default {
|
||||||
darkMode: 'Modo oscuro',
|
darkMode: 'Modo oscuro',
|
||||||
logOut: 'Cerrar sesión',
|
logOut: 'Cerrar sesión',
|
||||||
dataSaved: 'Datos guardados',
|
dataSaved: 'Datos guardados',
|
||||||
dataDeleted: 'Data deleted',
|
dataDeleted: 'Datos eliminados',
|
||||||
add: 'Añadir',
|
add: 'Añadir',
|
||||||
create: 'Crear',
|
create: 'Crear',
|
||||||
save: 'Guardar',
|
save: 'Guardar',
|
||||||
|
@ -240,6 +240,7 @@ export default {
|
||||||
summary: 'Resumen',
|
summary: 'Resumen',
|
||||||
basicData: 'Datos básicos',
|
basicData: 'Datos básicos',
|
||||||
rma: 'RMA',
|
rma: 'RMA',
|
||||||
|
photos: 'Fotos',
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
customer: 'Cliente',
|
customer: 'Cliente',
|
||||||
|
@ -293,6 +294,12 @@ export default {
|
||||||
picked: 'Recogida',
|
picked: 'Recogida',
|
||||||
returnOfMaterial: 'Autorización de retorno de materiales (RMA)',
|
returnOfMaterial: 'Autorización de retorno de materiales (RMA)',
|
||||||
},
|
},
|
||||||
|
photo: {
|
||||||
|
fileDescription:
|
||||||
|
'Reclamacion ID {claimId} del cliente {clientName} id {clientId}',
|
||||||
|
noData: 'No hay imágenes/videos, haz click aquí o arrastra y suelta el archivo',
|
||||||
|
dragDrop: 'Arrástralo y sueltalo aquí',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
invoiceOut: {
|
invoiceOut: {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
|
|
|
@ -2,13 +2,26 @@
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import Navbar from 'src/components/NavBar.vue';
|
import Navbar from 'src/components/NavBar.vue';
|
||||||
|
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
const stateStore = useStateStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-layout view="hHh LpR fFf">
|
<q-layout view="hHh LpR fFf">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
|
<q-drawer
|
||||||
|
v-model="stateStore.rightDrawer"
|
||||||
|
side="right"
|
||||||
|
:width="256"
|
||||||
|
:persistent="false"
|
||||||
|
>
|
||||||
|
<q-scroll-area class="fit text-grey-8">
|
||||||
|
<div id="rightPanel"></div>
|
||||||
|
</q-scroll-area>
|
||||||
|
</q-drawer>
|
||||||
<q-footer v-if="quasar.platform.is.mobile"></q-footer>
|
<q-footer v-if="quasar.platform.is.mobile"></q-footer>
|
||||||
</q-layout>
|
</q-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,12 +1,23 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useState } from 'composables/useState';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import ClaimDescriptor from './ClaimDescriptor.vue';
|
import ClaimDescriptor from './ClaimDescriptor.vue';
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||||
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
|
|
||||||
const state = useState();
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<q-drawer v-model="state.drawer.value" show-if-above :width="256" :breakpoint="500">
|
<teleport-slot to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="ClaimList"
|
||||||
|
:label="t('Search claim')"
|
||||||
|
:info="t('You can search by claim id or customer name')"
|
||||||
|
/>
|
||||||
|
</teleport-slot>
|
||||||
|
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
<q-scroll-area class="fit">
|
<q-scroll-area class="fit">
|
||||||
<claim-descriptor />
|
<claim-descriptor />
|
||||||
<q-separator />
|
<q-separator />
|
||||||
|
@ -20,31 +31,8 @@ const state = useState();
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<i18n>
|
||||||
.q-scrollarea__content {
|
es:
|
||||||
max-width: 100%;
|
Search claim: Buscar reclamación
|
||||||
}
|
You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente
|
||||||
</style>
|
</i18n>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.descriptor {
|
|
||||||
max-width: 256px;
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
margin: 0 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.q-card__actions {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#descriptor-skeleton .q-card__actions {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, computed, ref } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { toDate } from 'src/filters';
|
import { toDate } from 'src/filters';
|
||||||
import axios from 'axios';
|
|
||||||
import TicketDescriptorPopover from 'pages/Ticket/Card/TicketDescriptorPopover.vue';
|
import TicketDescriptorPopover from 'pages/Ticket/Card/TicketDescriptorPopover.vue';
|
||||||
import ClaimDescriptorMenu from 'pages/Claim/Card/ClaimDescriptorMenu.vue';
|
import ClaimDescriptorMenu from 'pages/Claim/Card/ClaimDescriptorMenu.vue';
|
||||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
@ -17,10 +16,6 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await fetch();
|
|
||||||
});
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@ -28,8 +23,6 @@ const entityId = computed(() => {
|
||||||
return $props.id || route.params.id;
|
return $props.id || route.params.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
const claim = ref();
|
|
||||||
async function fetch() {
|
|
||||||
const filter = {
|
const filter = {
|
||||||
include: [
|
include: [
|
||||||
{ relation: 'client' },
|
{ relation: 'client' },
|
||||||
|
@ -45,11 +38,6 @@ async function fetch() {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const options = { params: { filter } };
|
|
||||||
const { data } = await axios.get(`Claims/${entityId.value}`, options);
|
|
||||||
|
|
||||||
if (data) claim.value = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function stateColor(code) {
|
function stateColor(code) {
|
||||||
if (code === 'pending') return 'green';
|
if (code === 'pending') return 'green';
|
||||||
|
@ -59,40 +47,56 @@ function stateColor(code) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<skeleton-descriptor v-if="!claim" />
|
<card-descriptor
|
||||||
<card-descriptor v-if="claim" module="Claim" :data="claim" :description="claim.client.name">
|
ref="descriptor"
|
||||||
<template #menu>
|
:url="`Claims/${entityId}`"
|
||||||
<claim-descriptor-menu v-if="claim" :claim="claim" />
|
:filter="filter"
|
||||||
|
module="Claim"
|
||||||
|
>
|
||||||
|
<template #menu="{ entity }">
|
||||||
|
<claim-descriptor-menu :claim="entity" />
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #description="{ entity }">
|
||||||
|
<span>
|
||||||
|
{{ entity.client.name }}
|
||||||
|
<q-tooltip>{{ entity.client.name }}</q-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #body="{ entity }">
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.card.created') }}</q-item-label>
|
<q-item-label caption>{{ t('claim.card.created') }}</q-item-label>
|
||||||
<q-item-label>{{ toDate(claim.created) }}</q-item-label>
|
<q-item-label>{{ toDate(entity.created) }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section v-if="entity.claimState">
|
||||||
<q-item-label caption>{{ t('claim.card.state') }}</q-item-label>
|
<q-item-label caption>{{ t('claim.card.state') }}</q-item-label>
|
||||||
<q-item-label>
|
<q-item-label>
|
||||||
<q-chip :color="stateColor(claim.claimState.code)" dense>
|
<q-chip :color="stateColor(entity.claimState.code)" dense>
|
||||||
{{ claim.claimState.description }}
|
{{ entity.claimState.description }}
|
||||||
</q-chip>
|
</q-chip>
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.card.ticketId') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label class="link">
|
{{ t('claim.card.ticketId') }}
|
||||||
{{ claim.ticketFk }}
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
<span class="link">
|
||||||
|
{{ entity.ticketFk }}
|
||||||
<q-popup-proxy>
|
<q-popup-proxy>
|
||||||
<ticket-descriptor-popover :id="claim.ticketFk" />
|
<ticket-descriptor-popover :id="entity.ticketFk" />
|
||||||
</q-popup-proxy>
|
</q-popup-proxy>
|
||||||
|
</span>
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section v-if="entity.worker">
|
||||||
<q-item-label caption>{{ t('claim.card.assignedTo') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ claim.worker.user.name }}</q-item-label>
|
{{ t('claim.card.assignedTo') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ entity.worker.user.name }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
@ -102,7 +106,7 @@ function stateColor(code) {
|
||||||
size="md"
|
size="md"
|
||||||
icon="vn:client"
|
icon="vn:client"
|
||||||
color="primary"
|
color="primary"
|
||||||
:to="{ name: 'CustomerCard', params: { id: claim.clientFk } }"
|
:to="{ name: 'CustomerCard', params: { id: entity.clientFk } }"
|
||||||
>
|
>
|
||||||
<q-tooltip>{{ t('claim.card.customerSummary') }}</q-tooltip>
|
<q-tooltip>{{ t('claim.card.customerSummary') }}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
@ -110,7 +114,7 @@ function stateColor(code) {
|
||||||
size="md"
|
size="md"
|
||||||
icon="vn:ticket"
|
icon="vn:ticket"
|
||||||
color="primary"
|
color="primary"
|
||||||
:to="{ name: 'TicketCard', params: { id: claim.ticketFk } }"
|
:to="{ name: 'TicketCard', params: { id: entity.ticketFk } }"
|
||||||
>
|
>
|
||||||
<q-tooltip>{{ t('claim.card.claimedTicket') }}</q-tooltip>
|
<q-tooltip>{{ t('claim.card.claimedTicket') }}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
|
|
@ -0,0 +1,376 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||||
|
import TeleportSlot from 'src/components/ui/TeleportSlot.vue';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const session = useSession();
|
||||||
|
const token = session.getToken();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const claimId = computed(() => router.currentRoute.value.params.id);
|
||||||
|
|
||||||
|
const claimDms = ref([
|
||||||
|
{
|
||||||
|
dmsFk: 1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const client = ref({});
|
||||||
|
|
||||||
|
const inputFile = ref();
|
||||||
|
const files = ref({});
|
||||||
|
|
||||||
|
const claimDmsRef = ref();
|
||||||
|
const dmsType = ref({});
|
||||||
|
const config = ref({});
|
||||||
|
const dragFile = ref(false);
|
||||||
|
const dragFileTimeout = ref();
|
||||||
|
const claimDmsFilter = ref({
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'claimDms',
|
||||||
|
scope: {
|
||||||
|
include: {
|
||||||
|
relation: 'dms',
|
||||||
|
scope: {
|
||||||
|
fields: ['contentType'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
where: { id: claimId.value },
|
||||||
|
});
|
||||||
|
|
||||||
|
const multimediaDialog = ref();
|
||||||
|
const multimediaSlide = ref();
|
||||||
|
|
||||||
|
function openDialog(dmsId) {
|
||||||
|
multimediaSlide.value = dmsId;
|
||||||
|
multimediaDialog.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewDeleteDms(dmsId) {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
message: t('This file will be deleted'),
|
||||||
|
icon: 'delete',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(() => deleteDms(dmsId));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteDms(index) {
|
||||||
|
const dmsId = claimDms.value[index].dmsFk;
|
||||||
|
await axios.post(`ClaimDms/${dmsId}/removeFile`);
|
||||||
|
|
||||||
|
claimDms.value.splice(index, 1);
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataDeleted'),
|
||||||
|
type: 'positive',
|
||||||
|
icon: 'delete',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setClaimDms(data) {
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
claimDms.value = data.claimDms.map((media) => {
|
||||||
|
media.isVideo = media.dms.contentType == 'video/mp4';
|
||||||
|
media.url = `/api/Claims/${media.dmsFk}/downloadFile?access_token=${token}`;
|
||||||
|
return media;
|
||||||
|
});
|
||||||
|
client.value = data.client;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function create() {
|
||||||
|
const formData = new FormData();
|
||||||
|
const inputFiles = files.value;
|
||||||
|
for (let i = 0; i < inputFiles.length; i++)
|
||||||
|
formData.append(inputFiles[i].name, inputFiles[i]);
|
||||||
|
|
||||||
|
const query = `claims/${claimId.value}/uploadFile`;
|
||||||
|
|
||||||
|
const dms = {
|
||||||
|
hasFile: false,
|
||||||
|
hasFileAttached: false,
|
||||||
|
reference: claimId.value,
|
||||||
|
warehouseId: config.value.warehouseFk,
|
||||||
|
companyId: config.value.companyFk,
|
||||||
|
dmsTypeId: dmsType.value.id,
|
||||||
|
description: t('claim.photo.fileDescription', {
|
||||||
|
claimId: claimId.value,
|
||||||
|
clientName: client.value.name,
|
||||||
|
clientId: client.value.id,
|
||||||
|
}).toUpperCase(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await axios.post(query, formData, {
|
||||||
|
params: dms,
|
||||||
|
});
|
||||||
|
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataSaved'),
|
||||||
|
type: 'positive',
|
||||||
|
icon: 'check',
|
||||||
|
});
|
||||||
|
|
||||||
|
claimDmsRef.value.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDrop($data) {
|
||||||
|
dragFile.value = false;
|
||||||
|
files.value = $data.dataTransfer.files;
|
||||||
|
create();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDrag() {
|
||||||
|
clearTimeout(dragFileTimeout.value);
|
||||||
|
dragFileTimeout.value = setTimeout(() => (dragFile.value = false), 500);
|
||||||
|
dragFile.value = true;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<fetch-data
|
||||||
|
url="Claims"
|
||||||
|
:filter="claimDmsFilter"
|
||||||
|
@on-fetch="([data]) => setClaimDms(data)"
|
||||||
|
limit="20"
|
||||||
|
auto-load
|
||||||
|
ref="claimDmsRef"
|
||||||
|
/>
|
||||||
|
<fetch-data
|
||||||
|
url="DmsTypes/findOne"
|
||||||
|
:filter="{ where: { code: 'claim' } }"
|
||||||
|
@on-fetch="(data) => (dmsType = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<fetch-data
|
||||||
|
url="UserConfigs/getUserConfig"
|
||||||
|
@on-fetch="(data) => (config = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
:class="['container', { dragFile }]"
|
||||||
|
@drop.prevent="onDrop"
|
||||||
|
@dragenter.prevent
|
||||||
|
@dragover.prevent="onDrag"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-center items-center text-grey q-mt-md column"
|
||||||
|
v-if="dragFile"
|
||||||
|
>
|
||||||
|
<q-icon size="xl" name="file_download" />
|
||||||
|
<h5>
|
||||||
|
{{ t('claim.photo.dragDrop') }}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-center text-grey q-mt-md cursor-pointer"
|
||||||
|
v-if="!claimDms?.length && !dragFile"
|
||||||
|
@click="inputFile.nativeEl.click()"
|
||||||
|
>
|
||||||
|
<q-icon size="xl" name="image"></q-icon>
|
||||||
|
<q-icon size="xl" name="movie"></q-icon>
|
||||||
|
<h5>
|
||||||
|
{{ t('claim.photo.noData') }}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div class="multimediaParent bg-transparent" v-if="claimDms?.length && !dragFile">
|
||||||
|
<div
|
||||||
|
v-for="(media, index) of claimDms"
|
||||||
|
:key="index"
|
||||||
|
class="relative-position"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
icon="delete"
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
size="md"
|
||||||
|
class="all-pointer-events absolute delete-button zindex"
|
||||||
|
@click.stop="viewDeleteDms(index)"
|
||||||
|
round
|
||||||
|
/>
|
||||||
|
<q-icon
|
||||||
|
name="play_circle"
|
||||||
|
color="primary"
|
||||||
|
size="xl"
|
||||||
|
class="absolute-center zindex"
|
||||||
|
v-if="media.isVideo"
|
||||||
|
@click.stop="openDialog(media.dmsFk)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Video</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
<q-card class="multimedia relative-position">
|
||||||
|
<q-img
|
||||||
|
:src="media.url"
|
||||||
|
class="rounded-borders cursor-pointer fit"
|
||||||
|
@click="openDialog(media.dmsFk)"
|
||||||
|
v-if="!media.isVideo"
|
||||||
|
>
|
||||||
|
</q-img>
|
||||||
|
<video
|
||||||
|
:src="media.url"
|
||||||
|
class="rounded-borders cursor-pointer fit"
|
||||||
|
muted="muted"
|
||||||
|
v-if="media.isVideo"
|
||||||
|
@click="openDialog(media.dmsFk)"
|
||||||
|
/>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<teleport-slot v-if="!quasar.platform.is.mobile" to="#header-actions">
|
||||||
|
<div class="row q-gutter-x-sm">
|
||||||
|
<label for="fileInput">
|
||||||
|
<q-btn
|
||||||
|
@click="inputFile.nativeEl.click()"
|
||||||
|
icon="add"
|
||||||
|
color="primary"
|
||||||
|
dense
|
||||||
|
rounded
|
||||||
|
>
|
||||||
|
<q-input
|
||||||
|
ref="inputFile"
|
||||||
|
type="file"
|
||||||
|
style="display: none"
|
||||||
|
multiple
|
||||||
|
v-model="files"
|
||||||
|
@update:model-value="create()"
|
||||||
|
/>
|
||||||
|
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</label>
|
||||||
|
<q-separator vertical />
|
||||||
|
</div>
|
||||||
|
</teleport-slot>
|
||||||
|
|
||||||
|
<teleport-slot to=".q-footer">
|
||||||
|
<q-tabs align="justify" inline-label narrow-indicator>
|
||||||
|
<q-tab
|
||||||
|
@click="inputFile.nativeEl.click()"
|
||||||
|
icon="add_circle"
|
||||||
|
:label="t('globals.add')"
|
||||||
|
>
|
||||||
|
<q-input
|
||||||
|
ref="inputFile"
|
||||||
|
type="file"
|
||||||
|
style="display: none"
|
||||||
|
multiple
|
||||||
|
v-model="files"
|
||||||
|
@update:model-value="create()"
|
||||||
|
/>
|
||||||
|
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip>
|
||||||
|
</q-tab>
|
||||||
|
</q-tabs>
|
||||||
|
</teleport-slot>
|
||||||
|
|
||||||
|
<!-- MULTIMEDIA DIALOG START-->
|
||||||
|
<q-dialog
|
||||||
|
v-model="multimediaDialog"
|
||||||
|
transition-show="slide-up"
|
||||||
|
transition-hide="slide-down"
|
||||||
|
>
|
||||||
|
<q-toolbar class="absolute zindex close-button">
|
||||||
|
<q-space />
|
||||||
|
<q-btn icon="close" color="primary" round dense v-close-popup />
|
||||||
|
</q-toolbar>
|
||||||
|
<q-carousel swipeable animated v-model="multimediaSlide" arrows class="fit">
|
||||||
|
<q-carousel-slide
|
||||||
|
v-for="media of claimDms"
|
||||||
|
:key="media.dmsFk"
|
||||||
|
:name="media.dmsFk"
|
||||||
|
>
|
||||||
|
<q-img
|
||||||
|
:src="media.url"
|
||||||
|
class="fit"
|
||||||
|
fit="scale-down"
|
||||||
|
v-if="!media.isVideo"
|
||||||
|
/>
|
||||||
|
<video class="q-ma-none fit" v-if="media.isVideo" controls muted autoplay>
|
||||||
|
<source :src="media.url" type="video/mp4" />
|
||||||
|
</video>
|
||||||
|
</q-carousel-slide>
|
||||||
|
</q-carousel>
|
||||||
|
</q-dialog>
|
||||||
|
<!-- MULTIMEDIA DIALOG END-->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
min-height: 80vh;
|
||||||
|
min-width: 80%;
|
||||||
|
}
|
||||||
|
.q-dialog__inner--minimized > div {
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
.multimediaParent {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||||
|
|
||||||
|
grid-auto-rows: auto;
|
||||||
|
|
||||||
|
grid-gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multimedia {
|
||||||
|
transition: all 0.5s;
|
||||||
|
opacity: 1;
|
||||||
|
height: 250px;
|
||||||
|
|
||||||
|
.q-img {
|
||||||
|
object-fit: cover;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
video {
|
||||||
|
object-fit: cover;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.multimedia:hover {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-button {
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
top: 1%;
|
||||||
|
right: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zindex {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragFile {
|
||||||
|
border: 2px dashed $color-spacer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
This file will be deleted: Este archivo va a ser borrado
|
||||||
|
</i18n>
|
|
@ -1,25 +1,31 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import axios from 'axios';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import axios from 'axios';
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
import Paginate from 'src/components/PaginateData.vue';
|
import Paginate from 'src/components/PaginateData.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import TeleportSlot from 'components/ui/TeleportSlot';
|
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||||
|
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
import { toDate } from 'src/filters';
|
import { toDate } from 'src/filters';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const arrayData = useArrayData('ClaimRma');
|
||||||
|
|
||||||
const claim = ref([]);
|
const claim = ref();
|
||||||
const fetcher = ref();
|
const claimFilter = {
|
||||||
|
fields: ['rma'],
|
||||||
|
};
|
||||||
|
|
||||||
|
async function onFetch(data) {
|
||||||
|
claim.value = data;
|
||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
include: {
|
|
||||||
relation: 'rmas',
|
|
||||||
scope: {
|
|
||||||
include: {
|
include: {
|
||||||
relation: 'worker',
|
relation: 'worker',
|
||||||
scope: {
|
scope: {
|
||||||
|
@ -29,17 +35,21 @@ const filter = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
order: 'created DESC',
|
order: 'created DESC',
|
||||||
},
|
where: {
|
||||||
|
code: claim.value.rma,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
arrayData.applyFilter({ filter });
|
||||||
|
}
|
||||||
|
|
||||||
async function addRow() {
|
async function addRow() {
|
||||||
const formData = {
|
const formData = {
|
||||||
code: claim.value.rma,
|
code: claim.value.rma,
|
||||||
};
|
};
|
||||||
|
|
||||||
await axios.post(`ClaimRmas`, formData);
|
await axios.post(`ClaimRmas`, formData);
|
||||||
await fetcher.value.fetch();
|
await arrayData.refresh();
|
||||||
|
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
|
@ -48,19 +58,17 @@ async function addRow() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmShown = ref(false);
|
|
||||||
const rmaId = ref(null);
|
|
||||||
function confirmRemove(id) {
|
function confirmRemove(id) {
|
||||||
confirmShown.value = true;
|
quasar
|
||||||
rmaId.value = id;
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
})
|
||||||
|
.onOk(() => remove(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function remove() {
|
async function remove(id) {
|
||||||
const id = rmaId.value;
|
|
||||||
|
|
||||||
await axios.delete(`ClaimRmas/${id}`);
|
await axios.delete(`ClaimRmas/${id}`);
|
||||||
await fetcher.value.fetch();
|
await arrayData.refresh();
|
||||||
confirmShown.value = false;
|
|
||||||
|
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
|
@ -68,67 +76,65 @@ async function remove() {
|
||||||
icon: 'check',
|
icon: 'check',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
|
||||||
rmaId.value = null;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<fetch-data
|
<fetch-data
|
||||||
ref="fetcher"
|
|
||||||
:url="`Claims/${route.params.id}`"
|
:url="`Claims/${route.params.id}`"
|
||||||
:filter="filter"
|
:filter="claimFilter"
|
||||||
@on-fetch="(data) => (claim = data)"
|
@on-fetch="onFetch"
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<paginate :data="claim.rmas">
|
<paginate data-key="ClaimRma" url="ClaimRmas">
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<q-card class="card">
|
<q-card class="card">
|
||||||
<template v-for="row of rows" :key="row.id">
|
<template v-for="(row, index) of rows" :key="row.id">
|
||||||
<q-item class="q-pa-none items-start">
|
<q-item class="q-pa-none items-start">
|
||||||
<q-item-section class="q-pa-md">
|
<q-item-section class="q-pa-md">
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.rma.user') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ row.worker.user.name }}</q-item-label>
|
{{ t('claim.rma.user') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ row.worker.user.name }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.rma.created') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('claim.rma.created') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label>
|
<q-item-label>
|
||||||
{{ toDate(row.created, { timeStyle: 'medium' }) }}
|
{{
|
||||||
|
toDate(row.created, {
|
||||||
|
timeStyle: 'medium',
|
||||||
|
})
|
||||||
|
}}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-card-actions vertical class="justify-between">
|
<q-card-actions vertical class="justify-between">
|
||||||
<q-btn flat round color="orange" icon="vn:bin" @click="confirmRemove(row.id)">
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
color="orange"
|
||||||
|
icon="vn:bin"
|
||||||
|
@click="confirmRemove(row.id)"
|
||||||
|
>
|
||||||
<q-tooltip>{{ t('globals.remove') }}</q-tooltip>
|
<q-tooltip>{{ t('globals.remove') }}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-separator />
|
<q-separator v-if="index !== rows.length - 1" />
|
||||||
</template>
|
</template>
|
||||||
</q-card>
|
</q-card>
|
||||||
</template>
|
</template>
|
||||||
</paginate>
|
</paginate>
|
||||||
|
|
||||||
<q-dialog v-model="confirmShown" persistent @hide="hide">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section class="row items-center">
|
|
||||||
<q-avatar icon="warning" color="primary" text-color="white" />
|
|
||||||
<span class="q-ml-sm">{{ t('globals.confirmRemove') }}</span>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<q-card-actions align="right">
|
|
||||||
<q-btn flat :label="t('globals.no')" color="primary" v-close-popup autofocus />
|
|
||||||
<q-btn flat :label="t('globals.yes')" color="primary" @click="remove()" />
|
|
||||||
</q-card-actions>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
<teleport-slot v-if="!quasar.platform.is.mobile" to="#header-actions">
|
<teleport-slot v-if="!quasar.platform.is.mobile" to="#header-actions">
|
||||||
<div class="row q-gutter-x-sm">
|
<div class="row q-gutter-x-sm">
|
||||||
<q-btn @click="addRow()" icon="add" color="primary" dense rounded>
|
<q-btn @click="addRow()" icon="add" color="primary" dense rounded>
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import axios from 'axios';
|
|
||||||
import { toDate, toCurrency } from 'src/filters';
|
import { toDate, toCurrency } from 'src/filters';
|
||||||
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
|
|
||||||
onMounted(() => fetch());
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -20,16 +17,6 @@ const $props = defineProps({
|
||||||
|
|
||||||
const entityId = computed(() => $props.id || route.params.id);
|
const entityId = computed(() => $props.id || route.params.id);
|
||||||
|
|
||||||
const claim = ref(null);
|
|
||||||
const salesClaimed = ref(null);
|
|
||||||
function fetch() {
|
|
||||||
const id = entityId.value;
|
|
||||||
axios.get(`Claims/${id}/getSummary`).then(({ data }) => {
|
|
||||||
claim.value = data.claim;
|
|
||||||
salesClaimed.value = data.salesClaimed;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const detailsColumns = ref([
|
const detailsColumns = ref([
|
||||||
{
|
{
|
||||||
name: 'item',
|
name: 'item',
|
||||||
|
@ -77,7 +64,8 @@ const detailsColumns = ref([
|
||||||
{
|
{
|
||||||
name: 'total',
|
name: 'total',
|
||||||
label: 'claim.summary.total',
|
label: 'claim.summary.total',
|
||||||
field: ({ sale }) => toCurrency(sale.quantity * sale.price * ((100 - sale.discount) / 100)),
|
field: ({ sale }) =>
|
||||||
|
toCurrency(sale.quantity * sale.price * ((100 - sale.discount) / 100)),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -90,43 +78,68 @@ function stateColor(code) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="summary container">
|
<card-summary ref="summary" :url="`Claims/${entityId}/getSummary`">
|
||||||
<q-card>
|
<template #header="{ entity: { claim } }">
|
||||||
<skeleton-summary v-if="!claim" />
|
{{ claim.id }} - {{ claim.client.name }}
|
||||||
<template v-if="claim">
|
</template>
|
||||||
<div class="header bg-primary q-pa-sm q-mb-md">{{ claim.id }} - {{ claim.client.name }}</div>
|
<template #body="{ entity: { claim, salesClaimed } }">
|
||||||
|
<q-card-section class="row q-pa-none q-col-gutter-md">
|
||||||
|
<div class="col">
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.summary.created') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('claim.summary.created') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label>{{ toDate(claim.created) }}</q-item-label>
|
<q-item-label>{{ toDate(claim.created) }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section v-if="claim.claimState">
|
||||||
<q-item-label caption>{{ t('claim.summary.state') }}</q-item-label>
|
<q-item-label caption>{{
|
||||||
|
t('claim.summary.state')
|
||||||
|
}}</q-item-label>
|
||||||
<q-item-label>
|
<q-item-label>
|
||||||
<q-chip :color="stateColor(claim.claimState.code)" dense>
|
<q-chip
|
||||||
|
:color="stateColor(claim.claimState.code)"
|
||||||
|
dense
|
||||||
|
>
|
||||||
{{ claim.claimState.description }}
|
{{ claim.claimState.description }}
|
||||||
</q-chip>
|
</q-chip>
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section v-if="claim.worker && claim.worker.user">
|
||||||
<q-item-label caption>{{ t('claim.summary.assignedTo') }}</q-item-label>
|
<q-item-label caption>{{
|
||||||
<q-item-label>{{ claim.worker.user.nickname }}</q-item-label>
|
t('claim.summary.assignedTo')
|
||||||
|
}}</q-item-label>
|
||||||
|
<q-item-label>{{
|
||||||
|
claim.worker.user.nickname
|
||||||
|
}}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section
|
||||||
<q-item-label caption>{{ t('claim.summary.attendedBy') }}</q-item-label>
|
v-if="claim.client && claim.client.salesPersonUser"
|
||||||
<q-item-label>{{ claim.client.salesPersonUser.name }}</q-item-label>
|
>
|
||||||
|
<q-item-label caption>{{
|
||||||
|
t('claim.summary.attendedBy')
|
||||||
|
}}</q-item-label>
|
||||||
|
<q-item-label>{{
|
||||||
|
claim.client.salesPersonUser.name
|
||||||
|
}}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
<q-card-section class="q-pa-md">
|
<q-card-section class="q-pa-md">
|
||||||
<h6>{{ t('claim.summary.details') }}</h6>
|
<h6>{{ t('claim.summary.details') }}</h6>
|
||||||
<q-table :columns="detailsColumns" :rows="salesClaimed" flat>
|
<q-table :columns="detailsColumns" :rows="salesClaimed" flat>
|
||||||
<template #header="props">
|
<template #header="props">
|
||||||
<q-tr :props="props">
|
<q-tr :props="props">
|
||||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
<q-th
|
||||||
|
v-for="col in props.cols"
|
||||||
|
:key="col.name"
|
||||||
|
:props="props"
|
||||||
|
>
|
||||||
{{ t(col.label) }}
|
{{ t(col.label) }}
|
||||||
</q-th>
|
</q-th>
|
||||||
</q-tr>
|
</q-tr>
|
||||||
|
@ -155,45 +168,5 @@ function stateColor(code) {
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</template>
|
</template>
|
||||||
</q-card>
|
</card-summary>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.q-card {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 950px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.summary {
|
|
||||||
.header {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#slider-container {
|
|
||||||
max-width: 80%;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.q-slider {
|
|
||||||
.q-slider__marker-labels:nth-child(1) {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
.q-slider__marker-labels:nth-child(2) {
|
|
||||||
transform: none;
|
|
||||||
left: auto !important;
|
|
||||||
right: 0%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.q-dialog .summary {
|
|
||||||
max-width: 1200px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const workers = ref();
|
||||||
|
const states = ref();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<fetch-data url="ClaimStates" @on-fetch="(data) => (states = data)" auto-load />
|
||||||
|
<fetch-data
|
||||||
|
url="Workers/activeWithInheritedRole"
|
||||||
|
:filter="{ where: { role: 'salesPerson' } }"
|
||||||
|
@on-fetch="(data) => (workers = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||||
|
<template #tags="{ tag, formatFn }">
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||||
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body="{ params, searchFn }">
|
||||||
|
<q-list dense>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:label="t('Customer ID')"
|
||||||
|
v-model="params.clientFk"
|
||||||
|
lazy-rules
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="badge" size="sm"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:label="t('Client Name')"
|
||||||
|
v-model="params.clientName"
|
||||||
|
lazy-rules
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section v-if="!workers">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="workers">
|
||||||
|
<q-select
|
||||||
|
:label="t('Salesperson')"
|
||||||
|
v-model="params.salesPersonFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="workers"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section v-if="!workers">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="workers">
|
||||||
|
<q-select
|
||||||
|
:label="t('Attender')"
|
||||||
|
v-model="params.attenderFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="workers"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section v-if="!workers">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="workers">
|
||||||
|
<q-select
|
||||||
|
:label="t('Responsible')"
|
||||||
|
v-model="params.claimResponsibleFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="workers"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item class="q-mb-md">
|
||||||
|
<q-item-section v-if="!states">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="states">
|
||||||
|
<q-select
|
||||||
|
:label="t('State')"
|
||||||
|
v-model="params.claimStateFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="states"
|
||||||
|
option-value="id"
|
||||||
|
option-label="description"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator />
|
||||||
|
<q-expansion-item :label="t('More options')" expand-separator>
|
||||||
|
<!-- <q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-select
|
||||||
|
:label="t('Item')"
|
||||||
|
v-model="params.itemFk"
|
||||||
|
:options="items"
|
||||||
|
:loading="loading"
|
||||||
|
@filter="filterFn"
|
||||||
|
@virtual-scroll="onScroll"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item> -->
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
v-model="params.created"
|
||||||
|
:label="t('Created')"
|
||||||
|
autofocus
|
||||||
|
readonly
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<q-icon name="event" class="cursor-pointer">
|
||||||
|
<q-popup-proxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<q-date v-model="params.created">
|
||||||
|
<div class="row items-center justify-end">
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
label="Close"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-date>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-expansion-item>
|
||||||
|
</q-list>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
params:
|
||||||
|
search: Contains
|
||||||
|
clientFk: Customer
|
||||||
|
clientName: Customer
|
||||||
|
salesPersonFk: Salesperson
|
||||||
|
attenderFk: Attender
|
||||||
|
claimResponsibleFk: Responsible
|
||||||
|
claimStateFk: State
|
||||||
|
created: Created
|
||||||
|
es:
|
||||||
|
params:
|
||||||
|
search: Contiene
|
||||||
|
clientFk: Cliente
|
||||||
|
clientName: Cliente
|
||||||
|
salesPersonFk: Comercial
|
||||||
|
attenderFk: Asistente
|
||||||
|
claimResponsibleFk: Responsable
|
||||||
|
claimStateFk: Estado
|
||||||
|
created: Creada
|
||||||
|
Customer ID: ID cliente
|
||||||
|
Client Name: Nombre del cliente
|
||||||
|
Salesperson: Comercial
|
||||||
|
Attender: Asistente
|
||||||
|
Responsible: Responsable
|
||||||
|
State: Estado
|
||||||
|
Item: Artículo
|
||||||
|
Created: Creada
|
||||||
|
More options: Más opciones
|
||||||
|
</i18n>
|
|
@ -1,32 +1,24 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import Paginate from 'src/components/PaginateData.vue';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { toDate } from 'src/filters/index';
|
import { toDate } from 'src/filters/index';
|
||||||
|
import Paginate from 'src/components/PaginateData.vue';
|
||||||
import ClaimSummaryDialog from './Card/ClaimSummaryDialog.vue';
|
import ClaimSummaryDialog from './Card/ClaimSummaryDialog.vue';
|
||||||
import CustomerDescriptorPopover from 'src/pages/Customer/Card/CustomerDescriptorPopover.vue';
|
import CustomerDescriptorPopover from 'src/pages/Customer/Card/CustomerDescriptorPopover.vue';
|
||||||
|
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||||
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
|
import ClaimFilter from './ClaimFilter.vue';
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const filter = {
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
include: [
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
{
|
|
||||||
relation: 'client',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
relation: 'claimState',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
relation: 'worker',
|
|
||||||
scope: {
|
|
||||||
include: { relation: 'user' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
function stateColor(code) {
|
function stateColor(code) {
|
||||||
if (code === 'pending') return 'green';
|
if (code === 'pending') return 'green';
|
||||||
|
@ -49,41 +41,66 @@ function viewSummary(id) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<teleport-slot to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="ClaimList"
|
||||||
|
:label="t('Search claim')"
|
||||||
|
:info="t('You can search by claim id or customer name')"
|
||||||
|
/>
|
||||||
|
</teleport-slot>
|
||||||
|
<teleport-slot to="#rightPanel">
|
||||||
|
<ClaimFilter data-key="ClaimList" />
|
||||||
|
</teleport-slot>
|
||||||
<q-page class="q-pa-md">
|
<q-page class="q-pa-md">
|
||||||
<paginate url="/Claims" :filter="filter" sort-by="id DESC" auto-load>
|
<paginate data-key="ClaimList" url="Claims/filter" order="id DESC" auto-load>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<q-card class="card" v-for="row of rows" :key="row.id">
|
<q-card class="card" v-for="row of rows" :key="row.id">
|
||||||
<q-item class="q-pa-none items-start cursor-pointer q-hoverable" v-ripple clickable>
|
<q-item
|
||||||
|
class="q-pa-none items-start cursor-pointer q-hoverable"
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
>
|
||||||
<q-item-section class="q-pa-md" @click="navigate(row.id)">
|
<q-item-section class="q-pa-md" @click="navigate(row.id)">
|
||||||
<div class="text-h6 link">
|
<div class="text-h6 link">
|
||||||
{{ row.client.name }}
|
{{ row.clientName }}
|
||||||
<q-popup-proxy>
|
|
||||||
<customer-descriptor-popover :customer="row.client" />
|
|
||||||
</q-popup-proxy>
|
|
||||||
</div>
|
</div>
|
||||||
<q-item-label caption>#{{ row.id }}</q-item-label>
|
<q-item-label caption>#{{ row.id }}</q-item-label>
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.list.customer') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ row.client.name }}</q-item-label>
|
{{ t('claim.list.customer') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ row.clientName }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.list.assignedTo') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ row.worker.user.name }}</q-item-label>
|
{{ t('claim.list.assignedTo') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ row.workerName }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.list.created') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toDate(row.created) }}</q-item-label>
|
{{ t('claim.list.created') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toDate(row.created) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.list.state') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('claim.list.state') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label>
|
<q-item-label>
|
||||||
<q-chip :color="stateColor(row.claimState.code)" dense>
|
<q-badge
|
||||||
{{ row.claimState.description }}
|
:color="stateColor(row.stateCode)"
|
||||||
</q-chip>
|
class="q-ma-none"
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
{{ row.stateDescription }}
|
||||||
|
</q-badge>
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
@ -111,16 +128,34 @@ function viewSummary(id) {
|
||||||
</q-menu>
|
</q-menu>
|
||||||
</q-btn> -->
|
</q-btn> -->
|
||||||
|
|
||||||
<q-btn flat round color="orange" icon="arrow_circle_right" @click="navigate(row.id)">
|
<q-btn
|
||||||
<q-tooltip>{{ t('components.smartCard.openCard') }}</q-tooltip>
|
flat
|
||||||
|
round
|
||||||
|
color="orange"
|
||||||
|
icon="arrow_circle_right"
|
||||||
|
@click="navigate(row.id)"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
{{ t('components.smartCard.openCard') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn flat round color="grey-7" icon="preview" @click="viewSummary(row.id)">
|
<q-btn
|
||||||
<q-tooltip>{{ t('components.smartCard.openSummary') }}</q-tooltip>
|
flat
|
||||||
|
round
|
||||||
|
color="grey-7"
|
||||||
|
icon="preview"
|
||||||
|
@click="viewSummary(row.id)"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
{{ t('components.smartCard.openSummary') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn flat round color="grey-7" icon="vn:client">
|
<q-btn flat round color="grey-7" icon="vn:client">
|
||||||
<q-tooltip>{{ t('components.smartCard.viewDescription') }}</q-tooltip>
|
<q-tooltip>
|
||||||
|
{{ t('components.smartCard.viewDescription') }}
|
||||||
|
</q-tooltip>
|
||||||
<q-popup-proxy>
|
<q-popup-proxy>
|
||||||
<customer-descriptor-popover :customer="row.client" />
|
<CustomerDescriptorPopover :id="row.clientFk" />
|
||||||
</q-popup-proxy>
|
</q-popup-proxy>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
|
@ -130,3 +165,9 @@ function viewSummary(id) {
|
||||||
</paginate>
|
</paginate>
|
||||||
</q-page>
|
</q-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search claim: Buscar reclamación
|
||||||
|
You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useState } from 'src/composables/useState';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
|
||||||
const state = useState();
|
const stateStore = useStateStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-drawer v-model="state.drawer.value" show-if-above :width="256" :breakpoint="500">
|
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
<q-scroll-area class="fit text-grey-8">
|
<q-scroll-area class="fit text-grey-8">
|
||||||
<LeftMenu />
|
<LeftMenu />
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
|
|
|
@ -4,16 +4,15 @@ import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Paginate from 'src/components/PaginateData.vue';
|
import Paginate from 'src/components/PaginateData.vue';
|
||||||
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
|
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const rmas = ref([]);
|
const arrayData = useArrayData('ClaimRmaList');
|
||||||
const card = ref(null);
|
const isLoading = ref(false);
|
||||||
|
const input = ref();
|
||||||
function onFetch(data) {
|
|
||||||
rmas.value = data.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newRma = ref({
|
const newRma = ref({
|
||||||
code: '',
|
code: '',
|
||||||
|
@ -24,46 +23,38 @@ function onInputUpdate(value) {
|
||||||
newRma.value.code = value.toUpperCase();
|
newRma.value.code = value.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function submit() {
|
async function submit() {
|
||||||
const formData = newRma.value;
|
const formData = newRma.value;
|
||||||
if (formData.code === '') return;
|
if (formData.code === '') return;
|
||||||
|
|
||||||
axios
|
isLoading.value = true;
|
||||||
.post('ClaimRmas', formData)
|
await axios.post('ClaimRmas', formData);
|
||||||
.then(() => {
|
await arrayData.refresh();
|
||||||
|
isLoading.value = false;
|
||||||
|
input.value.$el.focus();
|
||||||
|
|
||||||
newRma.value = {
|
newRma.value = {
|
||||||
code: '',
|
code: '',
|
||||||
crated: new Date(),
|
created: new Date(),
|
||||||
};
|
};
|
||||||
})
|
|
||||||
.then(() => card.value.refresh());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmShown = ref(false);
|
|
||||||
const rmaId = ref(null);
|
|
||||||
function confirm(id) {
|
function confirm(id) {
|
||||||
confirmShown.value = true;
|
quasar
|
||||||
rmaId.value = id;
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
})
|
||||||
|
.onOk(() => remove(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove() {
|
async function remove(id) {
|
||||||
const id = rmaId.value;
|
await axios.delete(`ClaimRmas/${id}`);
|
||||||
axios
|
await arrayData.refresh();
|
||||||
.delete(`ClaimRmas/${id}`)
|
|
||||||
.then(() => {
|
|
||||||
confirmShown.value = false;
|
|
||||||
|
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
message: 'Entry deleted',
|
message: t('globals.rowRemoved'),
|
||||||
icon: 'check',
|
icon: 'check',
|
||||||
});
|
});
|
||||||
})
|
|
||||||
.then(() => card.value.refresh());
|
|
||||||
}
|
|
||||||
|
|
||||||
function hide() {
|
|
||||||
rmaId.value = null;
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -73,34 +64,74 @@ function hide() {
|
||||||
<q-card class="card q-pa-md">
|
<q-card class="card q-pa-md">
|
||||||
<q-form @submit="submit">
|
<q-form @submit="submit">
|
||||||
<q-input
|
<q-input
|
||||||
|
ref="input"
|
||||||
v-model="newRma.code"
|
v-model="newRma.code"
|
||||||
:label="t('claim.rmaList.code')"
|
:label="t('claim.rmaList.code')"
|
||||||
@update:model-value="onInputUpdate"
|
@update:model-value="onInputUpdate"
|
||||||
class="q-mb-md"
|
class="q-mb-md"
|
||||||
|
:readonly="isLoading"
|
||||||
|
:loading="isLoading"
|
||||||
autofocus
|
autofocus
|
||||||
/>
|
/>
|
||||||
<div class="text-caption">{{ rmas.length }} {{ t('claim.rmaList.records') }}</div>
|
<div class="text-caption">
|
||||||
|
{{ arrayData.totalRows }} {{ t('claim.rmaList.records') }}
|
||||||
|
</div>
|
||||||
</q-form>
|
</q-form>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-page-sticky>
|
</q-page-sticky>
|
||||||
|
<paginate
|
||||||
<paginate ref="card" url="/ClaimRmas" @on-fetch="onFetch" sort-by="id DESC" auto-load>
|
data-key="ClaimRmaList"
|
||||||
|
url="ClaimRmas"
|
||||||
|
order="id DESC"
|
||||||
|
:offset="50"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<q-card class="card">
|
<q-card class="card">
|
||||||
<template v-for="row of rows" :key="row.code">
|
<template v-if="isLoading">
|
||||||
<q-item class="q-pa-none items-start">
|
<q-item class="q-pa-none items-start">
|
||||||
<q-item-section class="q-pa-md">
|
<q-item-section class="q-pa-md">
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.rmaList.code') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
<q-skeleton />
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label
|
||||||
|
><q-skeleton type="text"
|
||||||
|
/></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-item-section>
|
||||||
|
<q-card-actions vertical class="justify-between">
|
||||||
|
<q-skeleton type="circle" class="q-mb-md" size="40px" />
|
||||||
|
</q-card-actions>
|
||||||
|
</q-item>
|
||||||
|
<q-separator />
|
||||||
|
</template>
|
||||||
|
<template v-for="row of rows" :key="row.id">
|
||||||
|
<q-item class="q-pa-none items-start">
|
||||||
|
<q-item-section class="q-pa-md">
|
||||||
|
<q-list>
|
||||||
|
<q-item class="q-pa-none">
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label caption>{{
|
||||||
|
t('claim.rmaList.code')
|
||||||
|
}}</q-item-label>
|
||||||
<q-item-label>{{ row.code }}</q-item-label>
|
<q-item-label>{{ row.code }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-card-actions vertical class="justify-between">
|
<q-card-actions vertical class="justify-between">
|
||||||
<q-btn flat round color="primary" icon="vn:bin" @click="confirm(row.id)">
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
color="primary"
|
||||||
|
icon="vn:bin"
|
||||||
|
@click="confirm(row.id)"
|
||||||
|
>
|
||||||
<q-tooltip>{{ t('globals.remove') }}</q-tooltip>
|
<q-tooltip>{{ t('globals.remove') }}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
|
@ -111,20 +142,6 @@ function hide() {
|
||||||
</template>
|
</template>
|
||||||
</paginate>
|
</paginate>
|
||||||
</q-page>
|
</q-page>
|
||||||
|
|
||||||
<q-dialog v-model="confirmShown" persistent @hide="hide">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section class="row items-center">
|
|
||||||
<q-avatar icon="warning" color="primary" text-color="white" />
|
|
||||||
<span class="q-ml-sm">{{ t('globals.confirmRemove') }}</span>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<q-card-actions align="right">
|
|
||||||
<q-btn flat :label="t('globals.no')" color="primary" v-close-popup autofocus />
|
|
||||||
<q-btn flat :label="t('globals.yes')" color="primary" @click="remove()" />
|
|
||||||
</q-card-actions>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -48,8 +48,16 @@ const filterOptions = {
|
||||||
@on-fetch="setWorkers"
|
@on-fetch="setWorkers"
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<fetch-data url="ContactChannels" @on-fetch="(data) => contactChannels = data" auto-load />
|
<fetch-data
|
||||||
<fetch-data url="BusinessTypes" @on-fetch="(data) => businessTypes = data" auto-load />
|
url="ContactChannels"
|
||||||
|
@on-fetch="(data) => (contactChannels = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<fetch-data
|
||||||
|
url="BusinessTypes"
|
||||||
|
@on-fetch="(data) => (businessTypes = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<q-card>
|
<q-card>
|
||||||
<form-model :url="`Clients/${route.params.id}`" model="customer">
|
<form-model :url="`Clients/${route.params.id}`" model="customer">
|
||||||
|
@ -125,11 +133,14 @@ const filterOptions = {
|
||||||
:label="t('customer.basicData.salesPerson')"
|
:label="t('customer.basicData.salesPerson')"
|
||||||
map-options
|
map-options
|
||||||
use-input
|
use-input
|
||||||
@filter="(value, update) => filter(value, update, filterOptions)"
|
@filter="
|
||||||
|
(value, update) =>
|
||||||
|
filter(value, update, filterOptions)
|
||||||
|
"
|
||||||
:rules="validate('client.salesPersonFk')"
|
:rules="validate('client.salesPersonFk')"
|
||||||
:input-debounce="0"
|
:input-debounce="0"
|
||||||
>
|
>
|
||||||
<template #before>
|
<template #prepend>
|
||||||
<q-avatar color="orange">
|
<q-avatar color="orange">
|
||||||
<q-img
|
<q-img
|
||||||
v-if="data.salesPersonFk"
|
v-if="data.salesPersonFk"
|
||||||
|
|
|
@ -1,14 +1,25 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useState } from 'src/composables/useState';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import CustomerDescriptor from './CustomerDescriptor.vue';
|
import CustomerDescriptor from './CustomerDescriptor.vue';
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||||
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
|
|
||||||
const state = useState();
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<q-drawer v-model="state.drawer.value" show-if-above :width="256" :breakpoint="500">
|
<teleport-slot to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="CustomerList"
|
||||||
|
:label="t('Search customer')"
|
||||||
|
:info="t('You can search by customer id or name')"
|
||||||
|
/>
|
||||||
|
</teleport-slot>
|
||||||
|
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
<q-scroll-area class="fit">
|
<q-scroll-area class="fit">
|
||||||
<customer-descriptor />
|
<CustomerDescriptor />
|
||||||
<q-separator />
|
<q-separator />
|
||||||
<left-menu source="card" />
|
<left-menu source="card" />
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
|
@ -19,3 +30,9 @@ const state = useState();
|
||||||
</q-page>
|
</q-page>
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search customer: Buscar cliente
|
||||||
|
You can search by customer id or name: Puedes buscar por id o nombre del cliente
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { toCurrency } from 'src/filters';
|
import { toCurrency } from 'src/filters';
|
||||||
import axios from 'axios';
|
|
||||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
@ -15,87 +13,125 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await fetch();
|
|
||||||
});
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const entityId = computed(() => {
|
const entityId = computed(() => {
|
||||||
return $props.id || route.params.id;
|
return $props.id || route.params.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
const customer = ref();
|
|
||||||
async function fetch() {
|
|
||||||
const { data } = await axios.get(`Clients/${entityId.value}/getCard`);
|
|
||||||
|
|
||||||
if (data) customer.value = data;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<skeleton-descriptor v-if="!customer" />
|
<card-descriptor module="Customer" :url="`Clients/${entityId}/getCard`">
|
||||||
<card-descriptor v-if="customer" module="Customer" :data="customer" :description="customer.name">
|
<template #body="{ entity }">
|
||||||
<!-- <template #menu>
|
<q-list dense>
|
||||||
<q-item clickable v-ripple>Option 1</q-item>
|
<q-item v-if="entity.salesPersonUser" class="row">
|
||||||
<q-item clickable v-ripple>Option 2</q-item>
|
<q-item-label class="col" caption>
|
||||||
</template> -->
|
{{ t('customer.card.salesPerson') }}
|
||||||
<template #body>
|
</q-item-label>
|
||||||
<q-list>
|
<q-item-label class="col q-ma-none">
|
||||||
<q-item v-if="customer.salesPersonUser">
|
{{ entity.salesPersonUser.name }}
|
||||||
<q-item-section>
|
</q-item-label>
|
||||||
<q-item-label caption>{{ t('customer.card.salesPerson') }}</q-item-label>
|
|
||||||
<q-item-label>{{ customer.salesPersonUser.name }}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item class="row">
|
||||||
<q-item-section>
|
<q-item-label class="col" caption>
|
||||||
<q-item-label caption>{{ t('customer.card.credit') }}</q-item-label>
|
{{ t('customer.card.credit') }}
|
||||||
<q-item-label>{{ toCurrency(customer.credit) }}</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
<q-item-label class="col q-ma-none">
|
||||||
<q-item-section>
|
{{ toCurrency(entity.credit) }}
|
||||||
<q-item-label caption>{{ t('customer.card.securedCredit') }}</q-item-label>
|
</q-item-label>
|
||||||
<q-item-label>{{ toCurrency(customer.creditInsurance) }}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item class="row">
|
||||||
<q-item-section v-if="customer.payMethod">
|
<q-item-label class="col" caption>
|
||||||
<q-item-label caption>{{ t('customer.card.payMethod') }}</q-item-label>
|
{{ t('customer.card.securedCredit') }}
|
||||||
<q-item-label>{{ customer.payMethod.name }}</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
<q-item-label class="col q-ma-none">
|
||||||
<q-item-section>
|
{{ toCurrency(entity.creditInsurance) }}
|
||||||
<q-item-label caption>{{ t('customer.card.debt') }}</q-item-label>
|
</q-item-label>
|
||||||
<q-item-label>{{ toCurrency(customer.debt) }}</q-item-label>
|
</q-item>
|
||||||
</q-item-section>
|
<q-item v-if="entity.payMethod" class="row">
|
||||||
|
<q-item-label class="col" caption>
|
||||||
|
{{ t('customer.card.payMethod') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label class="col q-ma-none">
|
||||||
|
{{ entity.payMethod.name }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item>
|
||||||
|
<q-item class="row">
|
||||||
|
<q-item-label class="col" caption>
|
||||||
|
{{ t('customer.card.debt') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label class="col q-ma-none">
|
||||||
|
{{ toCurrency(entity.debt) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
<q-card-actions class="q-gutter-md">
|
<q-card-actions class="q-gutter-md">
|
||||||
<q-icon v-if="customer.isActive == false" name="vn:disabled" size="xs" color="primary">
|
<q-icon
|
||||||
|
v-if="entity.isActive == false"
|
||||||
|
name="vn:disabled"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
<q-tooltip>{{ t('customer.card.isDisabled') }}</q-tooltip>
|
<q-tooltip>{{ t('customer.card.isDisabled') }}</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
<q-icon v-if="customer.isFreezed == true" name="vn:frozen" size="xs" color="primary">
|
<q-icon
|
||||||
|
v-if="entity.isFreezed == true"
|
||||||
|
name="vn:frozen"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
<q-tooltip>{{ t('customer.card.isFrozen') }}</q-tooltip>
|
<q-tooltip>{{ t('customer.card.isFrozen') }}</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
<q-icon v-if="customer.debt > customer.credit" name="vn:risk" size="xs" color="primary">
|
<q-icon
|
||||||
|
v-if="entity.debt > entity.credit"
|
||||||
|
name="vn:risk"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
<q-tooltip>{{ t('customer.card.hasDebt') }}</q-tooltip>
|
<q-tooltip>{{ t('customer.card.hasDebt') }}</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
<q-icon v-if="customer.isTaxDataChecked == false" name="vn:no036" size="xs" color="primary">
|
<q-icon
|
||||||
|
v-if="entity.isTaxDataChecked == false"
|
||||||
|
name="vn:no036"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
<q-tooltip>{{ t('customer.card.notChecked') }}</q-tooltip>
|
<q-tooltip>{{ t('customer.card.notChecked') }}</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
<q-icon v-if="customer.account.active == false" name="vn:noweb" size="xs" color="primary">
|
<q-icon
|
||||||
|
v-if="entity.account && entity.account.active == false"
|
||||||
|
name="vn:noweb"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
<q-tooltip>{{ t('customer.card.noWebAccess') }}</q-tooltip>
|
<q-tooltip>{{ t('customer.card.noWebAccess') }}</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
<!-- <q-card-actions>
|
<q-card-actions>
|
||||||
<q-btn size="md" icon="vn:ticket" color="primary">
|
<q-btn
|
||||||
<q-tooltip>Ticket list</q-tooltip>
|
:to="{
|
||||||
|
name: 'TicketList',
|
||||||
|
query: { params: JSON.stringify({ clientFk: entity.id }) },
|
||||||
|
}"
|
||||||
|
size="md"
|
||||||
|
icon="vn:ticket"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<q-tooltip>{{ t('ticketList') }}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
<q-btn
|
||||||
<q-btn size="md" icon="vn:invoice-out" color="primary">
|
:to="{
|
||||||
<q-tooltip>Invoice Out list</q-tooltip>
|
name: 'InvoiceOutList',
|
||||||
|
query: { params: JSON.stringify({ clientFk: entity.id }) },
|
||||||
|
}"
|
||||||
|
size="md"
|
||||||
|
icon="vn:invoice-out"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<q-tooltip>{{ t('invoiceOutList') }}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
<!--
|
||||||
<q-btn size="md" icon="vn:basketadd" color="primary">
|
<q-btn size="md" icon="vn:basketadd" color="primary">
|
||||||
<q-tooltip>Order list</q-tooltip>
|
<q-tooltip>Order list</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
@ -106,8 +142,21 @@ async function fetch() {
|
||||||
|
|
||||||
<q-btn size="md" icon="expand_more" color="primary">
|
<q-btn size="md" icon="expand_more" color="primary">
|
||||||
<q-tooltip>More options</q-tooltip>
|
<q-tooltip>More options</q-tooltip>
|
||||||
</q-btn>
|
</q-btn> -->
|
||||||
</q-card-actions> -->
|
</q-card-actions>
|
||||||
</template>
|
</template>
|
||||||
</card-descriptor>
|
</card-descriptor>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
{
|
||||||
|
"en": {
|
||||||
|
"ticketList": "Customer ticket list",
|
||||||
|
"invoiceOutList": "Customer invoice out list"
|
||||||
|
},
|
||||||
|
"es": {
|
||||||
|
"ticketList": "Listado de tickets del cliente",
|
||||||
|
"invoiceOutList": "Listado de facturas del cliente"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, computed } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import axios from 'axios';
|
|
||||||
import { toCurrency, toPercentage, toDate } from 'src/filters';
|
import { toCurrency, toPercentage, toDate } from 'src/filters';
|
||||||
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
|
|
||||||
onMounted(() => fetch());
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -19,37 +16,29 @@ const $props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
const entityId = computed(() => $props.id || route.params.id);
|
const entityId = computed(() => $props.id || route.params.id);
|
||||||
|
const summary = ref();
|
||||||
const customer = ref(null);
|
const customer = computed(() => summary.value.entity);
|
||||||
function fetch() {
|
|
||||||
const id = entityId.value;
|
|
||||||
axios.get(`Clients/${id}/summary`).then(({ data }) => {
|
|
||||||
customer.value = data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const balanceDue = computed(() => {
|
const balanceDue = computed(() => {
|
||||||
return customer.value.defaulters.length && customer.value.defaulters[0].amount;
|
return (
|
||||||
|
customer.value &&
|
||||||
|
customer.value.defaulters.length &&
|
||||||
|
customer.value.defaulters[0].amount
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const balanceDueWarning = computed(() => (balanceDue.value ? 'negative' : ''));
|
const balanceDueWarning = computed(() => (balanceDue.value ? 'negative' : ''));
|
||||||
|
|
||||||
const claimRate = computed(() => {
|
const claimRate = computed(() => {
|
||||||
const data = customer.value;
|
return customer.value.claimsRatio.claimingRate * 100;
|
||||||
|
|
||||||
return data.claimsRatio.claimingRate * 100;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const priceIncreasingRate = computed(() => {
|
const priceIncreasingRate = computed(() => {
|
||||||
const data = customer.value;
|
return customer.value.claimsRatio.priceIncreasing / 100;
|
||||||
|
|
||||||
return data.claimsRatio.priceIncreasing / 100;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const debtWarning = computed(() => {
|
const debtWarning = computed(() => {
|
||||||
const data = customer.value;
|
return customer.value.debt.debt > customer.value.credit ? 'negative' : '';
|
||||||
|
|
||||||
return data.debt.debt > data.credit ? 'negative' : '';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const creditWarning = computed(() => {
|
const creditWarning = computed(() => {
|
||||||
|
@ -62,70 +51,92 @@ const creditWarning = computed(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="summary container">
|
<card-summary ref="summary" :url="`Clients/${entityId}/summary`">
|
||||||
<q-card>
|
<template #body="{ entity }">
|
||||||
<skeleton-summary v-if="!customer" />
|
<q-card-section class="row q-pa-none q-col-gutter-md">
|
||||||
<template v-if="customer">
|
|
||||||
<div class="header bg-primary q-pa-sm q-mb-md">{{ customer.id }} - {{ customer.name }}</div>
|
|
||||||
<div class="row q-pa-md q-col-gutter-md q-mb-md">
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<q-list>
|
<q-list dense>
|
||||||
<q-item-label header class="text-h6">
|
<q-item-label header class="text-h6">
|
||||||
{{ t('customer.summary.basicData') }}
|
{{ t('customer.summary.basicData') }}
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'CustomerBasicData', params: { id: entityId } }"
|
:to="{
|
||||||
|
name: 'CustomerBasicData',
|
||||||
|
params: { id: entity.id },
|
||||||
|
}"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<q-icon name="open_in_new" />
|
<q-icon name="open_in_new" />
|
||||||
</router-link>
|
</router-link>
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
|
<q-separator class="q-mb-md" />
|
||||||
|
|
||||||
|
<q-item class="row col">
|
||||||
|
<q-item-label class="col" caption>
|
||||||
|
{{ t('customer.summary.customerId') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label class="col q-ma-none">
|
||||||
|
{{ entity.id }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item>
|
||||||
|
<q-item class="row col">
|
||||||
|
<q-item-label class="col" caption>
|
||||||
|
{{ t('customer.summary.name') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label class="col q-ma-none">{{
|
||||||
|
entity.name
|
||||||
|
}}</q-item-label>
|
||||||
|
</q-item>
|
||||||
|
<q-item class="row col">
|
||||||
|
<q-item-label class="col" caption>
|
||||||
|
{{ t('customer.summary.contact') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label class="col q-ma-none">{{
|
||||||
|
entity.contact
|
||||||
|
}}</q-item-label>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-item v-if="entity.salesPersonUser" class="row col">
|
||||||
|
<q-item-label class="col" caption>
|
||||||
|
{{ t('customer.summary.salesPerson') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label class="col q-ma-none">
|
||||||
|
{{ entity.salesPersonUser.name }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-item class="row col">
|
||||||
|
<q-item-label class="col" caption>
|
||||||
|
{{ t('customer.summary.phone') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label class="col q-ma-none">{{
|
||||||
|
entity.phone
|
||||||
|
}}</q-item-label>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-item class="row col">
|
||||||
|
<q-item-label class="col" caption>
|
||||||
|
{{ t('customer.summary.mobile') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label class="col q-ma-none">{{
|
||||||
|
entity.mobile
|
||||||
|
}}</q-item-label>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-item v-if="entity.contactChannel" class="row col">
|
||||||
|
<q-item-label class="col" caption>
|
||||||
|
{{ t('customer.summary.contactChannel') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label class="col q-ma-none">
|
||||||
|
{{ entity.contactChannel.name }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.customerId') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.id }}</q-item-label>
|
{{ t('customer.summary.email') }}
|
||||||
</q-item-section>
|
</q-item-label>
|
||||||
</q-item>
|
<q-item-label>{{ entity.email }}</q-item-label>
|
||||||
<q-item>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label caption>{{ t('customer.summary.name') }}</q-item-label>
|
|
||||||
<q-item-label>{{ customer.name }}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label caption>{{ t('customer.summary.contact') }}</q-item-label>
|
|
||||||
<q-item-label>{{ customer.contact }}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item v-if="customer.salesPersonUser">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label caption>{{ t('customer.summary.salesPerson') }}</q-item-label>
|
|
||||||
<q-item-label>{{ customer.salesPersonUser.name }}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label caption>{{ t('customer.summary.phone') }}</q-item-label>
|
|
||||||
<q-item-label>{{ customer.phone }}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label caption>{{ t('customer.summary.mobile') }}</q-item-label>
|
|
||||||
<q-item-label>{{ customer.mobile }}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label caption>{{ t('customer.summary.email') }}</q-item-label>
|
|
||||||
<q-item-label>{{ customer.email }}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item v-if="customer.contactChannel">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label caption>{{ t('customer.summary.contactChannel') }}</q-item-label>
|
|
||||||
<q-item-label>{{ customer.contactChannel.name }}</q-item-label>
|
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
@ -137,38 +148,50 @@ const creditWarning = computed(() => {
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.socialName') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.socialName }}</q-item-label>
|
{{ t('customer.summary.socialName') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ entity.socialName }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.fiscalId') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.fi }}</q-item-label>
|
{{ t('customer.summary.fiscalId') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ entity.fi }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.postcode') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.postcode }}</q-item-label>
|
{{ t('customer.summary.postcode') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ entity.postcode }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item v-if="customer.province">
|
<q-item v-if="entity.province">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.province') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.province.name }}</q-item-label>
|
{{ t('customer.summary.province') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ entity.province.name }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item v-if="customer.country">
|
<q-item v-if="entity.country">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.country') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.country.country }}</q-item-label>
|
{{ t('customer.summary.country') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ entity.country.country }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.street') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.street }}</q-item-label>
|
{{ t('customer.summary.street') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ entity.street }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
@ -180,48 +203,52 @@ const creditWarning = computed(() => {
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-model="customer.isEqualizated"
|
v-model="entity.isEqualizated"
|
||||||
:label="t('customer.summary.isEqualizated')"
|
:label="t('customer.summary.isEqualizated')"
|
||||||
disable
|
disable
|
||||||
/>
|
/>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-model="customer.isActive"
|
v-model="entity.isActive"
|
||||||
:label="t('customer.summary.isActive')"
|
:label="t('customer.summary.isActive')"
|
||||||
disable
|
disable
|
||||||
/>
|
/>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-model="customer.hasToInvoiceByAddress"
|
v-model="entity.hasToInvoiceByAddress"
|
||||||
:label="t('customer.summary.invoiceByAddress')"
|
:label="t('customer.summary.invoiceByAddress')"
|
||||||
disable
|
disable
|
||||||
/>
|
/>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-model="customer.isTaxDataChecked"
|
v-model="entity.isTaxDataChecked"
|
||||||
:label="t('customer.summary.verifiedData')"
|
:label="t('customer.summary.verifiedData')"
|
||||||
disable
|
disable
|
||||||
/>
|
/>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-model="customer.hasToInvoice"
|
v-model="entity.hasToInvoice"
|
||||||
:label="t('customer.summary.hasToInvoice')"
|
:label="t('customer.summary.hasToInvoice')"
|
||||||
disable
|
disable
|
||||||
/>
|
/>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-model="customer.isToBeMailed"
|
v-model="entity.isToBeMailed"
|
||||||
:label="t('customer.summary.notifyByEmail')"
|
:label="t('customer.summary.notifyByEmail')"
|
||||||
disable
|
disable
|
||||||
/>
|
/>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-checkbox v-model="customer.isVies" :label="t('customer.summary.vies')" disable />
|
<q-checkbox
|
||||||
|
v-model="entity.isVies"
|
||||||
|
:label="t('customer.summary.vies')"
|
||||||
|
disable
|
||||||
|
/>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</div>
|
</div>
|
||||||
|
@ -232,80 +259,104 @@ const creditWarning = computed(() => {
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.payMethod') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.payMethod.name }}</q-item-label>
|
{{ t('customer.summary.payMethod') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ entity.payMethod.name }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.bankAccount') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.iban }}</q-item-label>
|
{{ t('customer.summary.bankAccount') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ entity.iban }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.dueDay') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.dueDay }}</q-item-label>
|
{{ t('customer.summary.dueDay') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ entity.dueDay }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item dense>
|
|
||||||
<q-checkbox v-model="customer.hasLcr" :label="t('customer.summary.hasLcr')" disable />
|
|
||||||
</q-item>
|
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-model="customer.hasCoreVnl"
|
v-model="entity.hasLcr"
|
||||||
|
:label="t('customer.summary.hasLcr')"
|
||||||
|
disable
|
||||||
|
/>
|
||||||
|
</q-item>
|
||||||
|
<q-item dense>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="entity.hasCoreVnl"
|
||||||
:label="t('customer.summary.hasCoreVnl')"
|
:label="t('customer.summary.hasCoreVnl')"
|
||||||
disable
|
disable
|
||||||
/>
|
/>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-model="customer.hasSepaVnl"
|
v-model="entity.hasSepaVnl"
|
||||||
:label="t('customer.summary.hasB2BVnl')"
|
:label="t('customer.summary.hasB2BVnl')"
|
||||||
disable
|
disable
|
||||||
/>
|
/>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</div>
|
</div>
|
||||||
<div class="col" v-if="customer.defaultAddress">
|
<div class="col" v-if="entity.defaultAddress">
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item-label header class="text-h6">
|
<q-item-label header class="text-h6">
|
||||||
{{ t('customer.summary.consignee') }}
|
{{ t('customer.summary.consignee') }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.addressName') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.defaultAddress.nickname }}</q-item-label>
|
{{ t('customer.summary.addressName') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ entity.defaultAddress.nickname }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.addressCity') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.defaultAddress.city }}</q-item-label>
|
{{ t('customer.summary.addressCity') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{
|
||||||
|
entity.defaultAddress.city
|
||||||
|
}}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.addressStreet') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.defaultAddress.street }}</q-item-label>
|
{{ t('customer.summary.addressStreet') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ entity.defaultAddress.street }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</div>
|
</div>
|
||||||
<div class="col" v-if="customer.account">
|
<div class="col" v-if="entity.account">
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item-label header class="text-h6">
|
<q-item-label header class="text-h6">
|
||||||
{{ t('customer.summary.webAccess') }}
|
{{ t('customer.summary.webAccess') }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.username') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ customer.account.name }}</q-item-label>
|
{{ t('customer.summary.username') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ entity.account.name }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-model="customer.account.active"
|
v-model="entity.account.active"
|
||||||
:label="t('customer.summary.webAccess')"
|
:label="t('customer.summary.webAccess')"
|
||||||
disable
|
disable
|
||||||
/>
|
/>
|
||||||
|
@ -319,33 +370,49 @@ const creditWarning = computed(() => {
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.totalGreuge') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toCurrency(customer.totalGreuge) }}</q-item-label>
|
{{ t('customer.summary.totalGreuge') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toCurrency(entity.totalGreuge) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item v-if="customer.mana">
|
<q-item v-if="entity.mana">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.mana') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toCurrency(customer.mana.mana) }}</q-item-label>
|
{{ t('customer.summary.mana') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toCurrency(entity.mana.mana) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item v-if="customer.claimsRatio">
|
<q-item v-if="entity.claimsRatio">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>
|
<q-item-label caption>
|
||||||
{{ t('customer.summary.priceIncreasingRate') }}
|
{{ t('customer.summary.priceIncreasingRate') }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
<q-item-label>{{ toPercentage(priceIncreasingRate) }}</q-item-label>
|
<q-item-label>
|
||||||
|
{{ toPercentage(priceIncreasingRate) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item v-if="customer.averageInvoiced">
|
<q-item v-if="entity.averageInvoiced">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.averageInvoiced') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toCurrency(customer.averageInvoiced.invoiced) }}</q-item-label>
|
{{ t('customer.summary.averageInvoiced') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toCurrency(entity.averageInvoiced.invoiced) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item v-if="customer.claimsRatio">
|
<q-item v-if="entity.claimsRatio">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.claimRate') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('customer.summary.claimRate') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label>{{ toPercentage(claimRate) }}</q-item-label>
|
<q-item-label>{{ toPercentage(claimRate) }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
@ -356,137 +423,110 @@ const creditWarning = computed(() => {
|
||||||
<q-item-label header class="text-h6">
|
<q-item-label header class="text-h6">
|
||||||
{{ t('customer.summary.financialData') }}
|
{{ t('customer.summary.financialData') }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
<q-item v-if="customer.debt">
|
<q-item v-if="entity.debt">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.risk') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('customer.summary.risk') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label :class="debtWarning">
|
<q-item-label :class="debtWarning">
|
||||||
{{ toCurrency(customer.debt.debt) }}
|
{{ toCurrency(entity.debt.debt) }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section side>
|
<q-item-section side>
|
||||||
<q-icon name="vn:info">
|
<q-icon name="vn:info">
|
||||||
<q-tooltip>{{ t('customer.summary.riskInfo') }}</q-tooltip>
|
<q-tooltip>
|
||||||
|
{{ t('customer.summary.riskInfo') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.credit') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('customer.summary.credit') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label :class="creditWarning">
|
<q-item-label :class="creditWarning">
|
||||||
{{ toCurrency(customer.credit) }}
|
{{ toCurrency(entity.credit) }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section side>
|
<q-item-section side>
|
||||||
<q-icon name="vn:info">
|
<q-icon name="vn:info">
|
||||||
<q-tooltip>{{ t('customer.summary.creditInfo') }}</q-tooltip>
|
<q-tooltip>
|
||||||
|
{{ t('customer.summary.creditInfo') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item v-if="customer.creditInsurance">
|
<q-item v-if="entity.creditInsurance">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.securedCredit') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toCurrency(customer.creditInsurance) }}</q-item-label>
|
{{ t('customer.summary.securedCredit') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toCurrency(entity.creditInsurance) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section side>
|
<q-item-section side>
|
||||||
<q-icon name="vn:info">
|
<q-icon name="vn:info">
|
||||||
<q-tooltip>{{ t('customer.summary.securedCreditInfo') }}</q-tooltip>
|
<q-tooltip>
|
||||||
|
{{ t('customer.summary.securedCreditInfo') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.balance') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toCurrency(customer.sumRisk) || toCurrency(0) }}</q-item-label>
|
{{ t('customer.summary.balance') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toCurrency(entity.sumRisk) || toCurrency(0) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section side>
|
<q-item-section side>
|
||||||
<q-icon name="vn:info">
|
<q-icon name="vn:info">
|
||||||
<q-tooltip>{{ t('customer.summary.balanceInfo') }}</q-tooltip>
|
<q-tooltip>
|
||||||
|
{{ t('customer.summary.balanceInfo') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item v-if="customer.defaulters">
|
<q-item v-if="entity.defaulters">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.balanceDue') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('customer.summary.balanceDue') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label :class="balanceDueWarning">
|
<q-item-label :class="balanceDueWarning">
|
||||||
{{ toCurrency(balanceDue) }}
|
{{ toCurrency(balanceDue) }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section side>
|
<q-item-section side>
|
||||||
<q-icon name="vn:info">
|
<q-icon name="vn:info">
|
||||||
<q-tooltip>{{ t('customer.summary.balanceDueInfo') }}</q-tooltip>
|
<q-tooltip>
|
||||||
|
{{ t('customer.summary.balanceDueInfo') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-icon>
|
</q-icon>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item v-if="customer.recovery">
|
<q-item v-if="entity.recovery">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.summary.recoverySince') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toDate(customer.recovery.started) }}</q-item-label>
|
{{ t('customer.summary.recoverySince') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toDate(entity.recovery.started) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</q-card-section>
|
||||||
</template>
|
</template>
|
||||||
</q-card>
|
</card-summary>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scoped>
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.q-card {
|
<style lang="scss">
|
||||||
width: 100%;
|
.q-item__label + .q-item__label {
|
||||||
max-width: 1200px;
|
margin: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.negative {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.summary {
|
|
||||||
.q-list {
|
|
||||||
.q-item__label--header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: $primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.row {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
.col {
|
|
||||||
min-width: 250px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#slider-container {
|
|
||||||
max-width: 80%;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.q-slider {
|
|
||||||
.q-slider__marker-labels:nth-child(1) {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
.q-slider__marker-labels:nth-child(2) {
|
|
||||||
transform: none;
|
|
||||||
left: auto !important;
|
|
||||||
right: 0%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.q-dialog .summary {
|
|
||||||
max-width: 1200px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, watch } from 'vue'
|
import { reactive, watch } from 'vue';
|
||||||
|
|
||||||
const customer = reactive({
|
const customer = reactive({
|
||||||
name: '',
|
name: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(() => customer.name, () => {
|
watch(
|
||||||
|
() => customer.name,
|
||||||
|
() => {
|
||||||
console.log('customer.name changed');
|
console.log('customer.name changed');
|
||||||
});
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -20,7 +23,7 @@ watch(() => customer.name, () => {
|
||||||
label="Your name *"
|
label="Your name *"
|
||||||
hint="Name and surname"
|
hint="Name and surname"
|
||||||
lazy-rules
|
lazy-rules
|
||||||
:rules="[val => val && val.length > 0 || 'Please type something']"
|
:rules="[(val) => (val && val.length > 0) || 'Please type something']"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-input
|
<q-input
|
||||||
|
@ -30,8 +33,8 @@ watch(() => customer.name, () => {
|
||||||
label="Your age *"
|
label="Your age *"
|
||||||
lazy-rules
|
lazy-rules
|
||||||
:rules="[
|
:rules="[
|
||||||
val => val !== null && val !== '' || 'Please type your age',
|
(val) => (val !== null && val !== '') || 'Please type your age',
|
||||||
val => val > 0 && val < 100 || 'Please type a real age'
|
(val) => (val > 0 && val < 100) || 'Please type a real age',
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const provinces = ref();
|
||||||
|
const workers = ref();
|
||||||
|
const zones = ref();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<fetch-data url="Provinces" @on-fetch="(data) => (provinces = data)" auto-load />
|
||||||
|
<fetch-data url="Zones" @on-fetch="(data) => (zones = data)" auto-load />
|
||||||
|
<fetch-data
|
||||||
|
url="Workers/activeWithInheritedRole"
|
||||||
|
:filter="{ where: { role: 'salesPerson' } }"
|
||||||
|
@on-fetch="(data) => (workers = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||||
|
<template #tags="{ tag, formatFn }">
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||||
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body="{ params, searchFn }">
|
||||||
|
<q-list dense>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input :label="t('FI')" v-model="params.fi" lazy-rules>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="badge" size="sm"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input :label="t('Name')" v-model="params.name" lazy-rules />
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:label="t('Social Name')"
|
||||||
|
v-model="params.socialName"
|
||||||
|
lazy-rules
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section v-if="!workers">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="workers">
|
||||||
|
<q-select
|
||||||
|
:label="t('Salesperson')"
|
||||||
|
v-model="params.salesPersonFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="workers"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section v-if="!provinces">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="provinces">
|
||||||
|
<q-select
|
||||||
|
:label="t('Province')"
|
||||||
|
v-model="params.provinceFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="provinces"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item class="q-mb-md">
|
||||||
|
<q-item-section>
|
||||||
|
<q-input :label="t('City')" v-model="params.city" lazy-rules />
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator />
|
||||||
|
<q-expansion-item :label="t('More options')" expand-separator>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:label="t('Phone')"
|
||||||
|
v-model="params.phone"
|
||||||
|
lazy-rules
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="phone" size="sm"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:label="t('Email')"
|
||||||
|
v-model="params.email"
|
||||||
|
lazy-rules
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="email" size="sm"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section v-if="!zones">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="zones">
|
||||||
|
<q-select
|
||||||
|
:label="t('Zone')"
|
||||||
|
v-model="params.zoneFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="zones"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:label="t('Postcode')"
|
||||||
|
v-model="params.postcode"
|
||||||
|
lazy-rules
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-expansion-item>
|
||||||
|
</q-list>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
params:
|
||||||
|
search: Contains
|
||||||
|
fi: FI
|
||||||
|
name: Name
|
||||||
|
socialName: Social Name
|
||||||
|
salesPersonFk: Salesperson
|
||||||
|
provinceFk: Province
|
||||||
|
city: City
|
||||||
|
phone: Phone
|
||||||
|
email: Email
|
||||||
|
zoneFk: Zone
|
||||||
|
postcode: Postcode
|
||||||
|
es:
|
||||||
|
params:
|
||||||
|
search: Contiene
|
||||||
|
fi: NIF
|
||||||
|
name: Nombre
|
||||||
|
socialName: Razón Social
|
||||||
|
salesPersonFk: Comercial
|
||||||
|
provinceFk: Provincia
|
||||||
|
city: Ciudad
|
||||||
|
phone: Teléfono
|
||||||
|
email: Email
|
||||||
|
zoneFk: Zona
|
||||||
|
postcode: CP
|
||||||
|
FI: NIF
|
||||||
|
Name: Nombre
|
||||||
|
Social Name: Razón social
|
||||||
|
Salesperson: Comercial
|
||||||
|
Province: Provincia
|
||||||
|
City: Ciudad
|
||||||
|
More options: Más opciones
|
||||||
|
Phone: Teléfono
|
||||||
|
Email: Email
|
||||||
|
Zone: Zona
|
||||||
|
Postcode: Código postal
|
||||||
|
</i18n>
|
|
@ -1,14 +1,23 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import Paginate from 'src/components/PaginateData.vue';
|
import Paginate from 'src/components/PaginateData.vue';
|
||||||
import CustomerSummaryDialog from './Card/CustomerSummaryDialog.vue';
|
import CustomerSummaryDialog from './Card/CustomerSummaryDialog.vue';
|
||||||
|
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||||
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
|
import CustomerFilter from './CustomerFilter.vue';
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
|
|
||||||
function navigate(id) {
|
function navigate(id) {
|
||||||
router.push({ path: `/customer/${id}` });
|
router.push({ path: `/customer/${id}` });
|
||||||
}
|
}
|
||||||
|
@ -24,24 +33,43 @@ function viewSummary(id) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<teleport-slot to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="CustomerList"
|
||||||
|
:label="t('Search customer')"
|
||||||
|
:info="t('You can search by customer id or name')"
|
||||||
|
/>
|
||||||
|
</teleport-slot>
|
||||||
|
<teleport-slot to="#rightPanel">
|
||||||
|
<CustomerFilter data-key="CustomerList" />
|
||||||
|
</teleport-slot>
|
||||||
<q-page class="q-pa-md">
|
<q-page class="q-pa-md">
|
||||||
<paginate url="/Clients" sort-by="id DESC" auto-load>
|
<paginate data-key="CustomerList" url="/Clients/filter" order="id DESC" auto-load>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<q-card class="card" v-for="row of rows" :key="row.id">
|
<q-card class="card" v-for="row of rows" :key="row.id">
|
||||||
<q-item class="q-pa-none items-start cursor-pointer q-hoverable" v-ripple clickable>
|
<q-item
|
||||||
|
class="q-pa-none items-start cursor-pointer q-hoverable"
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
>
|
||||||
<q-item-section class="q-pa-md" @click="navigate(row.id)">
|
<q-item-section class="q-pa-md" @click="navigate(row.id)">
|
||||||
<div class="text-h6">{{ row.name }}</div>
|
<div class="text-h6">{{ row.name }}</div>
|
||||||
<q-item-label caption>#{{ row.id }}</q-item-label>
|
<q-item-label caption>#{{ row.id }}</q-item-label>
|
||||||
|
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.list.email') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('customer.list.email') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label>{{ row.email }}</q-item-label>
|
<q-item-label>{{ row.email }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('customer.list.phone') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('customer.list.phone') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label>{{ row.phone }}</q-item-label>
|
<q-item-label>{{ row.phone }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
@ -69,11 +97,27 @@ function viewSummary(id) {
|
||||||
</q-menu>
|
</q-menu>
|
||||||
</q-btn> -->
|
</q-btn> -->
|
||||||
|
|
||||||
<q-btn flat round color="orange" icon="arrow_circle_right" @click="navigate(row.id)">
|
<q-btn
|
||||||
<q-tooltip>{{ t('components.smartCard.openCard') }}</q-tooltip>
|
flat
|
||||||
|
round
|
||||||
|
color="orange"
|
||||||
|
icon="arrow_circle_right"
|
||||||
|
@click="navigate(row.id)"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
{{ t('components.smartCard.openCard') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn flat round color="grey-7" icon="preview" @click="viewSummary(row.id)">
|
<q-btn
|
||||||
<q-tooltip>{{ t('components.smartCard.openSummary') }}</q-tooltip>
|
flat
|
||||||
|
round
|
||||||
|
color="grey-7"
|
||||||
|
icon="preview"
|
||||||
|
@click="viewSummary(row.id)"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
{{ t('components.smartCard.openSummary') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<!-- <q-btn flat round color="grey-7" icon="vn:ticket">
|
<!-- <q-btn flat round color="grey-7" icon="vn:ticket">
|
||||||
<q-tooltip>{{ t('customer.list.customerOrders') }}</q-tooltip>
|
<q-tooltip>{{ t('customer.list.customerOrders') }}</q-tooltip>
|
||||||
|
@ -85,3 +129,9 @@ function viewSummary(id) {
|
||||||
</paginate>
|
</paginate>
|
||||||
</q-page>
|
</q-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search customer: Buscar cliente
|
||||||
|
You can search by customer id or name: Puedes buscar por id o nombre del cliente
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useState } from 'src/composables/useState';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
|
||||||
const state = useState();
|
const stateStore = useStateStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-drawer v-model="state.drawer.value" show-if-above :width="256" :breakpoint="500">
|
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
<q-scroll-area class="fit text-grey-8">
|
<q-scroll-area class="fit text-grey-8">
|
||||||
<LeftMenu />
|
<LeftMenu />
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
|
@ -15,3 +15,10 @@ const state = useState();
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
#searchbar,
|
||||||
|
.search-panel {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,23 +1,26 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, computed } from 'vue';
|
import { onMounted, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useState } from 'src/composables/useState';
|
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useNavigationStore } from 'src/stores/useNavigationStore';
|
import { useNavigationStore } from 'src/stores/useNavigationStore';
|
||||||
|
|
||||||
const state = useState();
|
const stateStore = useStateStore();
|
||||||
const navigation = useNavigationStore();
|
const navigation = useNavigationStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => navigation.fetchPinned());
|
||||||
navigation.fetchPinned();
|
|
||||||
});
|
|
||||||
|
|
||||||
const pinnedModules = computed(() => navigation.getPinnedModules());
|
const pinnedModules = computed(() => navigation.getPinnedModules());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-drawer v-model="state.drawer.value" show-if-above :width="256" :breakpoint="500">
|
<q-drawer
|
||||||
|
v-model="stateStore.leftDrawer"
|
||||||
|
show-if-above
|
||||||
|
:width="256"
|
||||||
|
:breakpoint="1000"
|
||||||
|
>
|
||||||
<q-scroll-area class="fit text-grey-8">
|
<q-scroll-area class="fit text-grey-8">
|
||||||
<LeftMenu />
|
<LeftMenu />
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
|
@ -26,13 +29,19 @@ const pinnedModules = computed(() => navigation.getPinnedModules());
|
||||||
<q-page class="q-pa-md">
|
<q-page class="q-pa-md">
|
||||||
<div class="row items-start wrap q-col-gutter-md q-mb-lg">
|
<div class="row items-start wrap q-col-gutter-md q-mb-lg">
|
||||||
<div class="col-12 col-md">
|
<div class="col-12 col-md">
|
||||||
<div class="text-h6 text-grey-8 q-mb-sm">{{ t('globals.pinnedModules') }}</div>
|
<div class="text-h6 text-grey-8 q-mb-sm">
|
||||||
|
{{ t('globals.pinnedModules') }}
|
||||||
|
</div>
|
||||||
<q-card class="row flex-container q-pa-md">
|
<q-card class="row flex-container q-pa-md">
|
||||||
<div class="text-grey-5" v-if="pinnedModules.length === 0">
|
<div class="text-grey-5" v-if="pinnedModules.length === 0">
|
||||||
{{ t('pinnedInfo') }}
|
{{ t('pinnedInfo') }}
|
||||||
</div>
|
</div>
|
||||||
<template v-if="pinnedModules.length">
|
<template v-if="pinnedModules.length">
|
||||||
<div v-for="item of pinnedModules" :key="item.title" class="row no-wrap q-pa-xs flex-item">
|
<div
|
||||||
|
v-for="item of pinnedModules"
|
||||||
|
:key="item.title"
|
||||||
|
class="row no-wrap q-pa-xs flex-item"
|
||||||
|
>
|
||||||
<q-btn
|
<q-btn
|
||||||
align="evenly"
|
align="evenly"
|
||||||
padding="16px"
|
padding="16px"
|
||||||
|
|
|
@ -1,13 +1,27 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useState } from 'src/composables/useState';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import InvoiceOutDescriptor from './InvoiceOutDescriptor.vue';
|
import InvoiceOutDescriptor from './InvoiceOutDescriptor.vue';
|
||||||
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||||
|
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||||
|
|
||||||
const state = useState();
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<q-drawer v-model="state.drawer.value" show-if-above :width="256" :breakpoint="500">
|
<teleport-slot to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="InvoiceOutList"
|
||||||
|
:label="t('Search invoice')"
|
||||||
|
:info="t('You can search by invoice reference')"
|
||||||
|
/>
|
||||||
|
</teleport-slot>
|
||||||
|
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
<q-scroll-area class="fit">
|
<q-scroll-area class="fit">
|
||||||
<InvoiceOutDescriptor />
|
<InvoiceOutDescriptor />
|
||||||
|
<q-separator />
|
||||||
|
<left-menu source="card" />
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
</q-drawer>
|
</q-drawer>
|
||||||
<q-page-container>
|
<q-page-container>
|
||||||
|
@ -16,3 +30,9 @@ const state = useState();
|
||||||
</q-page>
|
</q-page>
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search invoice: Buscar factura emitida
|
||||||
|
You can search by invoice reference: Puedes buscar por referencia de la factura
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { toCurrency, toDate } from 'src/filters';
|
import { toCurrency, toDate } from 'src/filters';
|
||||||
import axios from 'axios';
|
|
||||||
import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
|
import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
|
||||||
import CustomerDescriptorPopover from 'src/pages/Customer/Card/CustomerDescriptorPopover.vue';
|
import CustomerDescriptorPopover from 'src/pages/Customer/Card/CustomerDescriptorPopover.vue';
|
||||||
|
|
||||||
|
@ -15,19 +14,14 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await fetch();
|
|
||||||
});
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const entityId = computed(() => {
|
const entityId = computed(() => {
|
||||||
return $props.id || route.params.id;
|
return $props.id || route.params.id;
|
||||||
});
|
});
|
||||||
|
const descriptor = ref();
|
||||||
|
|
||||||
const invoiceOut = ref();
|
|
||||||
async function fetch() {
|
|
||||||
const filter = {
|
const filter = {
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
|
@ -45,56 +39,79 @@ async function fetch() {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = { params: { filter } };
|
function ticketFilter(invoice) {
|
||||||
const { data } = await axios.get(`InvoiceOuts/${entityId.value}`, options);
|
return JSON.stringify({ refFk: invoice.ref });
|
||||||
if (data) invoiceOut.value = data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const filter = computed(() => {
|
|
||||||
return invoiceOut.value ? JSON.stringify({ refFk: invoiceOut.value.ref }) : null;
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<card-descriptor v-if="invoiceOut" module="InvoiceOut" :data="invoiceOut" :description="invoiceOut.ref">
|
<card-descriptor
|
||||||
<template #body>
|
ref="descriptor"
|
||||||
|
module="InvoiceOut"
|
||||||
|
:url="`InvoiceOuts/${entityId}`"
|
||||||
|
:filter="filter"
|
||||||
|
>
|
||||||
|
<template #description="{ entity }">
|
||||||
|
<span>
|
||||||
|
{{ entity.ref }}
|
||||||
|
<q-tooltip>{{ entity.ref }}</q-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #body="{ entity }">
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('invoiceOut.card.issued') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toDate(invoiceOut.issued) }}</q-item-label>
|
{{ t('invoiceOut.card.issued') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ toDate(entity.issued) }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('invoiceOut.card.amount') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toCurrency(invoiceOut.amount) }}</q-item-label>
|
{{ t('invoiceOut.card.amount') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ toCurrency(entity.amount) }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section v-if="invoiceOut.company">
|
<q-item-section v-if="entity.client">
|
||||||
<q-item-label caption>{{ t('invoiceOut.card.client') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('invoiceOut.card.client') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label class="link">
|
<q-item-label class="link">
|
||||||
{{ invoiceOut.client.name }}
|
{{ entity.client.name }}
|
||||||
<q-popup-proxy>
|
<q-popup-proxy>
|
||||||
<customer-descriptor-popover :id="invoiceOut.client.id" />
|
<customer-descriptor-popover :id="entity.client.id" />
|
||||||
</q-popup-proxy>
|
</q-popup-proxy>
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section v-if="invoiceOut.company">
|
<q-item-section v-if="entity.company">
|
||||||
<q-item-label caption>{{ t('invoiceOut.card.company') }}</q-item-label>
|
<q-item-label caption>{{
|
||||||
<q-item-label>{{ invoiceOut.company.code }}</q-item-label>
|
t('invoiceOut.card.company')
|
||||||
|
}}</q-item-label>
|
||||||
|
<q-item-label>{{ entity.company.code }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
<q-card-actions>
|
<q-card-actions>
|
||||||
<q-btn
|
<q-btn
|
||||||
|
v-if="entity.client"
|
||||||
size="md"
|
size="md"
|
||||||
icon="vn:client"
|
icon="vn:client"
|
||||||
color="primary"
|
color="primary"
|
||||||
:to="{ name: 'CustomerCard', params: { id: invoiceOut.client.id } }"
|
:to="{ name: 'CustomerCard', params: { id: entity.client.id } }"
|
||||||
>
|
>
|
||||||
<q-tooltip>{{ t('invoiceOut.card.customerCard') }}</q-tooltip>
|
<q-tooltip>{{ t('invoiceOut.card.customerCard') }}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn size="md" icon="vn:ticket" color="primary" :to="{ name: 'TicketList', params: { q: filter } }">
|
<q-btn
|
||||||
|
size="md"
|
||||||
|
icon="vn:ticket"
|
||||||
|
color="primary"
|
||||||
|
:to="{
|
||||||
|
name: 'TicketList',
|
||||||
|
query: { q: ticketFilter(entity) },
|
||||||
|
}"
|
||||||
|
>
|
||||||
<q-tooltip>{{ t('invoiceOut.card.ticketList') }}</q-tooltip>
|
<q-tooltip>{{ t('invoiceOut.card.ticketList') }}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
|
|
|
@ -0,0 +1,263 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const workers = ref();
|
||||||
|
const workersCopy = ref();
|
||||||
|
const states = ref();
|
||||||
|
|
||||||
|
function setWorkers(data) {
|
||||||
|
workers.value = data;
|
||||||
|
workersCopy.value = data;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<fetch-data url="ClaimStates" @on-fetch="(data) => (states = data)" auto-load />
|
||||||
|
<fetch-data
|
||||||
|
url="Workers/activeWithInheritedRole"
|
||||||
|
:filter="{ where: { role: 'salesPerson' } }"
|
||||||
|
@on-fetch="setWorkers"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||||
|
<template #tags="{ tag, formatFn }">
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||||
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body="{ params, searchFn }">
|
||||||
|
<q-list dense>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:label="t('Customer ID')"
|
||||||
|
v-model="params.clientFk"
|
||||||
|
lazy-rules
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="vn:client" size="sm"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input :label="t('FI')" v-model="params.fi" lazy-rules>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="badge" size="sm"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input :label="t('Amount')" v-model="params.amount" lazy-rules>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="euro" size="sm"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:label="t('Min')"
|
||||||
|
type="number"
|
||||||
|
v-model.number="params.min"
|
||||||
|
lazy-rules
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="euro" size="sm"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:label="t('Max')"
|
||||||
|
type="number"
|
||||||
|
v-model.number="params.max"
|
||||||
|
lazy-rules
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="euro" size="sm"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item class="q-mb-md">
|
||||||
|
<q-item-section>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="params.hasPdf"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:label="t('Has PDF')"
|
||||||
|
toggle-indeterminate
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator />
|
||||||
|
<q-expansion-item :label="t('More options')" expand-separator>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:label="t('Issued')"
|
||||||
|
v-model="params.issued"
|
||||||
|
mask="date"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<q-icon name="event" class="cursor-pointer">
|
||||||
|
<q-popup-proxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<q-date v-model="params.issued" landscape>
|
||||||
|
<div
|
||||||
|
class="row items-center justify-end q-gutter-sm"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
@click="save"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-date>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:label="t('Created')"
|
||||||
|
v-model="params.created"
|
||||||
|
mask="date"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<q-icon name="event" class="cursor-pointer">
|
||||||
|
<q-popup-proxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<q-date v-model="params.created" landscape>
|
||||||
|
<div
|
||||||
|
class="row items-center justify-end q-gutter-sm"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
@click="save"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-date>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input :label="t('Dued')" v-model="params.dued" mask="date">
|
||||||
|
<template #append>
|
||||||
|
<q-icon name="event" class="cursor-pointer">
|
||||||
|
<q-popup-proxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<q-date v-model="params.dued" landscape>
|
||||||
|
<div
|
||||||
|
class="row items-center justify-end q-gutter-sm"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
@click="save"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-date>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-expansion-item>
|
||||||
|
</q-list>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
params:
|
||||||
|
search: Contains
|
||||||
|
clientFk: Customer
|
||||||
|
fi: FI
|
||||||
|
amount: Amount
|
||||||
|
min: Min
|
||||||
|
max: Max
|
||||||
|
hasPdf: Has PDF
|
||||||
|
issued: Issued
|
||||||
|
created: Created
|
||||||
|
dued: Dued
|
||||||
|
es:
|
||||||
|
params:
|
||||||
|
search: Contiene
|
||||||
|
clientFk: Cliente
|
||||||
|
fi: CIF
|
||||||
|
amount: Importe
|
||||||
|
min: Min
|
||||||
|
max: Max
|
||||||
|
hasPdf: Tiene PDF
|
||||||
|
issued: Emitida
|
||||||
|
created: Creada
|
||||||
|
dued: Vencida
|
||||||
|
Customer ID: ID cliente
|
||||||
|
FI: CIF
|
||||||
|
Amount: Importe
|
||||||
|
Has PDF: Tiene PDF
|
||||||
|
Issued: Fecha emisión
|
||||||
|
Created: Fecha creación
|
||||||
|
Dued: Fecha vencimiento
|
||||||
|
More options: Más opciones
|
||||||
|
</i18n>
|
|
@ -1,15 +1,24 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import Paginate from 'src/components/PaginateData.vue';
|
import Paginate from 'src/components/PaginateData.vue';
|
||||||
import InvoiceOutSummaryDialog from './Card/InvoiceOutSummaryDialog.vue';
|
import InvoiceOutSummaryDialog from './Card/InvoiceOutSummaryDialog.vue';
|
||||||
import { toDate, toCurrency } from 'src/filters/index';
|
import { toDate, toCurrency } from 'src/filters/index';
|
||||||
|
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||||
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
|
import InvoiceOutFilter from './InvoiceOutFilter.vue';
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
|
|
||||||
function navigate(id) {
|
function navigate(id) {
|
||||||
router.push({ path: `/invoiceOut/${id}` });
|
router.push({ path: `/invoiceOut/${id}` });
|
||||||
}
|
}
|
||||||
|
@ -25,54 +34,111 @@ function viewSummary(id) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<teleport-slot to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="InvoiceOutList"
|
||||||
|
:label="t('Search invoice')"
|
||||||
|
:info="t('You can search by invoice reference')"
|
||||||
|
/>
|
||||||
|
</teleport-slot>
|
||||||
|
<teleport-slot to="#rightPanel">
|
||||||
|
<InvoiceOutFilter data-key="InvoiceOutList" />
|
||||||
|
</teleport-slot>
|
||||||
<q-page class="q-pa-md">
|
<q-page class="q-pa-md">
|
||||||
<paginate url="/InvoiceOuts/filter" sort-by="issued DESC, id DESC" auto-load>
|
<paginate
|
||||||
|
data-key="InvoiceOutList"
|
||||||
|
url="InvoiceOuts/filter"
|
||||||
|
order="issued DESC, id DESC"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<q-card class="card" v-for="row of rows" :key="row.id">
|
<q-card class="card" v-for="row of rows" :key="row.id">
|
||||||
<q-item class="q-pa-none items-start cursor-pointer q-hoverable" v-ripple clickable>
|
<q-item
|
||||||
|
class="q-pa-none items-start cursor-pointer q-hoverable"
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
>
|
||||||
<q-item-section class="q-pa-md" @click="navigate(row.id)">
|
<q-item-section class="q-pa-md" @click="navigate(row.id)">
|
||||||
<div class="text-h6">{{ row.ref }}</div>
|
<div class="text-h6">{{ row.ref }}</div>
|
||||||
<q-item-label caption>#{{ row.id }}</q-item-label>
|
<q-item-label caption>#{{ row.id }}</q-item-label>
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('invoiceOut.list.issued') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toDate(row.issued) }}</q-item-label>
|
{{ t('invoiceOut.list.issued') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toDate(row.issued) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('invoiceOut.list.amount') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toCurrency(row.amount) }}</q-item-label>
|
{{ t('invoiceOut.list.amount') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toCurrency(row.amount) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('invoiceOut.list.client') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ row.clientSocialName }}</q-item-label>
|
{{ t('invoiceOut.list.client') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ row.clientSocialName }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('invoiceOut.list.created') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toDate(row.created) }}</q-item-label>
|
{{ t('invoiceOut.list.created') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toDate(row.created) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('invoiceOut.list.company') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('invoiceOut.list.company') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label>{{ row.companyCode }}</q-item-label>
|
<q-item-label>{{ row.companyCode }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('invoiceOut.list.dued') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toDate(row.dued) }}</q-item-label>
|
{{ t('invoiceOut.list.dued') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toDate(row.dued) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-separator vertical />
|
<q-separator vertical />
|
||||||
<q-card-actions vertical class="justify-between">
|
<q-card-actions vertical class="justify-between">
|
||||||
<q-btn flat round color="orange" icon="arrow_circle_right" @click="navigate(row.id)">
|
<q-btn
|
||||||
<q-tooltip>{{ t('components.smartCard.openCard') }}</q-tooltip>
|
flat
|
||||||
|
round
|
||||||
|
color="orange"
|
||||||
|
icon="arrow_circle_right"
|
||||||
|
@click="navigate(row.id)"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
{{ t('components.smartCard.openCard') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn flat round color="grey-7" icon="preview" @click="viewSummary(row.id)">
|
<q-btn
|
||||||
<q-tooltip>{{ t('components.smartCard.openSummary') }}</q-tooltip>
|
flat
|
||||||
|
round
|
||||||
|
color="grey-7"
|
||||||
|
icon="preview"
|
||||||
|
@click="viewSummary(row.id)"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
{{ t('components.smartCard.openSummary') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
@ -81,3 +147,9 @@ function viewSummary(id) {
|
||||||
</paginate>
|
</paginate>
|
||||||
</q-page>
|
</q-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search invoice: Buscar factura emitida
|
||||||
|
You can search by invoice reference: Puedes buscar por referencia de la factura
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useState } from 'src/composables/useState';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import LeftMenu from 'src/components/LeftMenu.vue';
|
import LeftMenu from 'src/components/LeftMenu.vue';
|
||||||
|
|
||||||
const state = useState();
|
const stateStore = useStateStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-drawer v-model="state.drawer.value" show-if-above :width="256" :breakpoint="500">
|
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
<q-scroll-area class="fit text-grey-8">
|
<q-scroll-area class="fit text-grey-8">
|
||||||
<LeftMenu />
|
<LeftMenu />
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
|
|
|
@ -138,6 +138,7 @@ async function onSubmit() {
|
||||||
color="primary"
|
color="primary"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
rounded
|
rounded
|
||||||
|
unelevated
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</q-form>
|
</q-form>
|
||||||
|
|
|
@ -1,14 +1,25 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useState } from 'src/composables/useState';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import TicketDescriptor from './TicketDescriptor.vue';
|
import TicketDescriptor from './TicketDescriptor.vue';
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||||
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
|
|
||||||
const state = useState();
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<q-drawer v-model="state.drawer.value" show-if-above :width="256" :breakpoint="500">
|
<teleport-slot to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="TicketList"
|
||||||
|
:label="t('Search ticket')"
|
||||||
|
:info="t('You can search by ticket id or alias')"
|
||||||
|
/>
|
||||||
|
</teleport-slot>
|
||||||
|
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
<q-scroll-area class="fit">
|
<q-scroll-area class="fit">
|
||||||
<ticket-descriptor />
|
<TicketDescriptor />
|
||||||
<q-separator />
|
<q-separator />
|
||||||
<left-menu source="card" />
|
<left-menu source="card" />
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
|
@ -48,3 +59,9 @@ const state = useState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search ticket: Buscar ticket
|
||||||
|
You can search by ticket id or alias: Puedes buscar por id o alias del ticket
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, computed, ref } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { toDate } from 'src/filters';
|
import { toDate } from 'src/filters';
|
||||||
import axios from 'axios';
|
|
||||||
import CustomerDescriptorPopover from 'src/pages/Customer/Card/CustomerDescriptorPopover.vue';
|
import CustomerDescriptorPopover from 'src/pages/Customer/Card/CustomerDescriptorPopover.vue';
|
||||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
@ -16,10 +14,6 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await fetch();
|
|
||||||
});
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@ -27,12 +21,35 @@ const entityId = computed(() => {
|
||||||
return $props.id || route.params.id;
|
return $props.id || route.params.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
const ticket = ref();
|
const filter = {
|
||||||
async function fetch() {
|
include: [
|
||||||
const { data } = await axios.get(`Tickets/${entityId.value}/summary`);
|
{
|
||||||
|
relation: 'client',
|
||||||
if (data) ticket.value = data;
|
scope: {
|
||||||
}
|
fields: ['id', 'name', 'salesPersonFk'],
|
||||||
|
include: { relation: 'salesPersonUser' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'ticketState',
|
||||||
|
scope: {
|
||||||
|
include: { relation: 'state' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'warehouse',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'agencyMode',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
function stateColor(state) {
|
function stateColor(state) {
|
||||||
if (state.code === 'OK') return 'text-green';
|
if (state.code === 'OK') return 'text-green';
|
||||||
|
@ -43,55 +60,64 @@ function stateColor(state) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<skeleton-descriptor v-if="!ticket" />
|
<card-descriptor module="Ticket" :url="`Tickets/${entityId}`" :filter="filter">
|
||||||
<card-descriptor v-if="ticket" module="Ticket" :data="ticket" :description="ticket.client.name">
|
<template #description="{ entity }">
|
||||||
<!-- <template #menu>
|
<span>
|
||||||
<q-item clickable v-ripple>Option 1</q-item>
|
{{ entity.client.name }}
|
||||||
<q-item clickable v-ripple>Option 2</q-item>
|
<q-tooltip>{{ entity.client.name }}</q-tooltip>
|
||||||
</template> -->
|
</span>
|
||||||
<template #body>
|
</template>
|
||||||
|
<template #body="{ entity }">
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section v-if="entity.ticketState">
|
||||||
<q-item-label caption>{{ t('ticket.card.ticketId') }}</q-item-label>
|
|
||||||
<q-item-label>#{{ ticket.id }}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label caption>{{ t('ticket.card.state') }}</q-item-label>
|
<q-item-label caption>{{ t('ticket.card.state') }}</q-item-label>
|
||||||
<q-item-label :class="stateColor(ticket.ticketState.state)">
|
<q-item-label :class="stateColor(entity.ticketState.state)">
|
||||||
{{ ticket.ticketState.state.name }}
|
{{ entity.ticketState.state.name }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label caption>
|
||||||
|
{{ t('ticket.card.shipped') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>{{ toDate(entity.shipped) }}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('ticket.card.customerId') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label class="link">
|
{{ t('ticket.card.customerId') }}
|
||||||
{{ ticket.clientFk }}
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
<span class="link">
|
||||||
|
{{ entity.clientFk }}
|
||||||
<q-popup-proxy>
|
<q-popup-proxy>
|
||||||
<customer-descriptor-popover :id="ticket.client.id" />
|
<customer-descriptor-popover :id="entity.client.id" />
|
||||||
</q-popup-proxy>
|
</q-popup-proxy>
|
||||||
|
</span>
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section v-if="entity.client && entity.client.salesPersonUser">
|
||||||
<q-item-label caption>{{ t('ticket.card.salesPerson') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ ticket.client.salesPersonUser.name }}</q-item-label>
|
{{ t('ticket.card.salesPerson') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ entity.client.salesPersonUser.name }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section v-if="entity.warehouse">
|
||||||
<q-item-label caption>{{ t('ticket.card.warehouse') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ ticket.warehouse.name }}</q-item-label>
|
{{ t('ticket.card.warehouse') }}
|
||||||
</q-item-section>
|
</q-item-label>
|
||||||
<q-item-section>
|
<q-item-label>{{ entity.warehouse.name }}</q-item-label>
|
||||||
<q-item-label caption>{{ t('ticket.card.shipped') }}</q-item-label>
|
|
||||||
<q-item-label>{{ toDate(ticket.shipped) }}</q-item-label>
|
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item v-if="entity.agencyMode">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('ticket.card.agency') }}</q-item-label>
|
<q-item-label caption>{{ t('ticket.card.agency') }}</q-item-label>
|
||||||
<q-item-label>{{ ticket.agencyMode.name }}</q-item-label>
|
<q-item-label>{{ entity.agencyMode.name }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
@ -101,7 +127,7 @@ function stateColor(state) {
|
||||||
size="md"
|
size="md"
|
||||||
icon="vn:client"
|
icon="vn:client"
|
||||||
color="primary"
|
color="primary"
|
||||||
:to="{ name: 'CustomerCard', params: { id: ticket.clientFk } }"
|
:to="{ name: 'CustomerCard', params: { id: entity.clientFk } }"
|
||||||
>
|
>
|
||||||
<q-tooltip>{{ t('ticket.card.customerCard') }}</q-tooltip>
|
<q-tooltip>{{ t('ticket.card.customerCard') }}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
|
|
@ -29,6 +29,7 @@ const entityId = computed(() => $props.id || route.params.id);
|
||||||
const ticket = ref();
|
const ticket = ref();
|
||||||
const salesLines = ref(null);
|
const salesLines = ref(null);
|
||||||
const editableStates = ref([]);
|
const editableStates = ref([]);
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
const { data } = await axios.get(`Tickets/${entityId.value}/summary`);
|
const { data } = await axios.get(`Tickets/${entityId.value}/summary`);
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
|
@ -0,0 +1,325 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const workers = ref();
|
||||||
|
const provinces = ref();
|
||||||
|
const states = ref();
|
||||||
|
const agencies = ref();
|
||||||
|
const warehouses = ref();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<fetch-data url="Provinces" @on-fetch="(data) => (provinces = data)" auto-load />
|
||||||
|
<fetch-data url="States" @on-fetch="(data) => (states = data)" auto-load />
|
||||||
|
<fetch-data url="AgencyModes" @on-fetch="(data) => (agencies = data)" auto-load />
|
||||||
|
<fetch-data url="Warehouses" @on-fetch="(data) => (warehouses = data)" auto-load />
|
||||||
|
<fetch-data
|
||||||
|
url="Workers/activeWithInheritedRole"
|
||||||
|
:filter="{ where: { role: 'salesPerson' } }"
|
||||||
|
@on-fetch="(data) => (workers = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||||
|
<template #tags="{ tag, formatFn }">
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||||
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body="{ params, searchFn }">
|
||||||
|
<q-list dense>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
v-model="params.clientFk"
|
||||||
|
:label="t('Customer ID')"
|
||||||
|
lazy-rules
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
v-model="params.orderFk"
|
||||||
|
:label="t('Order ID')"
|
||||||
|
lazy-rules
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input v-model="params.dateFrom" :label="t('From')" mask="date">
|
||||||
|
<template #append>
|
||||||
|
<q-icon name="event" class="cursor-pointer">
|
||||||
|
<q-popup-proxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<q-date v-model="params.dateFrom" landscape>
|
||||||
|
<div
|
||||||
|
class="row items-center justify-end q-gutter-sm"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
@click="save"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-date>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input v-model="params.dateTo" :label="t('To')" mask="date">
|
||||||
|
<template #append>
|
||||||
|
<q-icon name="event" class="cursor-pointer">
|
||||||
|
<q-popup-proxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<q-date v-model="params.dateTo" landscape>
|
||||||
|
<div
|
||||||
|
class="row items-center justify-end q-gutter-sm"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
@click="save"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-date>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section v-if="!workers">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="workers">
|
||||||
|
<q-select
|
||||||
|
:label="t('Salesperson')"
|
||||||
|
v-model="params.salesPersonFk"
|
||||||
|
:options="workers"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section v-if="!states">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="states">
|
||||||
|
<q-select
|
||||||
|
:label="t('State')"
|
||||||
|
v-model="params.stateFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="states"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
v-model="params.refFk"
|
||||||
|
:label="t('Invoice Ref.')"
|
||||||
|
lazy-rules
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="params.myTeam"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:label="t('My team')"
|
||||||
|
toggle-indeterminate
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="params.pending"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:label="t('Pending')"
|
||||||
|
toggle-indeterminate
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="params.hasInvoice"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:label="t('Invoiced')"
|
||||||
|
toggle-indeterminate
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="params.hasRoute"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:label="t('Routed')"
|
||||||
|
toggle-indeterminate
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="params.problems"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:label="t('With problems')"
|
||||||
|
toggle-indeterminate
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator />
|
||||||
|
<q-expansion-item :label="t('More options')" expand-separator>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section v-if="!provinces">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="provinces">
|
||||||
|
<q-select
|
||||||
|
:label="t('Province')"
|
||||||
|
v-model="params.provinceFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="provinces"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section v-if="!states">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="!states">
|
||||||
|
<q-select
|
||||||
|
:label="t('Agency')"
|
||||||
|
v-model="params.agencyModeFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="agencies"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section v-if="!warehouses">
|
||||||
|
<q-skeleton type="QInput" class="full-width" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section v-if="warehouses">
|
||||||
|
<q-select
|
||||||
|
:label="t('Warehouse')"
|
||||||
|
v-model="params.warehouseFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="warehouses"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-expansion-item>
|
||||||
|
</q-list>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
params:
|
||||||
|
search: Contains
|
||||||
|
clientFk: Customer
|
||||||
|
orderFK: Order
|
||||||
|
dateFrom: From
|
||||||
|
dateTo: To
|
||||||
|
salesPersonFk: Salesperson
|
||||||
|
stateFk: State
|
||||||
|
refFk: Invoice Ref.
|
||||||
|
myTeam: My team
|
||||||
|
pending: Pending
|
||||||
|
hasInvoice: Invoiced
|
||||||
|
hasRoute: Routed
|
||||||
|
provinceFk: Province
|
||||||
|
agencyModeFk: Agency
|
||||||
|
warehouseFk: Warehouse
|
||||||
|
es:
|
||||||
|
params:
|
||||||
|
search: Contiene
|
||||||
|
clientFk: Cliente
|
||||||
|
orderFK: Pedido
|
||||||
|
dateFrom: Desde
|
||||||
|
dateTo: Hasta
|
||||||
|
salesPersonFk: Comercial
|
||||||
|
stateFk: Estado
|
||||||
|
refFk: Ref. Factura
|
||||||
|
myTeam: Mi equipo
|
||||||
|
pending: Pendiente
|
||||||
|
hasInvoice: Facturado
|
||||||
|
hasRoute: Enrutado
|
||||||
|
Customer ID: ID Cliente
|
||||||
|
Order ID: ID Pedido
|
||||||
|
From: Desde
|
||||||
|
To: Hasta
|
||||||
|
Salesperson: Comercial
|
||||||
|
State: Estado
|
||||||
|
Invoice Ref.: Ref. Factura
|
||||||
|
My team: Mi equipo
|
||||||
|
Pending: Pendiente
|
||||||
|
With problems: Con problemas
|
||||||
|
Invoiced: Facturado
|
||||||
|
Routed: Enrutado
|
||||||
|
More options: Más opciones
|
||||||
|
Province: Provincia
|
||||||
|
Agency: Agencia
|
||||||
|
Warehouse: Almacén
|
||||||
|
Yes: Si
|
||||||
|
No: No
|
||||||
|
</i18n>
|
|
@ -1,14 +1,24 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import Paginate from 'src/components/PaginateData.vue';
|
import Paginate from 'src/components/PaginateData.vue';
|
||||||
import { toDate, toCurrency } from 'src/filters/index';
|
import { toDate, toCurrency } from 'src/filters/index';
|
||||||
import TicketSummaryDialog from './Card/TicketSummaryDialog.vue';
|
import TicketSummaryDialog from './Card/TicketSummaryDialog.vue';
|
||||||
|
|
||||||
|
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||||
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
|
import TicketFilter from './TicketFilter.vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
|
||||||
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
include: [
|
include: [
|
||||||
|
@ -38,11 +48,12 @@ const filter = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
function stateColor(state) {
|
function stateColor(row) {
|
||||||
if (state.code === 'OK') return 'green';
|
if (row.alertLevelCode === 'OK') return 'green';
|
||||||
if (state.code === 'FREE') return 'blue-3';
|
if (row.alertLevelCode === 'FREE') return 'blue-3';
|
||||||
if (state.alertLevel === 1) return 'orange';
|
if (row.alertLevel === 1) return 'orange';
|
||||||
if (state.alertLevel === 0) return 'red';
|
if (row.alertLevel === 0) return 'red';
|
||||||
|
return 'red';
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigate(id) {
|
function navigate(id) {
|
||||||
|
@ -60,58 +71,118 @@ function viewSummary(id) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<teleport-slot to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="TicketList"
|
||||||
|
:label="t('Search ticket')"
|
||||||
|
:info="t('You can search by ticket id or alias')"
|
||||||
|
/>
|
||||||
|
</teleport-slot>
|
||||||
|
<teleport-slot to="#rightPanel">
|
||||||
|
<TicketFilter data-key="TicketList" />
|
||||||
|
</teleport-slot>
|
||||||
<q-page class="q-pa-md">
|
<q-page class="q-pa-md">
|
||||||
<paginate url="/Tickets" :filter="filter" sort-by="id DESC" auto-load>
|
<paginate
|
||||||
|
data-key="TicketList"
|
||||||
|
url="Tickets/filter"
|
||||||
|
:filter="filter"
|
||||||
|
order="id DESC"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<q-card class="card" v-for="row of rows" :key="row.id">
|
<q-card class="card" v-for="row of rows" :key="row.id">
|
||||||
<q-item class="q-pa-none items-start cursor-pointer q-hoverable" v-ripple clickable>
|
<q-item
|
||||||
|
class="q-pa-none items-start cursor-pointer q-hoverable"
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
>
|
||||||
<q-item-section class="q-pa-md" @click="navigate(row.id)">
|
<q-item-section class="q-pa-md" @click="navigate(row.id)">
|
||||||
<div class="text-h6">{{ row.name }}</div>
|
<div class="text-h6">{{ row.name }}</div>
|
||||||
<q-item-label caption>#{{ row.id }}</q-item-label>
|
<q-item-label caption>#{{ row.id }}</q-item-label>
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('ticket.list.nickname') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('ticket.list.nickname') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label>{{ row.nickname }}</q-item-label>
|
<q-item-label>{{ row.nickname }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('ticket.list.state') }}</q-item-label>
|
<q-item-label caption>
|
||||||
|
{{ t('ticket.list.state') }}
|
||||||
|
</q-item-label>
|
||||||
<q-item-label>
|
<q-item-label>
|
||||||
<q-chip :color="stateColor(row.ticketState)" dense>
|
<q-badge
|
||||||
{{ row.ticketState.state.name }}
|
:color="stateColor(row)"
|
||||||
</q-chip>
|
class="q-ma-none"
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
{{ row.state }}
|
||||||
|
</q-badge>
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('ticket.list.shipped') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toDate(row.shipped) }}</q-item-label>
|
{{ t('ticket.list.shipped') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toDate(row.shipped) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('ticket.list.landed') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toDate(row.landed) }}</q-item-label>
|
{{ t('Zone') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ row.zoneName }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section v-if="row.client.salesPersonUser">
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('ticket.list.salesPerson') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ row.client.salesPersonUser.name }}</q-item-label>
|
{{ t('ticket.list.salesPerson') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ row.salesPerson }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('ticket.list.total') }}</q-item-label>
|
<q-item-label caption>
|
||||||
<q-item-label>{{ toCurrency(row.totalWithVat) }}</q-item-label>
|
{{ t('ticket.list.total') }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label>
|
||||||
|
{{ toCurrency(row.totalWithVat) }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-separator vertical />
|
<q-separator vertical />
|
||||||
<q-card-actions vertical class="justify-between">
|
<q-card-actions vertical class="justify-between">
|
||||||
<q-btn flat round color="orange" icon="arrow_circle_right" @click="navigate(row.id)">
|
<q-btn
|
||||||
<q-tooltip>{{ t('components.smartCard.openCard') }}</q-tooltip>
|
flat
|
||||||
|
round
|
||||||
|
color="orange"
|
||||||
|
icon="arrow_circle_right"
|
||||||
|
@click="navigate(row.id)"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
{{ t('components.smartCard.openCard') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn flat round color="grey-7" icon="preview" @click="viewSummary(row.id)">
|
<q-btn
|
||||||
<q-tooltip>{{ t('components.smartCard.openSummary') }}</q-tooltip>
|
flat
|
||||||
|
round
|
||||||
|
color="grey-7"
|
||||||
|
icon="preview"
|
||||||
|
@click="viewSummary(row.id)"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
{{ t('components.smartCard.openSummary') }}
|
||||||
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
@ -120,3 +191,10 @@ function viewSummary(id) {
|
||||||
</paginate>
|
</paginate>
|
||||||
</q-page>
|
</q-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search ticket: Buscar ticket
|
||||||
|
You can search by ticket id or alias: Puedes buscar por id o alias del ticket
|
||||||
|
Zone: Zona
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useState } from 'src/composables/useState';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
|
||||||
const state = useState();
|
const stateStore = useStateStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-drawer v-model="state.drawer.value" show-if-above :width="256" :breakpoint="500">
|
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
<q-scroll-area class="fit text-grey-8">
|
<q-scroll-area class="fit text-grey-8">
|
||||||
<LeftMenu />
|
<LeftMenu />
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default {
|
||||||
redirect: { name: 'ClaimMain' },
|
redirect: { name: 'ClaimMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['ClaimList', 'ClaimRmaList'],
|
main: ['ClaimList', 'ClaimRmaList'],
|
||||||
card: ['ClaimBasicData', 'ClaimRma'],
|
card: ['ClaimBasicData', 'ClaimRma', 'ClaimPhotos'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -76,6 +76,15 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Claim/Card/ClaimRma.vue'),
|
component: () => import('src/pages/Claim/Card/ClaimRma.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'ClaimPhotos',
|
||||||
|
path: 'photos',
|
||||||
|
meta: {
|
||||||
|
title: 'photos',
|
||||||
|
icon: 'image',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Claim/Card/ClaimPhoto.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -27,7 +27,7 @@ export default {
|
||||||
title: 'list',
|
title: 'list',
|
||||||
icon: 'view_list',
|
icon: 'view_list',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Customer/CustomerList.vue'),
|
component: () => import('src/pages/Customer/CustomerList.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'create',
|
path: 'create',
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export const useArrayDataStore = defineStore('arrayDataStore', () => {
|
||||||
|
const state = ref({});
|
||||||
|
|
||||||
|
function get(key) {
|
||||||
|
return state.value[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
function set(key) {
|
||||||
|
state.value[key] = {
|
||||||
|
filter: {},
|
||||||
|
userFilter: {},
|
||||||
|
userParams: {},
|
||||||
|
url: '',
|
||||||
|
limit: 10,
|
||||||
|
skip: 0,
|
||||||
|
order: '',
|
||||||
|
data: ref(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear(key) {
|
||||||
|
delete state.value[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
get,
|
||||||
|
set,
|
||||||
|
clear,
|
||||||
|
};
|
||||||
|
});
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export const useStateStore = defineStore('stateStore', () => {
|
||||||
|
const isMounted = ref(false);
|
||||||
|
const leftDrawer = ref(false);
|
||||||
|
const rightDrawer = ref(false);
|
||||||
|
|
||||||
|
function toggleLeftDrawer() {
|
||||||
|
leftDrawer.value = !leftDrawer.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleRightDrawer() {
|
||||||
|
rightDrawer.value = !rightDrawer.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMounted() {
|
||||||
|
isMounted.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isHeaderMounted() {
|
||||||
|
return isMounted.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLeftDrawerShown() {
|
||||||
|
return leftDrawer.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRightDrawerShown() {
|
||||||
|
return rightDrawer.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
leftDrawer,
|
||||||
|
rightDrawer,
|
||||||
|
setMounted,
|
||||||
|
isHeaderMounted,
|
||||||
|
toggleLeftDrawer,
|
||||||
|
toggleRightDrawer,
|
||||||
|
isLeftDrawerShown,
|
||||||
|
isRightDrawerShown,
|
||||||
|
};
|
||||||
|
});
|
Binary file not shown.
After Width: | Height: | Size: 429 KiB |
|
@ -0,0 +1,55 @@
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
describe('ClaimPhoto', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const claimId = 1;
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit(`/#/claim/${claimId}/photos`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add new file', () => {
|
||||||
|
cy.get('label > .q-btn').click();
|
||||||
|
cy.get('label > .q-btn input').selectFile('test/cypress/fixtures/image.jpg', {
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data saved');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add new file with drag and drop', () => {
|
||||||
|
cy.get('.container').selectFile('test/cypress/fixtures/image.jpg', {
|
||||||
|
action: 'drag-drop',
|
||||||
|
});
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data saved');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open first image dialog change to second and close', () => {
|
||||||
|
cy.get(
|
||||||
|
':nth-child(1) > .q-card > .q-img > .q-img__container > .q-img__image'
|
||||||
|
).click();
|
||||||
|
cy.get('.q-carousel__slide > .q-img > .q-img__container > .q-img__image').should(
|
||||||
|
'be.visible'
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get('.q-carousel__control > .q-btn > .q-btn__content > .q-icon').click();
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'.q-dialog__inner > .q-toolbar > .q-btn > .q-btn__content > .q-icon'
|
||||||
|
).click();
|
||||||
|
cy.get('.q-carousel__slide > .q-img > .q-img__container > .q-img__image').should(
|
||||||
|
'not.be.visible'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove third and fourth file', () => {
|
||||||
|
cy.get(
|
||||||
|
'.multimediaParent > :nth-child(3) > .q-btn > .q-btn__content > .q-icon'
|
||||||
|
).click();
|
||||||
|
cy.get('.q-btn--standard > .q-btn__content > .block').click();
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data deleted');
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'.multimediaParent > :nth-child(3) > .q-btn > .q-btn__content > .q-icon'
|
||||||
|
).click();
|
||||||
|
cy.get('.q-btn--standard > .q-btn__content > .block').click();
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data deleted');
|
||||||
|
});
|
||||||
|
});
|
|
@ -28,7 +28,7 @@
|
||||||
// Imports Quasar Cypress AE predefined commands
|
// Imports Quasar Cypress AE predefined commands
|
||||||
// import { registerCommands } from '@quasar/quasar-app-extension-testing-e2e-cypress';
|
// import { registerCommands } from '@quasar/quasar-app-extension-testing-e2e-cypress';
|
||||||
Cypress.Commands.add('login', (user) => {
|
Cypress.Commands.add('login', (user) => {
|
||||||
cy.visit('/#/login');
|
//cy.visit('/#/login');
|
||||||
cy.request({
|
cy.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/api/accounts/login',
|
url: '/api/accounts/login',
|
||||||
|
|
|
@ -4,17 +4,43 @@ import Paginate from 'components/PaginateData.vue';
|
||||||
|
|
||||||
describe('Paginate', () => {
|
describe('Paginate', () => {
|
||||||
const expectedUrl = '/api/customers';
|
const expectedUrl = '/api/customers';
|
||||||
|
|
||||||
let vm;
|
let vm;
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
const options = {
|
const options = {
|
||||||
attrs: {
|
attrs: {
|
||||||
url: expectedUrl,
|
url: expectedUrl,
|
||||||
sortBy: 'id DESC',
|
dataKey: 'CustomerList',
|
||||||
rowsPerPage: 3,
|
order: 'id DESC',
|
||||||
|
limit: 3,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
vm = createWrapper(Paginate, options).vm;
|
vm = createWrapper(Paginate, options).vm;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vm.store.data = [];
|
||||||
|
vm.store.skip = 0;
|
||||||
|
vm.pagination.page = 1;
|
||||||
|
vm.hasMoreData = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('paginate()', () => {
|
||||||
|
it('should call to the paginate() method and set the data on the rows property', async () => {
|
||||||
|
vi.spyOn(vm.arrayData, 'loadMore');
|
||||||
|
vm.store.data = [
|
||||||
|
{ id: 1, name: 'Tony Stark' },
|
||||||
|
{ id: 2, name: 'Jessica Jones' },
|
||||||
|
{ id: 3, name: 'Bruce Wayne' },
|
||||||
|
];
|
||||||
|
|
||||||
|
await vm.paginate();
|
||||||
|
|
||||||
|
expect(vm.arrayData.loadMore).toHaveBeenCalledWith();
|
||||||
|
expect(vm.store.data.length).toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call to the paginate() method and then call it again to paginate', async () => {
|
||||||
vi.spyOn(axios, 'get').mockResolvedValue({
|
vi.spyOn(axios, 'get').mockResolvedValue({
|
||||||
data: [
|
data: [
|
||||||
{ id: 1, name: 'Tony Stark' },
|
{ id: 1, name: 'Tony Stark' },
|
||||||
|
@ -22,64 +48,22 @@ describe('Paginate', () => {
|
||||||
{ id: 3, name: 'Bruce Wayne' },
|
{ id: 3, name: 'Bruce Wayne' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
vm.arrayData.hasMoreData.value = true;
|
||||||
|
vm.store.data = [
|
||||||
afterEach(() => {
|
{ id: 1, name: 'Tony Stark' },
|
||||||
vm.rows = [];
|
{ id: 2, name: 'Jessica Jones' },
|
||||||
vm.pagination.page = 1;
|
{ id: 3, name: 'Bruce Wayne' },
|
||||||
vm.hasMoreData = true;
|
];
|
||||||
});
|
|
||||||
|
|
||||||
describe('paginate()', () => {
|
|
||||||
it('should call to the paginate() method and set the data on the rows property', async () => {
|
|
||||||
const expectedOptions = {
|
|
||||||
params: {
|
|
||||||
filter: {
|
|
||||||
order: 'id DESC',
|
|
||||||
limit: 3,
|
|
||||||
skip: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
await vm.paginate();
|
await vm.paginate();
|
||||||
|
|
||||||
expect(axios.get).toHaveBeenCalledWith(expectedUrl, expectedOptions);
|
expect(vm.store.skip).toEqual(3);
|
||||||
expect(vm.rows.length).toEqual(3);
|
expect(vm.store.data.length).toEqual(6);
|
||||||
});
|
|
||||||
|
|
||||||
it('should call to the paginate() method and then call it again to paginate', async () => {
|
|
||||||
const expectedOptions = {
|
|
||||||
params: {
|
|
||||||
filter: {
|
|
||||||
order: 'id DESC',
|
|
||||||
limit: 3,
|
|
||||||
skip: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
await vm.paginate();
|
await vm.paginate();
|
||||||
|
|
||||||
expect(axios.get).toHaveBeenCalledWith(expectedUrl, expectedOptions);
|
expect(vm.store.skip).toEqual(6);
|
||||||
expect(vm.rows.length).toEqual(3);
|
expect(vm.store.data.length).toEqual(9);
|
||||||
|
|
||||||
const expectedOptionsPaginated = {
|
|
||||||
params: {
|
|
||||||
filter: {
|
|
||||||
order: 'id DESC',
|
|
||||||
limit: 3,
|
|
||||||
skip: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.pagination.page = 2;
|
|
||||||
|
|
||||||
await vm.paginate();
|
|
||||||
|
|
||||||
expect(axios.get).toHaveBeenCalledWith(expectedUrl, expectedOptionsPaginated);
|
|
||||||
expect(vm.rows.length).toEqual(6);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -95,16 +79,15 @@ describe('Paginate', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should increment the pagination and then call to the done() callback', async () => {
|
it('should increment the pagination and then call to the done() callback', async () => {
|
||||||
vm.rows = [
|
|
||||||
{ id: 1, name: 'Tony Stark' },
|
|
||||||
{ id: 2, name: 'Jessica Jones' },
|
|
||||||
{ id: 3, name: 'Bruce Wayne' },
|
|
||||||
];
|
|
||||||
|
|
||||||
expect(vm.pagination.page).toEqual(1);
|
expect(vm.pagination.page).toEqual(1);
|
||||||
|
|
||||||
const index = 1;
|
const index = 1;
|
||||||
const done = vi.fn();
|
const done = vi.fn();
|
||||||
|
vm.store.data = [
|
||||||
|
{ id: 1, name: 'Tony Stark' },
|
||||||
|
{ id: 2, name: 'Jessica Jones' },
|
||||||
|
{ id: 3, name: 'Bruce Wayne' },
|
||||||
|
];
|
||||||
|
|
||||||
await vm.onLoad(index, done);
|
await vm.onLoad(index, done);
|
||||||
|
|
||||||
|
@ -120,7 +103,7 @@ describe('Paginate', () => {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
vm.rows = [
|
vm.store.data = [
|
||||||
{ id: 1, name: 'Tony Stark' },
|
{ id: 1, name: 'Tony Stark' },
|
||||||
{ id: 2, name: 'Jessica Jones' },
|
{ id: 2, name: 'Jessica Jones' },
|
||||||
{ id: 3, name: 'Bruce Wayne' },
|
{ id: 3, name: 'Bruce Wayne' },
|
||||||
|
|
|
@ -25,7 +25,9 @@ describe('ClaimDescriptorMenu', () => {
|
||||||
|
|
||||||
await vm.deleteClaim();
|
await vm.deleteClaim();
|
||||||
|
|
||||||
expect(vm.quasar.notify).toHaveBeenCalledWith(expect.objectContaining({ type: 'positive' }));
|
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ type: 'positive' })
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
|
||||||
|
import { createWrapper, axios } from 'app/test/vitest/helper';
|
||||||
|
import ClaimPhoto from 'pages/Claim/Card/ClaimPhoto.vue';
|
||||||
|
|
||||||
|
describe('ClaimPhoto', () => {
|
||||||
|
let vm;
|
||||||
|
|
||||||
|
const claimMock = {
|
||||||
|
claimDms: [
|
||||||
|
{
|
||||||
|
dmsFk: 1,
|
||||||
|
dms: {
|
||||||
|
contentType: 'contentType',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
client: {
|
||||||
|
id: '1',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
beforeAll(() => {
|
||||||
|
vm = createWrapper(ClaimPhoto, {
|
||||||
|
global: {
|
||||||
|
stubs: ['FetchData', 'TeleportSlot', 'vue-i18n'],
|
||||||
|
mocks: {
|
||||||
|
fetch: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).vm;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deleteDms()', () => {
|
||||||
|
it('should delete dms and call quasar notify', async () => {
|
||||||
|
vi.spyOn(axios, 'post').mockResolvedValue({ data: true });
|
||||||
|
vi.spyOn(vm.quasar, 'notify');
|
||||||
|
|
||||||
|
await vm.deleteDms(0);
|
||||||
|
|
||||||
|
expect(axios.post).toHaveBeenCalledWith(
|
||||||
|
`ClaimDms/${claimMock.claimDms[0].dmsFk}/removeFile`
|
||||||
|
);
|
||||||
|
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ type: 'positive' })
|
||||||
|
);
|
||||||
|
expect(vm.claimDms).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('viewDeleteDms()', () => {
|
||||||
|
it('should call quasar dialog', async () => {
|
||||||
|
vi.spyOn(vm.quasar, 'dialog');
|
||||||
|
|
||||||
|
await vm.viewDeleteDms(1);
|
||||||
|
|
||||||
|
expect(vm.quasar.dialog).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
componentProps: {
|
||||||
|
message: 'This file will be deleted',
|
||||||
|
icon: 'delete',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setClaimDms()', () => {
|
||||||
|
it('should assign claimDms and client from data', async () => {
|
||||||
|
await vm.setClaimDms(claimMock);
|
||||||
|
|
||||||
|
expect(vm.claimDms).toEqual([
|
||||||
|
{
|
||||||
|
dmsFk: 1,
|
||||||
|
dms: {
|
||||||
|
contentType: 'contentType',
|
||||||
|
},
|
||||||
|
isVideo: false,
|
||||||
|
url: '/api/Claims/1/downloadFile?access_token=',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(vm.client).toEqual(claimMock.client);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('create()', () => {
|
||||||
|
it('should upload file and call quasar notify', async () => {
|
||||||
|
const files = [{ name: 'firstFile' }];
|
||||||
|
|
||||||
|
vi.spyOn(axios, 'post').mockResolvedValue({ data: true });
|
||||||
|
vi.spyOn(vm.quasar, 'notify');
|
||||||
|
vi.spyOn(vm.claimDmsRef, 'fetch');
|
||||||
|
|
||||||
|
await vm.create(files);
|
||||||
|
|
||||||
|
expect(axios.post).toHaveBeenCalledWith(
|
||||||
|
'claims/1/uploadFile',
|
||||||
|
new FormData(),
|
||||||
|
expect.objectContaining({
|
||||||
|
params: expect.objectContaining({ hasFile: false }),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ type: 'positive' })
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(vm.claimDmsRef.fetch).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,4 +1,4 @@
|
||||||
import { installQuasar } from '@quasar/quasar-app-extension-testing-unit-vitest';
|
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-unit-vitest';
|
||||||
import { mount, flushPromises } from '@vue/test-utils';
|
import { mount, flushPromises } from '@vue/test-utils';
|
||||||
import { createTestingPinia } from '@pinia/testing';
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
|
@ -6,27 +6,60 @@ import { i18n } from 'src/boot/i18n';
|
||||||
import { Notify, Dialog } from 'quasar';
|
import { Notify, Dialog } from 'quasar';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
installQuasar({
|
installQuasarPlugin({
|
||||||
plugins: {
|
plugins: {
|
||||||
Notify,
|
Notify,
|
||||||
Dialog,
|
Dialog,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const pinia = createTestingPinia({ createSpy: vi.fn, stubActions: false });
|
||||||
const mockPush = vi.fn();
|
const mockPush = vi.fn();
|
||||||
|
|
||||||
vi.mock('vue-router', () => ({
|
vi.mock('vue-router', () => ({
|
||||||
useRouter: () => ({
|
useRouter: () => ({
|
||||||
push: mockPush,
|
push: mockPush,
|
||||||
currentRoute: { value: 'myCurrentRoute' },
|
currentRoute: {
|
||||||
|
value: {
|
||||||
|
params: {
|
||||||
|
id: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
useRoute: () => ({
|
useRoute: () => ({
|
||||||
matched: [],
|
matched: [],
|
||||||
|
query: {},
|
||||||
|
params: {},
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export function createWrapper(component, options) {
|
class FormDataMock {
|
||||||
const pinia = createTestingPinia({ createSpy: vi.fn, stubActions: false });
|
append() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
delete() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
get() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
getAll() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
has() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
set() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
forEach() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
global.FormData = FormDataMock;
|
||||||
|
|
||||||
|
export function createWrapper(component, options) {
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
global: {
|
global: {
|
||||||
plugins: [i18n, pinia],
|
plugins: [i18n, pinia],
|
||||||
|
|
Loading…
Reference in New Issue