Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix-front into test
gitea/salix-front/pipeline/head This commit looks good
Details
gitea/salix-front/pipeline/head This commit looks good
Details
This commit is contained in:
commit
ab113ea908
|
@ -8,10 +8,10 @@
|
|||
"name": "salix-front",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.15.8",
|
||||
"@quasar/extras": "^1.15.11",
|
||||
"axios": "^1.2.1",
|
||||
"pinia": "^2.0.28",
|
||||
"quasar": "^2.11.1",
|
||||
"quasar": "^2.11.7",
|
||||
"validator": "^13.7.0",
|
||||
"vue": "^3.2.45",
|
||||
"vue-i18n": "^9.2.2",
|
||||
|
@ -21,8 +21,8 @@
|
|||
"devDependencies": {
|
||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
||||
"@pinia/testing": "^0.0.14",
|
||||
"@quasar/app-vite": "^1.1.3",
|
||||
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.1.2",
|
||||
"@quasar/app-vite": "^1.2.1",
|
||||
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.2.1",
|
||||
"@vue/test-utils": "^2.0.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"cypress": "^12.2.0",
|
||||
|
@ -770,33 +770,33 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@quasar/app-vite": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/app-vite/-/app-vite-1.1.3.tgz",
|
||||
"integrity": "sha512-YX6lVkjRFNDbYcORiMtNlDz3jlSf7ldF4zGZk8zaW/Q1CfjaLqpSqCmBP4eta6QXz7To0IdabROYKa55D6IDgA==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/app-vite/-/app-vite-1.2.1.tgz",
|
||||
"integrity": "sha512-iKvpucrnt5pQuuQxqkLLWuI7Wm9Xx9fcBK1miMHWBgdcbHOXhc/OrlxeLvRwxm7GMd91cp3nhslo5j3a+zMCOg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@quasar/fastclick": "1.1.5",
|
||||
"@quasar/vite-plugin": "^1.2.1",
|
||||
"@quasar/vite-plugin": "^1.3.0",
|
||||
"@rollup/pluginutils": "^4.1.2",
|
||||
"@types/chrome": "^0.0.191",
|
||||
"@types/chrome": "^0.0.208",
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/cordova": "0.0.34",
|
||||
"@types/express": "^4.17.13",
|
||||
"@vitejs/plugin-vue": "^2.2.0",
|
||||
"archiver": "^5.3.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"ci-info": "^3.3.0",
|
||||
"ci-info": "^3.7.1",
|
||||
"compression": "^1.7.4",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"dot-prop": "6.0.1",
|
||||
"elementtree": "0.1.7",
|
||||
"esbuild": "0.14.51",
|
||||
"express": "^4.17.3",
|
||||
"fast-glob": "3.2.11",
|
||||
"fs-extra": "^10.0.1",
|
||||
"fast-glob": "3.2.12",
|
||||
"fs-extra": "^11.1.0",
|
||||
"html-minifier": "^4.0.0",
|
||||
"inquirer": "^8.2.1",
|
||||
"isbinaryfile": "^4.0.8",
|
||||
"isbinaryfile": "^5.0.0",
|
||||
"kolorist": "^1.5.1",
|
||||
"lodash": "^4.17.21",
|
||||
"minimist": "^1.2.6",
|
||||
|
@ -815,7 +815,7 @@
|
|||
"quasar": "bin/quasar"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || ^16 || ^14.19",
|
||||
"node": "^24 || ^22 || ^20 || ^18 || ^16 || ^14.19",
|
||||
"npm": ">= 6.14.12",
|
||||
"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": {
|
||||
"version": "1.15.8",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/extras/-/extras-1.15.8.tgz",
|
||||
"integrity": "sha512-UR6Snu7DSYdDOGqcNjUr0FJtKNfPn2Jc2hKTC+y/Y7Gf+vWu0RYUl49cguD33nn+wpbgs28+cvmjx7u3NNogoQ==",
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/extras/-/extras-1.15.11.tgz",
|
||||
"integrity": "sha512-EEXL10EJQmL9jNs5fp0Kd/nyonG8hTODolj+qpYNPG3qEEoy3txr05Pdmp9qJYaXIzP9rOS9FGhFe9Mnq6MBBg==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://donate.quasar.dev"
|
||||
|
@ -891,12 +875,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@quasar/quasar-app-extension-testing-unit-vitest": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/quasar-app-extension-testing-unit-vitest/-/quasar-app-extension-testing-unit-vitest-0.1.2.tgz",
|
||||
"integrity": "sha512-yqpXu0J8Gttr00UCzEAMptZX8aggqsiG14cgeKLt0Hm3pU9PtwXqjX/EYifgQyldw0b7LbAzpdHhsXU2BU9vpA==",
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/quasar-app-extension-testing-unit-vitest/-/quasar-app-extension-testing-unit-vitest-0.2.1.tgz",
|
||||
"integrity": "sha512-jR7KgZfujDdfhZe0dcgiA+W2bfm8/NcEbpYuEyr1161Ckn+dL6Y9KZUDEZMUuRYzS+pn1SXsRPMcv8tgXToB4Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"happy-dom": "^5.0.0",
|
||||
"happy-dom": "^8.9.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"vite-jsconfig-paths": "^2.0.1",
|
||||
"vite-tsconfig-paths": "^3.5.0"
|
||||
|
@ -907,10 +891,10 @@
|
|||
"yarn": ">= 1.17.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/ui": "^0.15.0",
|
||||
"@vitest/ui": "^0.29.1",
|
||||
"@vue/test-utils": "^2.0.0",
|
||||
"quasar": "^2.0.0",
|
||||
"vitest": "^0.15.0",
|
||||
"quasar": "^2.10.2",
|
||||
"vitest": "^0.29.1",
|
||||
"vue": "^3.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
|
@ -977,9 +961,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/chrome": {
|
||||
"version": "0.0.191",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.191.tgz",
|
||||
"integrity": "sha512-hXYHJJ1Y265xKCw0o2Kz4CnR8aUhOMdyxK1AinET4EDr3fhpEMvOFDwdqz9LUX4syfTVYWb8w7vfC12s112ehg==",
|
||||
"version": "0.0.208",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.208.tgz",
|
||||
"integrity": "sha512-VDU/JnXkF5qaI7WBz14Azpa2VseZTgML0ia/g/B1sr9OfdOnHiH/zZ7P7qCDqxSlkqJh76/bPc8jLFcx8rHJmw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/filesystem": "*",
|
||||
|
@ -995,15 +979,6 @@
|
|||
"@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": {
|
||||
"version": "3.4.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
||||
|
@ -1057,15 +1032,6 @@
|
|||
"integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==",
|
||||
"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": {
|
||||
"version": "1.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.10.tgz",
|
||||
|
@ -1509,12 +1475,6 @@
|
|||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||
"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": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
|
@ -1997,10 +1957,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ci-info": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz",
|
||||
"integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==",
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
|
||||
"integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/sibiraj-s"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
|
@ -2257,45 +2223,6 @@
|
|||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"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": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
|
@ -3823,9 +3750,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz",
|
||||
"integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
|
@ -3833,7 +3760,7 @@
|
|||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=14.14"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
|
@ -3894,15 +3821,6 @@
|
|||
"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": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
|
@ -4023,20 +3941,32 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/happy-dom": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-5.4.0.tgz",
|
||||
"integrity": "sha512-JxXpBvEUdyCqfRHzHJKtiJ+6+WzTIL6kFCteAOEy13QEnHMD/D5uUIVVw3a4TmQroJriz0gnll4Uv1qZeSz/rA==",
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-8.9.0.tgz",
|
||||
"integrity": "sha512-JZwJuGdR7ko8L61136YzmrLv7LgTh5b8XaEM3P709mLjyQuXJ3zHTDXvUtBBahRjGlcYW0zGjIiEWizoTUGKfA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"css.escape": "^1.5.1",
|
||||
"he": "^1.2.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"node-fetch": "^2.x.x",
|
||||
"sync-request": "^6.1.0",
|
||||
"webidl-conversions": "^7.0.0",
|
||||
"whatwg-encoding": "^2.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": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
|
@ -4100,21 +4030,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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
|
@ -4131,21 +4046,6 @@
|
|||
"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": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
|
||||
|
@ -4498,12 +4398,12 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/isbinaryfile": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
|
||||
"integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.0.tgz",
|
||||
"integrity": "sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
"node": ">= 14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/gjtorikian/"
|
||||
|
@ -5469,12 +5369,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": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
|
@ -5736,15 +5630,6 @@
|
|||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"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": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
|
@ -5804,9 +5689,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/quasar": {
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.11.1.tgz",
|
||||
"integrity": "sha512-3whiJaOXbectd2R6Vfjr/vHKeY92zdDpx41zfZ5vrG+Yx16Q76euUQsdKlYr1PD7q33yvHvjegxuILgKJg5GqA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.11.7.tgz",
|
||||
"integrity": "sha512-OzDwYfoSwZbwqfNWz9Bfn1mhGE8YMxpTLhPvVkQU87ePF6qFj4aWttcTUifXITKldOAZziN1Mmv8VLQyITHwiw==",
|
||||
"engines": {
|
||||
"node": ">= 10.18.1",
|
||||
"npm": ">= 6.13.4",
|
||||
|
@ -6608,29 +6493,6 @@
|
|||
"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": {
|
||||
"version": "6.8.1",
|
||||
"resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
|
||||
|
@ -6691,48 +6553,6 @@
|
|||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||
"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": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||
|
@ -6930,12 +6750,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": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz",
|
||||
|
@ -8076,33 +7890,33 @@
|
|||
"dev": true
|
||||
},
|
||||
"@quasar/app-vite": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/app-vite/-/app-vite-1.1.3.tgz",
|
||||
"integrity": "sha512-YX6lVkjRFNDbYcORiMtNlDz3jlSf7ldF4zGZk8zaW/Q1CfjaLqpSqCmBP4eta6QXz7To0IdabROYKa55D6IDgA==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/app-vite/-/app-vite-1.2.1.tgz",
|
||||
"integrity": "sha512-iKvpucrnt5pQuuQxqkLLWuI7Wm9Xx9fcBK1miMHWBgdcbHOXhc/OrlxeLvRwxm7GMd91cp3nhslo5j3a+zMCOg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@quasar/fastclick": "1.1.5",
|
||||
"@quasar/vite-plugin": "^1.2.1",
|
||||
"@quasar/vite-plugin": "^1.3.0",
|
||||
"@rollup/pluginutils": "^4.1.2",
|
||||
"@types/chrome": "^0.0.191",
|
||||
"@types/chrome": "^0.0.208",
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/cordova": "0.0.34",
|
||||
"@types/express": "^4.17.13",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"archiver": "^5.3.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"ci-info": "^3.3.0",
|
||||
"ci-info": "^3.7.1",
|
||||
"compression": "^1.7.4",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"dot-prop": "6.0.1",
|
||||
"elementtree": "0.1.7",
|
||||
"esbuild": "0.14.51",
|
||||
"express": "^4.17.3",
|
||||
"fast-glob": "3.2.11",
|
||||
"fs-extra": "^10.0.1",
|
||||
"fast-glob": "3.2.12",
|
||||
"fs-extra": "^11.1.0",
|
||||
"html-minifier": "^4.0.0",
|
||||
"inquirer": "^8.2.1",
|
||||
"isbinaryfile": "^4.0.8",
|
||||
"isbinaryfile": "^5.0.0",
|
||||
"kolorist": "^1.5.1",
|
||||
"lodash": "^4.17.21",
|
||||
"minimist": "^1.2.6",
|
||||
|
@ -8116,27 +7930,12 @@
|
|||
"table": "^6.8.0",
|
||||
"vite": "^4.0.3",
|
||||
"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": {
|
||||
"version": "1.15.8",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/extras/-/extras-1.15.8.tgz",
|
||||
"integrity": "sha512-UR6Snu7DSYdDOGqcNjUr0FJtKNfPn2Jc2hKTC+y/Y7Gf+vWu0RYUl49cguD33nn+wpbgs28+cvmjx7u3NNogoQ=="
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/extras/-/extras-1.15.11.tgz",
|
||||
"integrity": "sha512-EEXL10EJQmL9jNs5fp0Kd/nyonG8hTODolj+qpYNPG3qEEoy3txr05Pdmp9qJYaXIzP9rOS9FGhFe9Mnq6MBBg=="
|
||||
},
|
||||
"@quasar/fastclick": {
|
||||
"version": "1.1.5",
|
||||
|
@ -8145,12 +7944,12 @@
|
|||
"dev": true
|
||||
},
|
||||
"@quasar/quasar-app-extension-testing-unit-vitest": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/quasar-app-extension-testing-unit-vitest/-/quasar-app-extension-testing-unit-vitest-0.1.2.tgz",
|
||||
"integrity": "sha512-yqpXu0J8Gttr00UCzEAMptZX8aggqsiG14cgeKLt0Hm3pU9PtwXqjX/EYifgQyldw0b7LbAzpdHhsXU2BU9vpA==",
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@quasar/quasar-app-extension-testing-unit-vitest/-/quasar-app-extension-testing-unit-vitest-0.2.1.tgz",
|
||||
"integrity": "sha512-jR7KgZfujDdfhZe0dcgiA+W2bfm8/NcEbpYuEyr1161Ckn+dL6Y9KZUDEZMUuRYzS+pn1SXsRPMcv8tgXToB4Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"happy-dom": "^5.0.0",
|
||||
"happy-dom": "^8.9.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"vite-jsconfig-paths": "^2.0.1",
|
||||
"vite-tsconfig-paths": "^3.5.0"
|
||||
|
@ -8199,9 +7998,9 @@
|
|||
}
|
||||
},
|
||||
"@types/chrome": {
|
||||
"version": "0.0.191",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.191.tgz",
|
||||
"integrity": "sha512-hXYHJJ1Y265xKCw0o2Kz4CnR8aUhOMdyxK1AinET4EDr3fhpEMvOFDwdqz9LUX4syfTVYWb8w7vfC12s112ehg==",
|
||||
"version": "0.0.208",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.208.tgz",
|
||||
"integrity": "sha512-VDU/JnXkF5qaI7WBz14Azpa2VseZTgML0ia/g/B1sr9OfdOnHiH/zZ7P7qCDqxSlkqJh76/bPc8jLFcx8rHJmw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/filesystem": "*",
|
||||
|
@ -8217,15 +8016,6 @@
|
|||
"@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": {
|
||||
"version": "3.4.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
||||
|
@ -8279,15 +8069,6 @@
|
|||
"integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==",
|
||||
"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": {
|
||||
"version": "1.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.10.tgz",
|
||||
|
@ -8656,12 +8437,6 @@
|
|||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||
"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": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
|
@ -9004,9 +8779,9 @@
|
|||
}
|
||||
},
|
||||
"ci-info": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz",
|
||||
"integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==",
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
|
||||
"integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
|
||||
"dev": true
|
||||
},
|
||||
"clean-css": {
|
||||
|
@ -9206,44 +8981,6 @@
|
|||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"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": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
|
@ -10302,9 +10039,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz",
|
||||
"integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
|
@ -10354,12 +10091,6 @@
|
|||
"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": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
|
@ -10453,18 +10184,29 @@
|
|||
"dev": true
|
||||
},
|
||||
"happy-dom": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-5.4.0.tgz",
|
||||
"integrity": "sha512-JxXpBvEUdyCqfRHzHJKtiJ+6+WzTIL6kFCteAOEy13QEnHMD/D5uUIVVw3a4TmQroJriz0gnll4Uv1qZeSz/rA==",
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-8.9.0.tgz",
|
||||
"integrity": "sha512-JZwJuGdR7ko8L61136YzmrLv7LgTh5b8XaEM3P709mLjyQuXJ3zHTDXvUtBBahRjGlcYW0zGjIiEWizoTUGKfA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"css.escape": "^1.5.1",
|
||||
"he": "^1.2.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"node-fetch": "^2.x.x",
|
||||
"sync-request": "^6.1.0",
|
||||
"webidl-conversions": "^7.0.0",
|
||||
"whatwg-encoding": "^2.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": {
|
||||
|
@ -10509,18 +10251,6 @@
|
|||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
|
@ -10534,23 +10264,6 @@
|
|||
"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": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
|
||||
|
@ -10793,9 +10506,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"isbinaryfile": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
|
||||
"integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.0.tgz",
|
||||
"integrity": "sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg==",
|
||||
"dev": true
|
||||
},
|
||||
"isexe": {
|
||||
|
@ -11540,12 +11253,6 @@
|
|||
"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": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
|
@ -11715,15 +11422,6 @@
|
|||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"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": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
|
@ -11771,9 +11469,9 @@
|
|||
}
|
||||
},
|
||||
"quasar": {
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.11.1.tgz",
|
||||
"integrity": "sha512-3whiJaOXbectd2R6Vfjr/vHKeY92zdDpx41zfZ5vrG+Yx16Q76euUQsdKlYr1PD7q33yvHvjegxuILgKJg5GqA=="
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.11.7.tgz",
|
||||
"integrity": "sha512-OzDwYfoSwZbwqfNWz9Bfn1mhGE8YMxpTLhPvVkQU87ePF6qFj4aWttcTUifXITKldOAZziN1Mmv8VLQyITHwiw=="
|
||||
},
|
||||
"queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
|
@ -12355,26 +12053,6 @@
|
|||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"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": {
|
||||
"version": "6.8.1",
|
||||
"resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
|
||||
|
@ -12427,44 +12105,6 @@
|
|||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||
"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": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||
|
@ -12623,12 +12263,6 @@
|
|||
"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": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz",
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
"test:unit:ci": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.15.8",
|
||||
"@quasar/extras": "^1.15.11",
|
||||
"axios": "^1.2.1",
|
||||
"pinia": "^2.0.28",
|
||||
"quasar": "^2.11.1",
|
||||
"quasar": "^2.11.7",
|
||||
"validator": "^13.7.0",
|
||||
"vue": "^3.2.45",
|
||||
"vue-i18n": "^9.2.2",
|
||||
|
@ -28,8 +28,8 @@
|
|||
"devDependencies": {
|
||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
||||
"@pinia/testing": "^0.0.14",
|
||||
"@quasar/app-vite": "^1.1.3",
|
||||
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.1.2",
|
||||
"@quasar/app-vite": "^1.2.1",
|
||||
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.2.1",
|
||||
"@vue/test-utils": "^2.0.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"cypress": "^12.2.0",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
|
||||
|
||||
const { configure } = require('quasar/wrappers');
|
||||
const VueI18nPlugin = require('@intlify/unplugin-vue-i18n/vite')
|
||||
const VueI18nPlugin = require('@intlify/unplugin-vue-i18n/vite');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = configure(function (/* ctx */) {
|
||||
|
@ -140,6 +140,8 @@ module.exports = configure(function (/* ctx */) {
|
|||
|
||||
// Quasar plugins
|
||||
plugins: ['Notify', 'Dialog'],
|
||||
//all: 'auto',
|
||||
//autoImportComponentCase: 'pascal',
|
||||
},
|
||||
|
||||
// animations: 'all', // --- includes all animations
|
||||
|
|
|
@ -44,6 +44,11 @@ function responseError(error) {
|
|||
let message = error.message;
|
||||
let logOut = false;
|
||||
|
||||
const response = error.response;
|
||||
if (response && response.data.error) {
|
||||
message = response.data.error.message;
|
||||
}
|
||||
|
||||
switch (error.response?.status) {
|
||||
case 401:
|
||||
message = 'login.loginError';
|
||||
|
|
|
@ -21,7 +21,14 @@ onMounted(() => stateStore.setMounted());
|
|||
<template>
|
||||
<q-header class="bg-dark" color="white" elevated>
|
||||
<q-toolbar class="q-py-sm q-px-md">
|
||||
<q-btn flat @click="stateStore.toggleLeftDrawer()" round dense icon="menu">
|
||||
<q-btn
|
||||
@click="stateStore.toggleLeftDrawer()"
|
||||
icon="menu"
|
||||
class="q-mr-sm"
|
||||
round
|
||||
dense
|
||||
flat
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
|
@ -48,24 +55,13 @@ onMounted(() => stateStore.setMounted());
|
|||
<div id="searchbar"></div>
|
||||
<q-space></q-space>
|
||||
<div class="q-pl-sm q-gutter-sm row items-center no-wrap">
|
||||
<div id="header-actions"></div>
|
||||
<div id="actions-prepend"></div>
|
||||
<q-btn id="pinnedModules" icon="apps" flat dense rounded>
|
||||
<q-tooltip bottom>
|
||||
{{ t('globals.pinnedModules') }}
|
||||
</q-tooltip>
|
||||
<PinnedModules />
|
||||
</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-avatar size="lg">
|
||||
<q-img
|
||||
|
@ -79,6 +75,7 @@ onMounted(() => stateStore.setMounted());
|
|||
</q-tooltip>
|
||||
<UserPanel />
|
||||
</q-btn>
|
||||
<div id="actions-append"></div>
|
||||
</div>
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
|
|
|
@ -86,6 +86,7 @@ async function paginate() {
|
|||
|
||||
if (!props.url) return;
|
||||
|
||||
isLoading.value = true;
|
||||
await arrayData.loadMore();
|
||||
|
||||
if (!arrayData.hasMoreData.value) {
|
||||
|
@ -121,7 +122,7 @@ async function onLoad(...params) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="column items-center">
|
||||
<div>
|
||||
<div
|
||||
v-if="store.data && store.data.length === 0 && !isLoading"
|
||||
class="info-row q-pa-md text-center"
|
||||
|
@ -150,27 +151,18 @@ async function onLoad(...params) {
|
|||
</q-card>
|
||||
</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">
|
||||
<q-infinite-scroll v-if="store.data" @load="onLoad" :offset="offset">
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-list {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
|
||||
// .q-infinite-scroll {
|
||||
// width: 100%;
|
||||
// }
|
||||
.info-row {
|
||||
width: 100%;
|
||||
|
||||
|
|
|
@ -3,12 +3,13 @@ import { ref } from 'vue';
|
|||
import { useDialogPluginComponent } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const $props = defineProps({
|
||||
address: {
|
||||
type: String,
|
||||
default: '',
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
requied: true,
|
||||
default: null,
|
||||
},
|
||||
send: {
|
||||
promise: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
|
@ -19,24 +20,30 @@ defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
|||
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
||||
const { t } = useI18n();
|
||||
|
||||
const address = ref($props.address);
|
||||
const address = ref(props.data.address);
|
||||
const isLoading = ref(false);
|
||||
|
||||
async function confirm() {
|
||||
isLoading.value = true;
|
||||
await $props.send(address.value);
|
||||
isLoading.value = false;
|
||||
const response = { address };
|
||||
|
||||
onDialogOK();
|
||||
if (props.promise) {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
Object.assign(response, props.data);
|
||||
await props.promise(response);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onDialogOK(response);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<q-dialog ref="dialogRef" persistent>
|
||||
<q-card class="q-pa-sm">
|
||||
<q-card-section class="row items-center q-pb-none">
|
||||
<span class="text-h6 text-grey">{{
|
||||
t('Send email notification: Send email notification')
|
||||
}}</span>
|
||||
<span class="text-h6 text-grey">{{ t('Send email notification') }}</span>
|
||||
<q-space />
|
||||
<q-btn icon="close" flat round dense v-close-popup />
|
||||
</q-card-section>
|
||||
|
@ -53,6 +60,7 @@ async function confirm() {
|
|||
color="primary"
|
||||
:loading="isLoading"
|
||||
@click="confirm"
|
||||
unelevated
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
|
@ -67,6 +75,6 @@ async function confirm() {
|
|||
|
||||
<i18n>
|
||||
es:
|
||||
Send email notification: Enviar notificación por correo,
|
||||
Send email notification: Enviar notificación por correo
|
||||
The notification will be sent to the following address: La notificación se enviará a la siguiente dirección
|
||||
</i18n>
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useDialogPluginComponent } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
||||
const { t, availableLocales } = useI18n();
|
||||
|
||||
defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
||||
const props = defineProps({
|
||||
subject: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'Verdnatura',
|
||||
},
|
||||
phone: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
template: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
locale: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'es',
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
promise: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const maxLength = 160;
|
||||
const locale = ref(props.locale);
|
||||
const subject = ref(props.subject);
|
||||
const phone = ref(props.phone);
|
||||
const message = ref('');
|
||||
|
||||
updateMessage();
|
||||
|
||||
function updateMessage() {
|
||||
const params = props.data;
|
||||
const key = `templates['${props.template}']`;
|
||||
|
||||
message.value = t(key, params, { locale: locale.value });
|
||||
}
|
||||
|
||||
const totalLength = computed(() => message.value.length);
|
||||
const color = computed(() => {
|
||||
if (totalLength.value == maxLength) return 'negative';
|
||||
if ((totalLength.value / maxLength) * 100 > 90) return 'warning';
|
||||
return 'positive';
|
||||
});
|
||||
|
||||
const languages = availableLocales.map((locale) => ({ label: t(locale), value: locale }));
|
||||
|
||||
const isLoading = ref(false);
|
||||
async function send() {
|
||||
const response = {
|
||||
destination: phone.value,
|
||||
message: message.value,
|
||||
};
|
||||
if (props.promise) {
|
||||
isLoading.value = true;
|
||||
|
||||
try {
|
||||
Object.assign(response, props.data);
|
||||
await props.promise(response);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onDialogOK(response);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog ref="dialogRef" persistent>
|
||||
<q-card class="q-pa-sm">
|
||||
<q-card-section class="row items-center q-pb-none">
|
||||
<span class="text-h6 text-grey">
|
||||
{{ t('Send SMS') }}
|
||||
</span>
|
||||
<q-space />
|
||||
<q-btn icon="close" :disable="isLoading" flat round dense v-close-popup />
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-banner class="bg-amber text-white" rounded dense>
|
||||
<template #avatar>
|
||||
<q-icon name="warning" />
|
||||
</template>
|
||||
<span
|
||||
v-html="t('CustomerDefaultLanguage', { locale: t(props.locale) })"
|
||||
></span>
|
||||
</q-banner>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pb-xs">
|
||||
<q-select
|
||||
:label="t('Language')"
|
||||
:options="languages"
|
||||
v-model="locale"
|
||||
@update:model-value="updateMessage()"
|
||||
emit-value
|
||||
map-options
|
||||
:input-debounce="0"
|
||||
rounded
|
||||
outlined
|
||||
dense
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pb-xs">
|
||||
<q-input
|
||||
:label="t('Phone')"
|
||||
v-model="phone"
|
||||
rounded
|
||||
outlined
|
||||
autofocus
|
||||
dense
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pb-xs">
|
||||
<q-input
|
||||
:label="t('Subject')"
|
||||
v-model="subject"
|
||||
rounded
|
||||
outlined
|
||||
autofocus
|
||||
dense
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-mb-md" q-input>
|
||||
<q-input
|
||||
:label="t('Message')"
|
||||
v-model="message"
|
||||
type="textarea"
|
||||
:maxlength="maxLength"
|
||||
:counter="true"
|
||||
:autogrow="true"
|
||||
:bottom-slots="true"
|
||||
:rules="[(value) => value.length < maxLength || 'Error!']"
|
||||
stack-label
|
||||
outlined
|
||||
autofocus
|
||||
>
|
||||
<template #append>
|
||||
<q-icon
|
||||
v-if="message !== ''"
|
||||
name="close"
|
||||
@click="message = ''"
|
||||
class="cursor-pointer"
|
||||
/>
|
||||
</template>
|
||||
<template #counter>
|
||||
<q-chip :color="color" dense>
|
||||
{{ totalLength }}/{{ maxLength }}
|
||||
</q-chip>
|
||||
</template>
|
||||
</q-input>
|
||||
</q-card-section>
|
||||
<q-card-actions align="right">
|
||||
<q-btn
|
||||
:label="t('globals.cancel')"
|
||||
color="primary"
|
||||
:disable="isLoading"
|
||||
flat
|
||||
v-close-popup
|
||||
/>
|
||||
<q-btn
|
||||
:label="t('globals.confirm')"
|
||||
@click="send()"
|
||||
:loading="isLoading"
|
||||
color="primary"
|
||||
unelevated
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.q-chip {
|
||||
transition: background 0.36s;
|
||||
}
|
||||
.q-card {
|
||||
width: 500px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
CustomerDefaultLanguage: This customer uses <strong>{locale}</strong> as their default language
|
||||
en: English
|
||||
es: Spanish
|
||||
fr: French
|
||||
templates:
|
||||
pendingPayment: 'Your order is pending of payment.
|
||||
Please, enter the website and make the payment with a credit card. Thank you.'
|
||||
minAmount: 'A minimum amount of 50€ (VAT excluded) is required for your order
|
||||
{ orderId } of { shipped } to receive it without additional shipping costs.'
|
||||
orderChanges: 'Order {orderId} of { shipped }\r
|
||||
{ changes }'
|
||||
es:
|
||||
Send SMS: Enviar SMS
|
||||
CustomerDefaultLanguage: Este cliente utiliza <strong>{locale}</strong> como idioma por defecto
|
||||
Language: Idioma
|
||||
Phone: Móvil
|
||||
Subject: Asunto
|
||||
Message: Mensaje
|
||||
templates:
|
||||
pendingPayment: 'Su pedido está pendiente de pago.
|
||||
Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.'
|
||||
minAmount: 'Es necesario un importe mínimo de 50€ (Sin IVA) en su pedido
|
||||
{ orderId } del día { shipped } para recibirlo sin portes adicionales.'
|
||||
orderChanges: 'Pedido {orderId} día { shipped }
|
||||
{ changes }'
|
||||
en: Inglés
|
||||
es: Español
|
||||
fr: Francés
|
||||
fr:
|
||||
Send SMS: Envoyer SMS
|
||||
CustomerDefaultLanguage: Ce client utilise l'{locale} comme langue par défaut
|
||||
Language: Langage
|
||||
Phone: Mobile
|
||||
Subject: Affaire
|
||||
Message: Message
|
||||
templates:
|
||||
pendingPayment: 'Votre commande est en attente de paiement.
|
||||
Veuillez vous connecter sur le site web et effectuer le paiement par carte. Merci beaucoup.'
|
||||
minAmount: 'Un montant minimum de 50€ (TVA non incluse) est requis pour votre commande
|
||||
{ orderId } du { shipped } afin de la recevoir sans frais de port supplémentaires.'
|
||||
orderChanges: 'Commande { orderId } du { shipped }\r { changes }'
|
||||
en: Anglais
|
||||
es: Espagnol
|
||||
fr: Français
|
||||
</i18n>
|
|
@ -16,7 +16,7 @@ const props = defineProps({
|
|||
module: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const slots = useSlots();
|
||||
|
@ -24,14 +24,18 @@ const { t } = useI18n();
|
|||
|
||||
onMounted(() => fetch());
|
||||
|
||||
const emit = defineEmits(['onFetch']);
|
||||
|
||||
const entity = ref();
|
||||
async function fetch() {
|
||||
const params = {};
|
||||
|
||||
if (props.filter) params.filter = props.filter;
|
||||
if (props.filter) params.filter = JSON.stringify(props.filter);
|
||||
|
||||
const { data } = await axios.get(props.url, { params });
|
||||
entity.value = data;
|
||||
|
||||
emit('onFetch', data);
|
||||
}
|
||||
|
||||
watch(props, async () => {
|
||||
|
@ -46,18 +50,18 @@ watch(props, async () => {
|
|||
<div class="header bg-primary q-pa-sm">
|
||||
<router-link :to="{ name: `${module}List` }">
|
||||
<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>
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="{ name: `${module}Summary`, params: { id: entity.id } }"
|
||||
>
|
||||
<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>
|
||||
</router-link>
|
||||
|
||||
|
@ -80,8 +84,9 @@ watch(props, async () => {
|
|||
</q-menu>
|
||||
</q-btn>
|
||||
</div>
|
||||
<slot name="before" />
|
||||
<div class="body q-py-sm">
|
||||
<q-list>
|
||||
<q-list dense>
|
||||
<q-item-label header class="ellipsis text-h5" :lines="1">
|
||||
<slot name="description" :entity="entity">
|
||||
<span>
|
||||
|
@ -98,6 +103,7 @@ watch(props, async () => {
|
|||
</q-list>
|
||||
<slot name="body" :entity="entity" />
|
||||
</div>
|
||||
<slot name="after" />
|
||||
</template>
|
||||
<!-- Skeleton -->
|
||||
<skeleton-descriptor v-if="!entity" />
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
<script setup>
|
||||
import { nextTick, ref } from 'vue';
|
||||
|
||||
const $props = defineProps({
|
||||
to: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const isHeaderMounted = ref(false);
|
||||
nextTick(() => {
|
||||
isHeaderMounted.value = document.querySelector($props.to) !== null;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<teleport v-if="isHeaderMounted" :to="$props.to">
|
||||
<slot />
|
||||
</teleport>
|
||||
</template>
|
|
@ -10,7 +10,7 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: null,
|
||||
},
|
||||
question: {
|
||||
title: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
|
@ -18,15 +18,37 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: null,
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
promise: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
||||
|
||||
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
||||
|
||||
const question = props.question || t('question');
|
||||
const message = props.message || t('message');
|
||||
const title = props.title || t('Confirm');
|
||||
const message = props.message || t('Are you sure you want to continue?');
|
||||
const isLoading = ref(false);
|
||||
|
||||
async function confirm() {
|
||||
isLoading.value = true;
|
||||
if (props.promise) {
|
||||
try {
|
||||
await props.promise(props.data);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
onDialogOK(props.data);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<q-dialog ref="dialogRef" persistent>
|
||||
|
@ -39,20 +61,28 @@ const isLoading = ref(false);
|
|||
size="xl"
|
||||
v-if="icon"
|
||||
/>
|
||||
<span class="text-h6 text-grey">{{ message }}</span>
|
||||
<span class="text-h6 text-grey">{{ title }}</span>
|
||||
<q-space />
|
||||
<q-btn icon="close" flat round dense v-close-popup />
|
||||
<q-btn icon="close" :disable="isLoading" flat round dense v-close-popup />
|
||||
</q-card-section>
|
||||
<q-card-section class="row items-center">
|
||||
{{ question }}
|
||||
{{ message }}
|
||||
</q-card-section>
|
||||
<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"
|
||||
:disable="isLoading"
|
||||
flat
|
||||
v-close-popup
|
||||
/>
|
||||
<q-btn
|
||||
:label="t('globals.confirm')"
|
||||
color="primary"
|
||||
:loading="isLoading"
|
||||
@click="onDialogOK()"
|
||||
@click="confirm()"
|
||||
unelevated
|
||||
autofocus
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
|
@ -66,13 +96,7 @@ const isLoading = ref(false);
|
|||
</style>
|
||||
|
||||
<i18n>
|
||||
"en": {
|
||||
"question": "Are you sure you want to continue?",
|
||||
"message": "Confirm"
|
||||
}
|
||||
|
||||
"es": {
|
||||
"question": "¿Seguro que quieres continuar?",
|
||||
"message": "Confirmar"
|
||||
}
|
||||
es:
|
||||
Confirm: Confirmar
|
||||
Are you sure you want to continue?: ¿Seguro que quieres continuar?
|
||||
</i18n>
|
||||
|
|
|
@ -87,15 +87,29 @@ async function search() {
|
|||
</q-form>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@media screen and (max-width: $breakpoint-xs-max) {
|
||||
.q-field {
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: $breakpoint-xs-max) {
|
||||
.q-field {
|
||||
width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.q-field {
|
||||
transition: width 0.36s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<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;
|
||||
|
|
|
@ -50,7 +50,7 @@ export function useArrayData(key, userOptions) {
|
|||
Object.assign(store.filter, filter);
|
||||
|
||||
const params = {
|
||||
filter: JSON.stringify(filter),
|
||||
filter: JSON.stringify(store.filter),
|
||||
};
|
||||
|
||||
Object.assign(params, store.userParams);
|
||||
|
|
|
@ -242,6 +242,7 @@ export default {
|
|||
basicData: 'Basic Data',
|
||||
rma: 'RMA',
|
||||
photos: 'Photos',
|
||||
log: 'Audit logs',
|
||||
},
|
||||
list: {
|
||||
customer: 'Customer',
|
||||
|
@ -344,6 +345,47 @@ export default {
|
|||
totalWithVat: 'Amount',
|
||||
},
|
||||
},
|
||||
worker: {
|
||||
pageTitles: {
|
||||
workers: 'Workers',
|
||||
list: 'List',
|
||||
basicData: 'Basic data',
|
||||
summary: 'Summary',
|
||||
},
|
||||
list: {
|
||||
name: 'Name',
|
||||
email: 'Email',
|
||||
phone: 'Phone',
|
||||
mobile: 'Mobile',
|
||||
active: 'Active',
|
||||
department: 'Department',
|
||||
schedule: 'Schedule',
|
||||
},
|
||||
card: {
|
||||
workerId: 'Worker ID',
|
||||
name: 'Name',
|
||||
email: 'Email',
|
||||
phone: 'Phone',
|
||||
mobile: 'Mobile',
|
||||
active: 'Active',
|
||||
warehouse: 'Warehouse',
|
||||
agency: 'Agency',
|
||||
salesPerson: 'Sales person',
|
||||
},
|
||||
summary: {
|
||||
basicData: 'Basic data',
|
||||
boss: 'Boss',
|
||||
phoneExtension: 'Phone extension',
|
||||
entPhone: 'Enterprise phone',
|
||||
personalPhone: 'Personal phone',
|
||||
noBoss: 'No boss',
|
||||
userData: 'User data',
|
||||
userId: 'User ID',
|
||||
role: 'Role',
|
||||
sipExtension: 'Extension',
|
||||
},
|
||||
imageNotFound: 'Image not found',
|
||||
},
|
||||
components: {
|
||||
topbar: {},
|
||||
userPanel: {
|
||||
|
|
|
@ -241,6 +241,7 @@ export default {
|
|||
basicData: 'Datos básicos',
|
||||
rma: 'RMA',
|
||||
photos: 'Fotos',
|
||||
log: 'Registros de auditoría',
|
||||
},
|
||||
list: {
|
||||
customer: 'Cliente',
|
||||
|
@ -344,6 +345,47 @@ export default {
|
|||
totalWithVat: 'Importe',
|
||||
},
|
||||
},
|
||||
worker: {
|
||||
pageTitles: {
|
||||
workers: 'Trabajadores',
|
||||
list: 'Listado',
|
||||
basicData: 'Datos básicos',
|
||||
summary: 'Resumen',
|
||||
},
|
||||
list: {
|
||||
name: 'Nombre',
|
||||
email: 'Email',
|
||||
phone: 'Teléfono',
|
||||
mobile: 'Móvil',
|
||||
active: 'Activo',
|
||||
department: 'Departamento',
|
||||
schedule: 'Horario',
|
||||
},
|
||||
card: {
|
||||
workerId: 'ID Trabajador',
|
||||
name: 'Nombre',
|
||||
email: 'Email',
|
||||
phone: 'Teléfono',
|
||||
mobile: 'Móvil',
|
||||
active: 'Activo',
|
||||
warehouse: 'Almacén',
|
||||
agency: 'Empresa',
|
||||
salesPerson: 'Comercial',
|
||||
},
|
||||
summary: {
|
||||
basicData: 'Datos básicos',
|
||||
boss: 'Jefe',
|
||||
phoneExtension: 'Extensión de teléfono',
|
||||
entPhone: 'Teléfono de empresa',
|
||||
personalPhone: 'Teléfono personal',
|
||||
noBoss: 'Sin jefe',
|
||||
userData: 'Datos de usuario',
|
||||
userId: 'ID del usuario',
|
||||
role: 'Rol',
|
||||
sipExtension: 'Extensión',
|
||||
},
|
||||
imageNotFound: 'No se ha encontrado la imagen',
|
||||
},
|
||||
components: {
|
||||
topbar: {},
|
||||
userPanel: {
|
||||
|
|
|
@ -2,26 +2,13 @@
|
|||
import { useQuasar } from 'quasar';
|
||||
import Navbar from 'src/components/NavBar.vue';
|
||||
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const stateStore = useStateStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-layout view="hHh LpR fFf">
|
||||
<Navbar />
|
||||
<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-layout>
|
||||
</template>
|
||||
|
|
|
@ -81,22 +81,47 @@ const statesFilter = {
|
|||
/>
|
||||
<fetch-data url="ClaimStates" @on-fetch="setClaimStates" auto-load />
|
||||
|
||||
<div class="container">
|
||||
<div class="column items-center">
|
||||
<q-card>
|
||||
<form-model :url="`Claims/${route.params.id}`" :filter="claimFilter" model="claim">
|
||||
<form-model
|
||||
:url="`Claims/${route.params.id}`"
|
||||
:filter="claimFilter"
|
||||
model="claim"
|
||||
>
|
||||
<template #form="{ data, validate, filter }">
|
||||
<div class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-input v-model="data.client.name" :label="t('claim.basicData.customer')" disable />
|
||||
<q-input
|
||||
v-model="data.client.name"
|
||||
:label="t('claim.basicData.customer')"
|
||||
disable
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input v-model="data.created" mask="####-##-##" fill-mask="_" autofocus>
|
||||
<q-input
|
||||
v-model="data.created"
|
||||
mask="####-##-##"
|
||||
fill-mask="_"
|
||||
autofocus
|
||||
>
|
||||
<template #append>
|
||||
<q-icon name="event" class="cursor-pointer">
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-date v-model="data.created" mask="YYYY-MM-DD">
|
||||
<q-popup-proxy
|
||||
cover
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<q-date
|
||||
v-model="data.created"
|
||||
mask="YYYY-MM-DD"
|
||||
>
|
||||
<div class="row items-center justify-end">
|
||||
<q-btn v-close-popup label="Close" color="primary" flat />
|
||||
<q-btn
|
||||
v-close-popup
|
||||
label="Close"
|
||||
color="primary"
|
||||
flat
|
||||
/>
|
||||
</div>
|
||||
</q-date>
|
||||
</q-popup-proxy>
|
||||
|
@ -116,7 +141,9 @@ const statesFilter = {
|
|||
:label="t('claim.basicData.assignedTo')"
|
||||
map-options
|
||||
use-input
|
||||
@filter="(value, update) => filter(value, update, workerFilter)"
|
||||
@filter="
|
||||
(value, update) => filter(value, update, workerFilter)
|
||||
"
|
||||
:rules="validate('claim.claimStateFk')"
|
||||
:input-debounce="0"
|
||||
>
|
||||
|
@ -141,7 +168,9 @@ const statesFilter = {
|
|||
:label="t('claim.basicData.state')"
|
||||
map-options
|
||||
use-input
|
||||
@filter="(value, update) => filter(value, update, statesFilter)"
|
||||
@filter="
|
||||
(value, update) => filter(value, update, statesFilter)
|
||||
"
|
||||
:rules="validate('claim.claimStateFk')"
|
||||
:input-debounce="0"
|
||||
>
|
||||
|
@ -166,7 +195,10 @@ const statesFilter = {
|
|||
</div>
|
||||
<div class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-checkbox v-model="data.hasToPickUp" :label="t('claim.basicData.picked')" />
|
||||
<q-checkbox
|
||||
v-model="data.hasToPickUp"
|
||||
:label="t('claim.basicData.picked')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -176,12 +208,8 @@ const statesFilter = {
|
|||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.q-card {
|
||||
width: 800px;
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -3,20 +3,19 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useStateStore } from 'stores/useStateStore';
|
||||
import ClaimDescriptor from './ClaimDescriptor.vue';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||
<VnSearchbar
|
||||
data-key="ClaimList"
|
||||
:label="t('Search claim')"
|
||||
:info="t('You can search by claim id or customer name')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit">
|
||||
<claim-descriptor />
|
||||
|
|
|
@ -6,6 +6,7 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useRouter } from 'vue-router';
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
claim: {
|
||||
|
@ -33,13 +34,15 @@ function confirmPickupOrder() {
|
|||
quasar.dialog({
|
||||
component: SendEmailDialog,
|
||||
componentProps: {
|
||||
data: {
|
||||
address: customer.email,
|
||||
},
|
||||
send: sendPickupOrder,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function sendPickupOrder(address) {
|
||||
function sendPickupOrder({ address }) {
|
||||
const id = claim.value.id;
|
||||
const customer = claim.value.client;
|
||||
return sendEmail(`Claims/${id}/claim-pickup-email`, {
|
||||
|
@ -48,16 +51,26 @@ function sendPickupOrder(address) {
|
|||
});
|
||||
}
|
||||
|
||||
const showConfirmDialog = ref(false);
|
||||
async function deleteClaim() {
|
||||
function confirmRemove() {
|
||||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('confirmDeletion'),
|
||||
message: t('confirmDeletionMessage'),
|
||||
promise: remove,
|
||||
},
|
||||
})
|
||||
.onOk(async () => await router.push({ name: 'ClaimList' }));
|
||||
}
|
||||
|
||||
async function remove() {
|
||||
const id = claim.value.id;
|
||||
await axios.delete(`Claims/${id}`);
|
||||
quasar.notify({
|
||||
message: t('globals.dataDeleted'),
|
||||
type: 'positive',
|
||||
icon: 'check',
|
||||
type: 'positive'
|
||||
});
|
||||
await router.push({ name: 'ClaimList' });
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
|
@ -87,27 +100,12 @@ async function deleteClaim() {
|
|||
</q-menu>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
<q-item @click="showConfirmDialog = true" v-ripple clickable>
|
||||
<q-item @click="confirmRemove()" v-ripple clickable>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="delete" />
|
||||
</q-item-section>
|
||||
<q-item-section>{{ t('deleteClaim') }}</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-dialog v-model="showConfirmDialog">
|
||||
<q-card class="q-pa-sm">
|
||||
<q-card-section class="row items-center q-pb-none">
|
||||
<span class="text-h6 text-grey">{{ t('confirmDeletion') }}</span>
|
||||
<q-space />
|
||||
<q-btn icon="close" flat round dense v-close-popup />
|
||||
</q-card-section>
|
||||
<q-card-section class="row items-center">{{ t('confirmDeletionMessage') }}</q-card-section>
|
||||
<q-card-actions align="right">
|
||||
<q-btn :label="t('globals.cancel')" color="primary" flat v-close-popup />
|
||||
<q-btn :label="t('globals.confirm')" color="primary" @click="deleteClaim" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import ClaimLogFilter from './ClaimLogFilter.vue';
|
||||
|
||||
import { toDate } from 'src/filters';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const route = useRoute();
|
||||
const session = useSession();
|
||||
const token = session.getToken();
|
||||
const { t } = useI18n();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'property',
|
||||
label: 'Property',
|
||||
field: (row) => t(`properties.${row.property}`),
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'before',
|
||||
label: 'Before',
|
||||
field: (row) => formatValue(row.before),
|
||||
},
|
||||
{
|
||||
name: 'after',
|
||||
label: 'After',
|
||||
field: (row) => formatValue(row.after),
|
||||
},
|
||||
];
|
||||
|
||||
function formatValue(value) {
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? t('Yes') : t('No');
|
||||
}
|
||||
|
||||
if (isNaN(value) && !isNaN(Date.parse(value))) {
|
||||
return toDate(value);
|
||||
}
|
||||
|
||||
if (value === undefined) {
|
||||
return t('Nothing');
|
||||
}
|
||||
|
||||
return `"${value}"`;
|
||||
}
|
||||
|
||||
function actionColor(action) {
|
||||
if (action === 'insert') return 'positive';
|
||||
if (action === 'update') return 'positive';
|
||||
if (action === 'delete') return 'negative';
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="column items-center">
|
||||
<q-timeline class="q-pa-md">
|
||||
<q-timeline-entry heading tag="h4"> {{ t('Audit logs') }} </q-timeline-entry>
|
||||
<Paginate
|
||||
data-key="ClaimLogs"
|
||||
:url="`Claims/${route.params.id}/logs`"
|
||||
order="id DESC"
|
||||
:offset="100"
|
||||
:limit="5"
|
||||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<template v-for="log of rows" :key="log.id">
|
||||
<q-timeline-entry
|
||||
:avatar="`/api/Images/user/160x160/${log.userFk}/download?access_token=${token}`"
|
||||
>
|
||||
<template #subtitle>
|
||||
{{ log.userName }} -
|
||||
{{
|
||||
toDate(log.created, {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: 'short',
|
||||
})
|
||||
}}
|
||||
</template>
|
||||
<template #title>
|
||||
<q-chip :color="actionColor(log.action)">
|
||||
{{ t(`actions.${log.action}`) }}
|
||||
</q-chip>
|
||||
{{ t(`models.${log.model}`) }}
|
||||
</template>
|
||||
<q-table
|
||||
:rows="log.changes"
|
||||
:columns="columns"
|
||||
row-key="property"
|
||||
hide-pagination
|
||||
dense
|
||||
flat
|
||||
>
|
||||
<template #header="props">
|
||||
<q-tr :props="props">
|
||||
<q-th
|
||||
v-for="col in props.cols"
|
||||
:key="col.name"
|
||||
:props="props"
|
||||
>
|
||||
{{ t(col.label) }}
|
||||
</q-th>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-timeline-entry>
|
||||
</template>
|
||||
</template>
|
||||
</Paginate>
|
||||
</q-timeline>
|
||||
</div>
|
||||
<Teleport v-if="stateStore.isHeaderMounted()" to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn flat @click="stateStore.toggleRightDrawer()" round dense icon="menu">
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.rightDrawer" show-if-above side="right" :width="300">
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<ClaimLogFilter data-key="ClaimLogs" />
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.q-timeline {
|
||||
width: 100%;
|
||||
max-width: 80em;
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
en:
|
||||
actions:
|
||||
insert: Creates
|
||||
update: Updates
|
||||
delete: Deletes
|
||||
models:
|
||||
Claim: Claim
|
||||
ClaimDms: Document
|
||||
ClaimBeginning: Claimed Sales
|
||||
ClaimObservation: Observation
|
||||
properties:
|
||||
id: ID
|
||||
claimFk: Claim ID
|
||||
saleFk: Sale ID
|
||||
quantity: Quantity
|
||||
observation: Observation
|
||||
ticketCreated: Created
|
||||
created: Created
|
||||
isChargedToMana: Charged to mana
|
||||
hasToPickUp: Has to pick Up
|
||||
dmsFk: Document ID
|
||||
text: Description
|
||||
es:
|
||||
Audit logs: Registros de auditoría
|
||||
Property: Propiedad
|
||||
Before: Antes
|
||||
After: Después
|
||||
Yes: Si
|
||||
Nothing: Nada
|
||||
actions:
|
||||
insert: Crea
|
||||
update: Actualiza
|
||||
delete: Elimina
|
||||
models:
|
||||
Claim: Reclamación
|
||||
ClaimDms: Documento
|
||||
ClaimBeginning: Línea reclamada
|
||||
ClaimObservation: Observación
|
||||
properties:
|
||||
id: ID
|
||||
claimFk: ID reclamación
|
||||
saleFk: ID linea de venta
|
||||
quantity: Cantidad
|
||||
observation: Observación
|
||||
ticketCreated: Creado
|
||||
created: Creado
|
||||
isChargedToMana: Cargado a maná
|
||||
hasToPickUp: Se debe recoger
|
||||
dmsFk: ID documento
|
||||
text: Descripción
|
||||
</i18n>
|
|
@ -0,0 +1,79 @@
|
|||
<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();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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-date
|
||||
v-model="params.created"
|
||||
@update:model-value="searchFn()"
|
||||
dense
|
||||
flat
|
||||
minimal
|
||||
>
|
||||
</q-date>
|
||||
<q-list dense>
|
||||
<q-separator />
|
||||
<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('User')"
|
||||
v-model="params.userFk"
|
||||
@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-list>
|
||||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
params:
|
||||
search: Contains
|
||||
userFk: User
|
||||
created: Created
|
||||
es:
|
||||
params:
|
||||
search: Contiene
|
||||
userFk: Usuario
|
||||
created: Creada
|
||||
User: Usuario
|
||||
</i18n>
|
|
@ -1,22 +1,20 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { ref, computed } from 'vue';
|
||||
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 { useStateStore } from 'stores/useStateStore';
|
||||
import { useSession } from 'composables/useSession';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const session = useSession();
|
||||
const token = session.getToken();
|
||||
const quasar = useQuasar();
|
||||
|
||||
const claimId = computed(() => router.currentRoute.value.params.id);
|
||||
|
||||
|
@ -66,23 +64,23 @@ function openDialog(dmsId) {
|
|||
multimediaDialog.value = true;
|
||||
}
|
||||
|
||||
function viewDeleteDms(dmsId) {
|
||||
function viewDeleteDms(index) {
|
||||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
message: t('This file will be deleted'),
|
||||
title: t('This file will be deleted'),
|
||||
icon: 'delete',
|
||||
data: { index },
|
||||
promise: deleteDms,
|
||||
},
|
||||
})
|
||||
.onOk(() => deleteDms(dmsId));
|
||||
.onOk(() => claimDms.value.splice(index, 1));
|
||||
}
|
||||
|
||||
async function deleteDms(index) {
|
||||
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',
|
||||
|
@ -95,7 +93,7 @@ function setClaimDms(data) {
|
|||
|
||||
claimDms.value = data.claimDms.map((media) => {
|
||||
media.isVideo = media.dms.contentType == 'video/mp4';
|
||||
media.url = `${window.location.origin}/api/Claims/${media.dmsFk}/downloadFile?access_token=${token}`;
|
||||
media.url = `/api/Claims/${media.dmsFk}/downloadFile?access_token=${token}`;
|
||||
return media;
|
||||
});
|
||||
client.value = data.client;
|
||||
|
@ -239,7 +237,10 @@ function onDrag() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<teleport-slot v-if="!quasar.platform.is.mobile" to="#header-actions">
|
||||
<Teleport
|
||||
v-if="stateStore.isHeaderMounted() && !quasar.platform.is.mobile"
|
||||
to="#actions-prepend"
|
||||
>
|
||||
<div class="row q-gutter-x-sm">
|
||||
<label for="fileInput">
|
||||
<q-btn
|
||||
|
@ -262,10 +263,16 @@ function onDrag() {
|
|||
</label>
|
||||
<q-separator vertical />
|
||||
</div>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
|
||||
<teleport-slot to=".q-footer">
|
||||
<q-tabs align="justify" inline-label narrow-indicator>
|
||||
<q-page-sticky
|
||||
v-if="quasar.platform.is.mobile"
|
||||
position="bottom"
|
||||
:offset="[0, 0]"
|
||||
expand
|
||||
>
|
||||
<q-toolbar class="bg-primary text-white q-pa-none">
|
||||
<q-tabs class="full-width" align="justify" inline-label narrow-indicator>
|
||||
<q-tab
|
||||
@click="inputFile.nativeEl.click()"
|
||||
icon="add_circle"
|
||||
|
@ -282,7 +289,8 @@ function onDrag() {
|
|||
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip>
|
||||
</q-tab>
|
||||
</q-tabs>
|
||||
</teleport-slot>
|
||||
</q-toolbar>
|
||||
</q-page-sticky>
|
||||
|
||||
<!-- MULTIMEDIA DIALOG START-->
|
||||
<q-dialog
|
||||
|
|
|
@ -5,9 +5,9 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useQuasar } from 'quasar';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
|
||||
import { toDate } from 'src/filters';
|
||||
|
@ -15,6 +15,7 @@ import { toDate } from 'src/filters';
|
|||
const quasar = useQuasar();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const stateStore = useStateStore();
|
||||
const arrayData = useArrayData('ClaimRma');
|
||||
|
||||
const claim = ref();
|
||||
|
@ -44,6 +45,12 @@ async function onFetch(data) {
|
|||
}
|
||||
|
||||
async function addRow() {
|
||||
if (!claim.value.rma) {
|
||||
return quasar.notify({
|
||||
message: `This claim is not associated to any RMA`,
|
||||
type: 'negative',
|
||||
});
|
||||
}
|
||||
const formData = {
|
||||
code: claim.value.rma,
|
||||
};
|
||||
|
@ -62,18 +69,19 @@ function confirmRemove(id) {
|
|||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
data: { id },
|
||||
promise: remove,
|
||||
},
|
||||
})
|
||||
.onOk(() => remove(id));
|
||||
.onOk(async () => await arrayData.refresh());
|
||||
}
|
||||
|
||||
async function remove(id) {
|
||||
async function remove({ id }) {
|
||||
await axios.delete(`ClaimRmas/${id}`);
|
||||
await arrayData.refresh();
|
||||
|
||||
quasar.notify({
|
||||
type: 'positive',
|
||||
message: t('globals.rowRemoved'),
|
||||
icon: 'check',
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
@ -84,6 +92,8 @@ async function remove(id) {
|
|||
@on-fetch="onFetch"
|
||||
auto-load
|
||||
/>
|
||||
<div class="column items-center">
|
||||
<div class="list">
|
||||
<paginate data-key="ClaimRma" url="ClaimRmas">
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card">
|
||||
|
@ -134,24 +144,40 @@ async function remove(id) {
|
|||
</q-card>
|
||||
</template>
|
||||
</paginate>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<teleport-slot v-if="!quasar.platform.is.mobile" to="#header-actions">
|
||||
<Teleport
|
||||
v-if="stateStore.isHeaderMounted() && !quasar.platform.is.mobile"
|
||||
to="#actions-prepend"
|
||||
>
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn @click="addRow()" icon="add" color="primary" dense rounded>
|
||||
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip>
|
||||
</q-btn>
|
||||
<q-separator vertical />
|
||||
</div>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
|
||||
<teleport-slot to=".q-footer">
|
||||
<q-tabs align="justify" inline-label narrow-indicator>
|
||||
<q-page-sticky
|
||||
v-if="quasar.platform.is.mobile"
|
||||
position="bottom"
|
||||
:offset="[0, 0]"
|
||||
expand
|
||||
>
|
||||
<q-toolbar class="bg-primary text-white q-pa-none">
|
||||
<q-tabs class="full-width" align="justify" inline-label narrow-indicator>
|
||||
<q-tab @click="addRow()" icon="add_circle" :label="t('globals.add')" />
|
||||
</q-tabs>
|
||||
</teleport-slot>
|
||||
</q-toolbar>
|
||||
</q-page-sticky>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.list {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
.q-toolbar {
|
||||
background-color: $grey-9;
|
||||
}
|
||||
|
@ -163,3 +189,8 @@ async function remove(id) {
|
|||
z-index: 2998;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
This claim is not associated to any RMA: Esta reclamación no está asociada a ninguna ARM
|
||||
</i18n>
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
<script setup>
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { toDate } from 'src/filters/index';
|
||||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import { toDate } from 'filters/index';
|
||||
import Paginate from 'components/PaginateData.vue';
|
||||
import ClaimSummaryDialog from './Card/ClaimSummaryDialog.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 CustomerDescriptorPopover from 'pages/Customer/Card/CustomerDescriptorPopover.vue';
|
||||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||
import ClaimFilter from './ClaimFilter.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
|
@ -17,9 +15,6 @@ const router = useRouter();
|
|||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
|
||||
onMounted(() => (stateStore.rightDrawer = true));
|
||||
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||
|
||||
function stateColor(code) {
|
||||
if (code === 'pending') return 'green';
|
||||
if (code === 'managed') return 'orange';
|
||||
|
@ -41,20 +36,40 @@ function viewSummary(id) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport 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">
|
||||
</Teleport>
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<ClaimFilter data-key="ClaimList" />
|
||||
</teleport-slot>
|
||||
<q-page class="q-pa-md">
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page class="column items-center q-pa-md">
|
||||
<div class="card-list">
|
||||
<paginate data-key="ClaimList" url="Claims/filter" order="id DESC" auto-load>
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card" v-for="row of rows" :key="row.id">
|
||||
<q-card class="card q-mb-md" v-for="row of rows" :key="row.id">
|
||||
<q-item
|
||||
class="q-pa-none items-start cursor-pointer q-hoverable"
|
||||
v-ripple
|
||||
|
@ -71,13 +86,17 @@ function viewSummary(id) {
|
|||
<q-item-label caption>
|
||||
{{ t('claim.list.customer') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ row.clientName }}</q-item-label>
|
||||
<q-item-label>
|
||||
{{ row.clientName }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('claim.list.assignedTo') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ row.workerName }}</q-item-label>
|
||||
<q-item-label>
|
||||
{{ row.workerName }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item class="q-pa-none">
|
||||
|
@ -163,9 +182,17 @@ function viewSummary(id) {
|
|||
</q-card>
|
||||
</template>
|
||||
</paginate>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-list {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search claim: Buscar reclamación
|
||||
|
|
|
@ -43,23 +43,25 @@ function confirm(id) {
|
|||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
data: { id },
|
||||
promise: remove,
|
||||
},
|
||||
})
|
||||
.onOk(() => remove(id));
|
||||
.onOk(async () => await arrayData.refresh());
|
||||
}
|
||||
|
||||
async function remove(id) {
|
||||
async function remove({ id }) {
|
||||
await axios.delete(`ClaimRmas/${id}`);
|
||||
await arrayData.refresh();
|
||||
quasar.notify({
|
||||
type: 'positive',
|
||||
message: t('globals.rowRemoved'),
|
||||
icon: 'check',
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-page class="q-pa-md sticky">
|
||||
<q-page class="column items-center q-pa-md sticky">
|
||||
<q-page-sticky expand position="top" :offset="[16, 16]">
|
||||
<q-card class="card q-pa-md">
|
||||
<q-form @submit="submit">
|
||||
|
@ -79,6 +81,7 @@ async function remove(id) {
|
|||
</q-form>
|
||||
</q-card>
|
||||
</q-page-sticky>
|
||||
<div class="card-list">
|
||||
<paginate
|
||||
data-key="ClaimRmaList"
|
||||
url="ClaimRmas"
|
||||
|
@ -105,7 +108,11 @@ async function remove(id) {
|
|||
</q-list>
|
||||
</q-item-section>
|
||||
<q-card-actions vertical class="justify-between">
|
||||
<q-skeleton type="circle" class="q-mb-md" size="40px" />
|
||||
<q-skeleton
|
||||
type="circle"
|
||||
class="q-mb-md"
|
||||
size="40px"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
|
@ -119,7 +126,9 @@ async function remove(id) {
|
|||
<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>
|
||||
</q-list>
|
||||
|
@ -141,6 +150,7 @@ async function remove(id) {
|
|||
</q-card>
|
||||
</template>
|
||||
</paginate>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
|
@ -149,7 +159,7 @@ async function remove(id) {
|
|||
padding-top: 156px;
|
||||
}
|
||||
|
||||
.card {
|
||||
.card-list, .card {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ const filterOptions = {
|
|||
@on-fetch="(data) => (businessTypes = data)"
|
||||
auto-load
|
||||
/>
|
||||
<div class="container">
|
||||
<div class="column items-center">
|
||||
<q-card>
|
||||
<form-model :url="`Clients/${route.params.id}`" model="customer">
|
||||
<template #form="{ data, validate, filter }">
|
||||
|
@ -172,11 +172,6 @@ const filterOptions = {
|
|||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.q-card {
|
||||
width: 800px;
|
||||
}
|
||||
|
|
|
@ -3,20 +3,19 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useStateStore } from 'stores/useStateStore';
|
||||
import CustomerDescriptor from './CustomerDescriptor.vue';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||
<VnSearchbar
|
||||
data-key="CustomerList"
|
||||
:label="t('Search customer')"
|
||||
:info="t('You can search by customer id or name')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit">
|
||||
<CustomerDescriptor />
|
||||
|
|
|
@ -4,6 +4,7 @@ import { useRoute } from 'vue-router';
|
|||
import { useI18n } from 'vue-i18n';
|
||||
import { toCurrency } from 'src/filters';
|
||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
|
@ -30,7 +31,10 @@ const entityId = computed(() => {
|
|||
{{ t('customer.card.salesPerson') }}
|
||||
</q-item-label>
|
||||
<q-item-label class="col q-ma-none">
|
||||
<span class="link">
|
||||
{{ entity.salesPersonUser.name }}
|
||||
<WorkerDescriptorProxy :id="entity.salesPersonFk" />
|
||||
</span>
|
||||
</q-item-label>
|
||||
</q-item>
|
||||
<q-item class="row">
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
<script setup>
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import Paginate from 'src/components/PaginateData.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';
|
||||
|
||||
|
@ -15,9 +13,6 @@ const router = useRouter();
|
|||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
|
||||
onMounted(() => (stateStore.rightDrawer = true));
|
||||
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||
|
||||
function navigate(id) {
|
||||
router.push({ path: `/customer/${id}` });
|
||||
}
|
||||
|
@ -33,20 +28,45 @@ function viewSummary(id) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport 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">
|
||||
</Teleport>
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<CustomerFilter data-key="CustomerList" />
|
||||
</teleport-slot>
|
||||
<q-page class="q-pa-md">
|
||||
<paginate data-key="CustomerList" url="/Clients/filter" order="id DESC" auto-load>
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page class="column items-center q-pa-md">
|
||||
<div class="card-list">
|
||||
<paginate
|
||||
data-key="CustomerList"
|
||||
url="/Clients/filter"
|
||||
order="id DESC"
|
||||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card" v-for="row of rows" :key="row.id">
|
||||
<q-card class="card q-mb-md" v-for="row of rows" :key="row.id">
|
||||
<q-item
|
||||
class="q-pa-none items-start cursor-pointer q-hoverable"
|
||||
v-ripple
|
||||
|
@ -100,7 +120,7 @@ function viewSummary(id) {
|
|||
<q-btn
|
||||
flat
|
||||
round
|
||||
color="orange"
|
||||
color="primary"
|
||||
icon="arrow_circle_right"
|
||||
@click="navigate(row.id)"
|
||||
>
|
||||
|
@ -127,9 +147,17 @@ function viewSummary(id) {
|
|||
</q-card>
|
||||
</template>
|
||||
</paginate>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-list {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search customer: Buscar cliente
|
||||
|
|
|
@ -3,20 +3,19 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useStateStore } from 'stores/useStateStore';
|
||||
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 stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||
<VnSearchbar
|
||||
data-key="InvoiceOutList"
|
||||
:label="t('Search invoice')"
|
||||
:info="t('You can search by invoice reference')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit">
|
||||
<InvoiceOutDescriptor />
|
||||
|
|
|
@ -7,7 +7,6 @@ import { useStateStore } from 'stores/useStateStore';
|
|||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import InvoiceOutSummaryDialog from './Card/InvoiceOutSummaryDialog.vue';
|
||||
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';
|
||||
|
||||
|
@ -34,17 +33,37 @@ function viewSummary(id) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="InvoiceOutList"
|
||||
:label="t('Search invoice')"
|
||||
:info="t('You can search by invoice reference')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
<teleport-slot to="#rightPanel">
|
||||
</Teleport>
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<InvoiceOutFilter data-key="InvoiceOutList" />
|
||||
</teleport-slot>
|
||||
<q-page class="q-pa-md">
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page class="column items-center q-pa-md">
|
||||
<div class="card-list">
|
||||
<paginate
|
||||
data-key="InvoiceOutList"
|
||||
url="InvoiceOuts/filter"
|
||||
|
@ -52,7 +71,7 @@ function viewSummary(id) {
|
|||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card" v-for="row of rows" :key="row.id">
|
||||
<q-card class="card q-mb-md" v-for="row of rows" :key="row.id">
|
||||
<q-item
|
||||
class="q-pa-none items-start cursor-pointer q-hoverable"
|
||||
v-ripple
|
||||
|
@ -103,7 +122,9 @@ function viewSummary(id) {
|
|||
<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-label caption>
|
||||
|
@ -145,9 +166,17 @@ function viewSummary(id) {
|
|||
</q-card>
|
||||
</template>
|
||||
</paginate>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-list {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search invoice: Buscar factura emitida
|
||||
|
|
|
@ -3,20 +3,19 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useStateStore } from 'stores/useStateStore';
|
||||
import TicketDescriptor from './TicketDescriptor.vue';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||
<VnSearchbar
|
||||
data-key="TicketList"
|
||||
:label="t('Search ticket')"
|
||||
:info="t('You can search by ticket id or alias')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit">
|
||||
<TicketDescriptor />
|
||||
|
|
|
@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n';
|
|||
import { toDate } from 'src/filters';
|
||||
import CustomerDescriptorPopover from 'src/pages/Customer/Card/CustomerDescriptorPopover.vue';
|
||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||
import TicketDescriptorMenu from './TicketDescriptorMenu.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
|
@ -23,11 +24,25 @@ const entityId = computed(() => {
|
|||
|
||||
const filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'address',
|
||||
scope: {
|
||||
fields: ['id', 'name', 'mobile', 'phone'],
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'client',
|
||||
scope: {
|
||||
fields: ['id', 'name', 'salesPersonFk'],
|
||||
include: { relation: 'salesPersonUser' },
|
||||
fields: ['id', 'name', 'salesPersonFk', 'phone', 'mobile', 'email'],
|
||||
include: [
|
||||
{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['id', 'lang'],
|
||||
},
|
||||
},
|
||||
{ relation: 'salesPersonUser' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -61,6 +76,9 @@ function stateColor(state) {
|
|||
|
||||
<template>
|
||||
<card-descriptor module="Ticket" :url="`Tickets/${entityId}`" :filter="filter">
|
||||
<template #menu="{ entity }">
|
||||
<TicketDescriptorMenu :ticket="entity" />
|
||||
</template>
|
||||
<template #description="{ entity }">
|
||||
<span>
|
||||
{{ entity.client.name }}
|
||||
|
@ -121,6 +139,16 @@ function stateColor(state) {
|
|||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
<q-card-actions class="q-gutter-md">
|
||||
<q-icon
|
||||
v-if="entity.isDeleted == true"
|
||||
name="vn:deletedTicket"
|
||||
size="xs"
|
||||
color="primary"
|
||||
>
|
||||
<q-tooltip>{{ t('This ticket is deleted') }}</q-tooltip>
|
||||
</q-icon>
|
||||
</q-card-actions>
|
||||
|
||||
<q-card-actions>
|
||||
<q-btn
|
||||
|
@ -135,3 +163,8 @@ function stateColor(state) {
|
|||
</template>
|
||||
</card-descriptor>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
This ticket is deleted: Este ticket está eliminado
|
||||
</i18n>
|
||||
|
|
|
@ -0,0 +1,256 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { ref } from 'vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
import VnSmsDialog from 'components/common/VnSmsDialog.vue';
|
||||
import toDate from 'filters/toDate';
|
||||
|
||||
const props = defineProps({
|
||||
ticket: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
const { openReport, sendEmail } = usePrintService();
|
||||
|
||||
const ticket = ref(props.ticket);
|
||||
|
||||
function openDeliveryNote(type = 'deliveryNote', documentType = 'pdf') {
|
||||
const path = `Tickets/${ticket.value.id}/delivery-note-${documentType}`;
|
||||
openReport(path, {
|
||||
recipientId: ticket.value.clientFk,
|
||||
type: type,
|
||||
});
|
||||
}
|
||||
|
||||
function sendDeliveryNoteConfirmation(type = 'deliveryNote', documentType = 'pdf') {
|
||||
const customer = ticket.value.client;
|
||||
quasar.dialog({
|
||||
component: SendEmailDialog,
|
||||
componentProps: {
|
||||
data: {
|
||||
address: customer.email,
|
||||
type: type,
|
||||
documentType: documentType,
|
||||
},
|
||||
promise: sendDeliveryNote,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function sendDeliveryNote({ address, type, documentType }) {
|
||||
const id = ticket.value.id;
|
||||
const customer = ticket.value.client;
|
||||
let pathName = 'delivery-note-email';
|
||||
if (documentType == 'csv') pathName = 'delivery-note-csv-email';
|
||||
|
||||
const path = `Tickets/${id}/${pathName}`;
|
||||
return sendEmail(path, {
|
||||
recipientId: customer.id,
|
||||
recipient: address,
|
||||
type: type,
|
||||
});
|
||||
}
|
||||
|
||||
const shipped = toDate(ticket.value.shipped);
|
||||
function showSmsDialog(template, customData) {
|
||||
const address = ticket.value.address;
|
||||
const client = ticket.value.client;
|
||||
const phone =
|
||||
route.params.phone ||
|
||||
address.mobile ||
|
||||
address.phone ||
|
||||
client.mobile ||
|
||||
client.phone;
|
||||
|
||||
const data = {
|
||||
orderId: ticket.value.id,
|
||||
shipped: shipped,
|
||||
};
|
||||
|
||||
if (typeof customData === 'object') {
|
||||
Object.assign(data, customData);
|
||||
}
|
||||
|
||||
quasar.dialog({
|
||||
component: VnSmsDialog,
|
||||
componentProps: {
|
||||
phone: phone,
|
||||
template: template,
|
||||
locale: client.user.lang,
|
||||
data: data,
|
||||
promise: sendSms,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function showSmsDialogWithChanges() {
|
||||
const query = `TicketLogs/${route.params.id}/getChanges`;
|
||||
const response = await axios.get(query);
|
||||
|
||||
showSmsDialog('orderChanges', { changes: response.data });
|
||||
}
|
||||
|
||||
async function sendSms(body) {
|
||||
await axios.post(`Tickets/${route.params.id}/sendSms`, body);
|
||||
quasar.notify({
|
||||
message: 'Notification sent',
|
||||
type: 'positive',
|
||||
});
|
||||
}
|
||||
|
||||
function confirmDelete() {
|
||||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
promise: remove,
|
||||
},
|
||||
})
|
||||
.onOk(async () => await router.push({ name: 'TicketList' }));
|
||||
}
|
||||
|
||||
async function remove() {
|
||||
const id = route.params.id;
|
||||
await axios.post(`Tickets/${id}/setDeleted`);
|
||||
|
||||
quasar.notify({
|
||||
message: t('Ticket deleted'),
|
||||
type: 'positive',
|
||||
});
|
||||
quasar.notify({
|
||||
message: t('You can undo this action within the first hour'),
|
||||
icon: 'info',
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<q-item v-ripple clickable>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="picture_as_pdf" />
|
||||
</q-item-section>
|
||||
<q-item-section>{{ t('Open Delivery Note...') }}</q-item-section>
|
||||
<q-item-section side>
|
||||
<q-icon name="keyboard_arrow_right" />
|
||||
</q-item-section>
|
||||
<q-menu anchor="top end" self="top start" auto-close bordered>
|
||||
<q-list>
|
||||
<q-item @click="openDeliveryNote('deliveryNote')" v-ripple clickable>
|
||||
<q-item-section>{{ t('With prices') }}</q-item-section>
|
||||
</q-item>
|
||||
<q-item @click="openDeliveryNote('withoutPrices')" v-ripple clickable>
|
||||
<q-item-section>{{ t('Without prices') }}</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
@click="openDeliveryNote('deliveryNote', 'csv')"
|
||||
v-ripple
|
||||
clickable
|
||||
>
|
||||
<q-item-section>{{ t('As CSV') }}</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-item>
|
||||
<q-item v-ripple clickable>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="send" />
|
||||
</q-item-section>
|
||||
<q-item-section>{{ t('Send Delivery Note...') }}</q-item-section>
|
||||
<q-item-section side>
|
||||
<q-icon name="keyboard_arrow_right" />
|
||||
</q-item-section>
|
||||
<q-menu anchor="top end" self="top start" auto-close>
|
||||
<q-list>
|
||||
<q-item
|
||||
@click="sendDeliveryNoteConfirmation('deliveryNote')"
|
||||
v-ripple
|
||||
clickable
|
||||
>
|
||||
<q-item-section>{{ t('With prices') }}</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
@click="sendDeliveryNoteConfirmation('withoutPrices')"
|
||||
v-ripple
|
||||
clickable
|
||||
>
|
||||
<q-item-section>{{ t('Without prices') }}</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
@click="sendDeliveryNoteConfirmation('deliveryNote', 'csv')"
|
||||
v-ripple
|
||||
clickable
|
||||
>
|
||||
<q-item-section>{{ t('As CSV') }}</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-item>
|
||||
<q-item @click="openDeliveryNote('proforma')" v-ripple clickable>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="receipt" />
|
||||
</q-item-section>
|
||||
<q-item-section>{{ t('Open Proforma Invoice') }}</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-ripple clickable>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="sms" />
|
||||
</q-item-section>
|
||||
<q-item-section>{{ t('Send SMS...') }}</q-item-section>
|
||||
<q-item-section side>
|
||||
<q-icon name="keyboard_arrow_right" />
|
||||
</q-item-section>
|
||||
<q-menu anchor="top end" self="top start" auto-close>
|
||||
<q-list>
|
||||
<q-item @click="showSmsDialog('pendingPayment')" v-ripple clickable>
|
||||
<q-item-section>{{ t('Pending payment') }}</q-item-section>
|
||||
</q-item>
|
||||
<q-item @click="showSmsDialog('minAmount')" v-ripple clickable>
|
||||
<q-item-section>{{ t('Minimum amount') }}</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
@click="showSmsDialogWithChanges('orderChanges')"
|
||||
v-ripple
|
||||
clickable
|
||||
>
|
||||
<q-item-section>{{ t('Order changes') }}</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-item>
|
||||
<template v-if="!ticket.isDeleted">
|
||||
<q-separator />
|
||||
<q-item @click="confirmDelete()" v-ripple clickable>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="delete" />
|
||||
</q-item-section>
|
||||
<q-item-section>{{ t('Delete ticket') }}</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Open Delivery Note...: Abrir albarán...
|
||||
Send Delivery Note...: Enviar albarán...
|
||||
With prices: Con precios
|
||||
Without prices: Sin precios
|
||||
As CSV: Como CSV
|
||||
Open Proforma Invoice: Abrir factura proforma
|
||||
Delete ticket: Eliminar ticket
|
||||
Send SMS...: Enviar SMS
|
||||
Pending payment: Pago pendiente
|
||||
Minimum amount: Importe mínimo
|
||||
Order changes: Cambios del pedido
|
||||
Ticket deleted: Ticket eliminado
|
||||
You can undo this action within the first hour: Puedes deshacer esta acción dentro de la primera hora
|
||||
</i18n>
|
|
@ -7,8 +7,6 @@ import { useStateStore } from 'stores/useStateStore';
|
|||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import { toDate, toCurrency } from 'src/filters/index';
|
||||
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';
|
||||
|
||||
|
@ -71,17 +69,37 @@ function viewSummary(id) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport 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">
|
||||
</Teleport>
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<TicketFilter data-key="TicketList" />
|
||||
</teleport-slot>
|
||||
<q-page class="q-pa-md">
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page class="column items-center q-pa-md">
|
||||
<div class="card-list">
|
||||
<paginate
|
||||
data-key="TicketList"
|
||||
url="Tickets/filter"
|
||||
|
@ -90,7 +108,7 @@ function viewSummary(id) {
|
|||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card" v-for="row of rows" :key="row.id">
|
||||
<q-card class="card q-mb-md" v-for="row of rows" :key="row.id">
|
||||
<q-item
|
||||
class="q-pa-none items-start cursor-pointer q-hoverable"
|
||||
v-ripple
|
||||
|
@ -105,7 +123,9 @@ function viewSummary(id) {
|
|||
<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-label caption>
|
||||
|
@ -189,9 +209,17 @@ function viewSummary(id) {
|
|||
</q-card>
|
||||
</template>
|
||||
</paginate>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-list {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search ticket: Buscar ticket
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import WorkerDescriptor from './WorkerDescriptor.vue';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||
<VnSearchbar
|
||||
data-key="WorkerList"
|
||||
:label="t('Search worker')"
|
||||
:info="t('You can search by worker id or name')"
|
||||
/>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit">
|
||||
<WorkerDescriptor />
|
||||
<q-separator />
|
||||
<left-menu source="card" />
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page-container>
|
||||
<q-page class="q-pa-md">
|
||||
<router-view></router-view>
|
||||
</q-page>
|
||||
</q-page-container>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search worker: Buscar trabajador
|
||||
You can search by worker id or name: Puedes buscar por id o nombre del trabajador
|
||||
</i18n>
|
|
@ -0,0 +1,138 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const { getToken } = useSession();
|
||||
|
||||
const entityId = computed(() => {
|
||||
return $props.id || route.params.id;
|
||||
});
|
||||
|
||||
const worker = ref();
|
||||
const filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['email', 'name', 'nickname'],
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'department',
|
||||
scope: {
|
||||
include: [
|
||||
{
|
||||
relation: 'department',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'sip',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const sip = computed(() => worker.value.sip && worker.value.sip.extension);
|
||||
|
||||
function getWorkerAvatar() {
|
||||
const token = getToken();
|
||||
return `/api/Images/user/160x160/${route.params.id}/download?access_token=${token}`;
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<card-descriptor
|
||||
module="Worker"
|
||||
:url="`Workers/${entityId}`"
|
||||
:filter="filter"
|
||||
@on-fetch="(data) => (worker = data)"
|
||||
>
|
||||
<template #before>
|
||||
<q-img :src="getWorkerAvatar()" class="photo">
|
||||
<template #error>
|
||||
<div
|
||||
class="absolute-full bg-grey-10 text-center q-pa-md flex flex-center"
|
||||
>
|
||||
<div>
|
||||
<div class="text-grey-5" style="opacity: 0.4; font-size: 5vh">
|
||||
<q-icon name="vn:claims" />
|
||||
</div>
|
||||
<div class="text-grey-5" style="opacity: 0.4">
|
||||
{{ t('worker.imageNotFound') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</q-img>
|
||||
</template>
|
||||
<template #description="{ entity }">
|
||||
<span>
|
||||
{{ entity.user.nickname }}
|
||||
<q-tooltip>{{ entity.user.nickname }}</q-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<q-list>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption> {{ t('worker.card.name') }} </q-item-label>
|
||||
<q-item-label>{{ entity.user.nickname }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.card.email') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ entity.user.email }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.list.department') }}
|
||||
</q-item-label>
|
||||
<q-item-label>
|
||||
{{ entity.department.department.name }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.card.phone') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ entity.phone }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.sipExtension') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ sip }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</template>
|
||||
</card-descriptor>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.photo {
|
||||
height: 256px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,16 @@
|
|||
<script setup>
|
||||
import WorkerDescriptor from './WorkerDescriptor.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-popup-proxy>
|
||||
<WorkerDescriptor v-if="$props.id" :id="$props.id" />
|
||||
</q-popup-proxy>
|
||||
</template>
|
|
@ -0,0 +1,292 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { ref, onMounted, computed, onUpdated } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
||||
import WorkerDescriptorProxy from './WorkerDescriptorProxy.vue';
|
||||
|
||||
onMounted(() => fetch());
|
||||
onUpdated(() => fetch());
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const entityId = computed(() => $props.id || route.params.id);
|
||||
|
||||
const worker = ref(null);
|
||||
|
||||
const filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['email', 'name', 'nickname', 'roleFk'],
|
||||
include: {
|
||||
relation: 'role',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'department',
|
||||
scope: {
|
||||
include: {
|
||||
relation: 'department',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'boss',
|
||||
},
|
||||
{
|
||||
relation: 'client',
|
||||
},
|
||||
{
|
||||
relation: 'sip',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function fetch() {
|
||||
const id = entityId.value;
|
||||
axios.get(`/Workers/${id}`, { params: { filter } }).then((response) => {
|
||||
worker.value = response.data;
|
||||
});
|
||||
}
|
||||
|
||||
function sipExtension() {
|
||||
if (worker.value.sip) return worker.value.sip.extension;
|
||||
return '-';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="summary container">
|
||||
<q-card>
|
||||
<SkeletonSummary v-if="!worker" />
|
||||
<template v-if="worker">
|
||||
<div class="header bg-primary q-pa-sm q-mb-md">
|
||||
{{ worker.id }} - {{ worker.firstName }} {{ worker.lastName }}
|
||||
</div>
|
||||
<div class="row q-pa-md q-col-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-list>
|
||||
<q-item-label header class="text-h6">
|
||||
{{ t('worker.summary.basicData') }}
|
||||
</q-item-label>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption> ID </q-item-label>
|
||||
<q-item-label>{{ worker.id }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.card.name') }}
|
||||
</q-item-label>
|
||||
<q-item-label>
|
||||
{{ worker.user.nickname }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.list.department') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
worker.department.department.name
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.list.email') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ worker.user.email }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
class="items-start cursor-pointer q-hoverable"
|
||||
v-if="worker.boss"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.summary.boss') }}
|
||||
</q-item-label>
|
||||
<q-item-label>
|
||||
<span class="link">
|
||||
{{ worker.boss.name }}
|
||||
<WorkerDescriptorProxy :id="worker.bossFk" />
|
||||
</span>
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.phoneExtension') }}
|
||||
</q-item-label>
|
||||
<q-item-label>
|
||||
{{
|
||||
worker.mobileExtension == ''
|
||||
? worker.mobileExtension
|
||||
: '-'
|
||||
}}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.entPhone') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
worker.phone == '' ? worker.phone : '-'
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.personalPhone') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
worker.client.phone == ''
|
||||
? worker.client.phone
|
||||
: '-'
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-list>
|
||||
<q-item-label header class="text-h6">
|
||||
{{ t('worker.summary.userData') }}
|
||||
</q-item-label>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.summary.userId') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ worker.user.id }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.card.name') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
worker.user.nickname
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.role') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
worker.user.role.name
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.sipExtension') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ sipExtension() }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.q-card {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.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>
|
|
@ -0,0 +1,21 @@
|
|||
<script setup>
|
||||
import { useDialogPluginComponent } from 'quasar';
|
||||
import WorkerSummary from './WorkerSummary.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
defineEmits([...useDialogPluginComponent.emits]);
|
||||
|
||||
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog ref="dialogRef" @hide="onDialogHide">
|
||||
<worker-summary v-if="$props.id" :id="$props.id" />
|
||||
</q-dialog>
|
||||
</template>
|
|
@ -0,0 +1,121 @@
|
|||
<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 departments = ref();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<fetch-data url="Departments" @on-fetch="(data) => (departments = 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('First Name')"
|
||||
v-model="params.firstName"
|
||||
lazy-rules
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-input
|
||||
:label="t('Last Name')"
|
||||
v-model="params.lastName"
|
||||
lazy-rules
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-input
|
||||
:label="t('User Name')"
|
||||
v-model="params.userName"
|
||||
lazy-rules
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section v-if="!departments">
|
||||
<q-skeleton type="QInput" class="full-width" />
|
||||
</q-item-section>
|
||||
<q-item-section v-if="departments">
|
||||
<q-select
|
||||
:label="t('Department')"
|
||||
v-model="params.departmentFk"
|
||||
@update:model-value="searchFn()"
|
||||
:options="departments"
|
||||
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>
|
||||
<q-input
|
||||
:label="t('Extension')"
|
||||
v-model="params.extension"
|
||||
lazy-rules
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
params:
|
||||
search: Contains
|
||||
fi: FI
|
||||
firstName: First name
|
||||
lastName: Last name
|
||||
userName: User
|
||||
extension: Extension
|
||||
es:
|
||||
params:
|
||||
search: Contiene
|
||||
fi: NIF
|
||||
firstName: Nombre
|
||||
lastName: Apellidos
|
||||
userName: Usuario
|
||||
extension: Extensión
|
||||
FI: NIF
|
||||
First Name: Nombre
|
||||
Last Name: Apellidos
|
||||
User Name: Usuario
|
||||
Department: Departamento
|
||||
Extension: Extensión
|
||||
</i18n>
|
|
@ -0,0 +1,155 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import WorkerSummaryDialog from './Card/WorkerSummaryDialog.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
import WorkerFilter from './WorkerFilter.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const quasar = useQuasar();
|
||||
|
||||
function navigate(id) {
|
||||
router.push({ path: `/worker/${id}` });
|
||||
}
|
||||
|
||||
function viewSummary(id) {
|
||||
quasar.dialog({
|
||||
component: WorkerSummaryDialog,
|
||||
componentProps: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="WorkerList"
|
||||
:label="t('Search worker')"
|
||||
:info="t('You can search by worker id or name')"
|
||||
/>
|
||||
</Teleport>
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<WorkerFilter data-key="WorkerList" />
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page class="column items-center q-pa-md">
|
||||
<div class="card-list">
|
||||
<paginate
|
||||
data-key="WorkerList"
|
||||
url="Workers/filter"
|
||||
order="id DESC"
|
||||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card q-mb-md" 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-section class="q-pa-md" @click="navigate(row.id)">
|
||||
<q-item-label class="text-h6">
|
||||
{{ row.nickname }}
|
||||
</q-item-label>
|
||||
<q-item-label caption>#{{ row.id }}</q-item-label>
|
||||
<q-list>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.list.name') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
row.userName
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.list.email') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ row.email }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{
|
||||
t('worker.list.department')
|
||||
}}</q-item-label>
|
||||
<q-item-label>
|
||||
{{ row.department }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-item-section>
|
||||
<q-separator vertical />
|
||||
<q-card-actions vertical class="justify-between">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
color="primary"
|
||||
icon="arrow_circle_right"
|
||||
@click="navigate(row.id)"
|
||||
>
|
||||
<q-tooltip>
|
||||
{{ t('components.smartCard.openCard') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
color="grey-7"
|
||||
icon="preview"
|
||||
@click="viewSummary(row.id)"
|
||||
>
|
||||
<q-tooltip>
|
||||
{{ t('components.smartCard.openSummary') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</q-item>
|
||||
</q-card>
|
||||
</template>
|
||||
</paginate>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-list {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search worker: Buscar trabajador
|
||||
You can search by worker id or name: Puedes buscar por id o nombre del trabajador
|
||||
</i18n>
|
|
@ -0,0 +1,17 @@
|
|||
<script setup>
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import LeftMenu from 'src/components/LeftMenu.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<LeftMenu />
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page-container>
|
||||
<router-view></router-view>
|
||||
</q-page-container>
|
||||
</template>
|
|
@ -11,7 +11,7 @@ export default {
|
|||
redirect: { name: 'ClaimMain' },
|
||||
menus: {
|
||||
main: ['ClaimList', 'ClaimRmaList'],
|
||||
card: ['ClaimBasicData', 'ClaimRma', 'ClaimPhotos'],
|
||||
card: ['ClaimBasicData', 'ClaimRma', 'ClaimPhotos', 'ClaimLog'],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
|
@ -85,6 +85,15 @@ export default {
|
|||
},
|
||||
component: () => import('src/pages/Claim/Card/ClaimPhoto.vue'),
|
||||
},
|
||||
{
|
||||
name: 'ClaimLog',
|
||||
path: 'log',
|
||||
meta: {
|
||||
title: 'log',
|
||||
icon: 'history',
|
||||
},
|
||||
component: () => import('src/pages/Claim/Card/ClaimLog.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -2,10 +2,12 @@ import Customer from './customer';
|
|||
import Ticket from './ticket';
|
||||
import Claim from './claim';
|
||||
import InvoiceOut from './invoiceOut';
|
||||
import Worker from './worker';
|
||||
|
||||
export default [
|
||||
Customer,
|
||||
Ticket,
|
||||
Claim,
|
||||
InvoiceOut
|
||||
InvoiceOut,
|
||||
Worker
|
||||
]
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import { RouterView } from 'vue-router';
|
||||
|
||||
export default {
|
||||
path: '/worker',
|
||||
name: 'Worker',
|
||||
meta: {
|
||||
title: 'workers',
|
||||
icon: 'vn:worker',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'WorkerMain' },
|
||||
menus: {
|
||||
main: ['WorkerList'],
|
||||
card: [],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'WorkerMain',
|
||||
component: () => import('src/pages/Worker/WorkerMain.vue'),
|
||||
redirect: { name: 'WorkerList' },
|
||||
children: [
|
||||
{
|
||||
path: 'list',
|
||||
name: 'WorkerList',
|
||||
meta: {
|
||||
title: 'list',
|
||||
icon: 'view_list',
|
||||
},
|
||||
component: () => import('src/pages/Worker/WorkerList.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'WorkerCard',
|
||||
path: ':id',
|
||||
component: () => import('src/pages/Worker/Card/WorkerCard.vue'),
|
||||
redirect: { name: 'WorkerSummary' },
|
||||
children: [
|
||||
{
|
||||
name: 'WorkerSummary',
|
||||
path: 'summary',
|
||||
meta: {
|
||||
title: 'summary',
|
||||
},
|
||||
component: () => import('src/pages/Worker/Card/WorkerSummary.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
import customer from './modules/customer';
|
||||
import ticket from './modules/ticket';
|
||||
import claim from './modules/claim';
|
||||
import worker from './modules/worker';
|
||||
import invoiceOut from './modules/invoiceOut';
|
||||
|
||||
const routes = [
|
||||
|
@ -26,6 +27,7 @@ const routes = [
|
|||
customer,
|
||||
ticket,
|
||||
claim,
|
||||
worker,
|
||||
invoiceOut,
|
||||
{
|
||||
path: '/:catchAll(.*)*',
|
||||
|
|
|
@ -6,7 +6,7 @@ import { useRole } from 'src/composables/useRole';
|
|||
import routes from 'src/router/modules';
|
||||
|
||||
export const useNavigationStore = defineStore('navigationStore', () => {
|
||||
const modules = ['customer', 'claim', 'ticket', 'invoiceOut'];
|
||||
const modules = ['customer', 'claim', 'ticket', 'invoiceOut', 'worker'];
|
||||
const pinnedModules = ref([]);
|
||||
const role = useRole();
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
describe('WorkerList', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(1280, 720);
|
||||
cy.login('developer');
|
||||
cy.visit('/#/worker/list');
|
||||
});
|
||||
|
||||
it('should load workers', () => {
|
||||
cy.get('div[class="q-item__label text-h6"]').eq(0).should('have.text', 'Jessica Jones');
|
||||
cy.get('div[class="q-item__label text-h6"]').eq(1).should('have.text', 'Bruce Banner');
|
||||
cy.get('div[class="q-item__label text-h6"]').eq(2).should('have.text', 'Charles Xavier');
|
||||
});
|
||||
|
||||
it('should open the worker summary', () => {
|
||||
cy.get('div[class="q-item__section column q-item__section--side justify-center q-pa-md"]').eq(0).click();
|
||||
cy.get('div[class="header bg-primary q-pa-sm q-mb-md"').should('have.text', '1110 - Jessica Jones');
|
||||
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(0).should('have.text', 'Basic data');
|
||||
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(1).should('have.text', 'User data');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe('WorkerSummary', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(1280, 720)
|
||||
cy.login('developer')
|
||||
cy.visit('/#/worker/19/summary');
|
||||
});
|
||||
|
||||
it('should load worker summary', () => {
|
||||
cy.get('div[class="header bg-primary q-pa-sm q-mb-md"').should('have.text', '19 - salesBoss');
|
||||
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(0).should('have.text', 'Basic data');
|
||||
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(1).should('have.text', 'User data');
|
||||
cy.get('div[class="q-item__section column q-item__section--main justify-center"]').eq(0).should('have.text', 'NamesalesBossNick');
|
||||
});
|
||||
|
||||
});
|
|
@ -34,10 +34,17 @@ describe('App', () => {
|
|||
const response = {
|
||||
response: {
|
||||
status: 401,
|
||||
data: {
|
||||
error: {
|
||||
message: 'Invalid username or password',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(vm.responseError(response)).rejects.toEqual(expect.objectContaining(response));
|
||||
expect(vm.responseError(response)).rejects.toEqual(
|
||||
expect.objectContaining(response)
|
||||
);
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'Invalid username or password',
|
||||
|
@ -54,10 +61,17 @@ describe('App', () => {
|
|||
const response = {
|
||||
response: {
|
||||
status: 401,
|
||||
data: {
|
||||
error: {
|
||||
message: 'Access denied',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(vm.responseError(response)).rejects.toEqual(expect.objectContaining(response));
|
||||
expect(vm.responseError(response)).rejects.toEqual(
|
||||
expect.objectContaining(response)
|
||||
);
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'Access denied',
|
||||
|
|
|
@ -18,12 +18,12 @@ describe('ClaimDescriptorMenu', () => {
|
|||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('deleteClaim()', () => {
|
||||
describe('remove()', () => {
|
||||
it('should delete the claim', async () => {
|
||||
vi.spyOn(axios, 'delete').mockResolvedValue({ data: true });
|
||||
vi.spyOn(vm.quasar, 'notify');
|
||||
|
||||
await vm.deleteClaim();
|
||||
await vm.remove();
|
||||
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ type: 'positive' })
|
||||
|
|
|
@ -21,7 +21,7 @@ describe('ClaimPhoto', () => {
|
|||
beforeAll(() => {
|
||||
vm = createWrapper(ClaimPhoto, {
|
||||
global: {
|
||||
stubs: ['FetchData', 'TeleportSlot', 'vue-i18n'],
|
||||
stubs: ['FetchData', 'vue-i18n'],
|
||||
mocks: {
|
||||
fetch: vi.fn(),
|
||||
},
|
||||
|
@ -38,7 +38,7 @@ describe('ClaimPhoto', () => {
|
|||
vi.spyOn(axios, 'post').mockResolvedValue({ data: true });
|
||||
vi.spyOn(vm.quasar, 'notify');
|
||||
|
||||
await vm.deleteDms(0);
|
||||
await vm.deleteDms({ index: 0 });
|
||||
|
||||
expect(axios.post).toHaveBeenCalledWith(
|
||||
`ClaimDms/${claimMock.claimDms[0].dmsFk}/removeFile`
|
||||
|
@ -46,7 +46,6 @@ describe('ClaimPhoto', () => {
|
|||
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ type: 'positive' })
|
||||
);
|
||||
expect(vm.claimDms).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -59,8 +58,10 @@ describe('ClaimPhoto', () => {
|
|||
expect(vm.quasar.dialog).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
componentProps: {
|
||||
message: 'This file will be deleted',
|
||||
title: 'This file will be deleted',
|
||||
icon: 'delete',
|
||||
data: { index: 1 },
|
||||
promise: vm.deleteDms
|
||||
},
|
||||
})
|
||||
);
|
||||
|
@ -78,7 +79,7 @@ describe('ClaimPhoto', () => {
|
|||
contentType: 'contentType',
|
||||
},
|
||||
isVideo: false,
|
||||
url: '///api/Claims/1/downloadFile?access_token=',
|
||||
url: '/api/Claims/1/downloadFile?access_token=',
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
|
@ -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 { createTestingPinia } from '@pinia/testing';
|
||||
import { vi } from 'vitest';
|
||||
|
@ -6,7 +6,7 @@ import { i18n } from 'src/boot/i18n';
|
|||
import { Notify, Dialog } from 'quasar';
|
||||
import axios from 'axios';
|
||||
|
||||
installQuasar({
|
||||
installQuasarPlugin({
|
||||
plugins: {
|
||||
Notify,
|
||||
Dialog,
|
||||
|
|
Loading…
Reference in New Issue