Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix-front into 6175-addMicrosipLink
|
@ -13,5 +13,6 @@
|
||||||
],
|
],
|
||||||
"[vue]": {
|
"[vue]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
}
|
},
|
||||||
|
"cSpell.words": ["axios"]
|
||||||
}
|
}
|
||||||
|
|
10
CHANGELOG.md
|
@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2352.01] - 2023-12-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- (carros) => Se añade contador de carros. #6545
|
||||||
|
- (Reclamaciones) => Se añade la sección para hacer acciones sobre una reclamación. #5654
|
||||||
|
### Changed
|
||||||
|
### Fixed
|
||||||
|
- (Reclamaciones) => Se corrige el color de la barra según el tema y el evento de actualziar cantidades #6334
|
||||||
|
|
||||||
|
|
||||||
## [2253.01] - 2023-01-05
|
## [2253.01] - 2023-01-05
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "salix-front",
|
"name": "salix-front",
|
||||||
"version": "23.36.01",
|
"version": "23.40.01",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "salix-front",
|
"name": "salix-front",
|
||||||
"version": "23.36.01",
|
"version": "23.52.01",
|
||||||
"description": "Salix frontend",
|
"description": "Salix frontend",
|
||||||
"productName": "Salix",
|
"productName": "Salix",
|
||||||
"author": "Verdnatura",
|
"author": "Verdnatura",
|
||||||
|
@ -9,13 +9,13 @@
|
||||||
"lint": "eslint --ext .js,.vue ./",
|
"lint": "eslint --ext .js,.vue ./",
|
||||||
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
||||||
"test:e2e": "cypress open",
|
"test:e2e": "cypress open",
|
||||||
"test:e2e:ci": "cypress run --browser chromium",
|
"test:e2e:ci": "cd ../salix && gulp docker && cd ../salix-front && cypress run --browser chromium",
|
||||||
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
|
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
|
||||||
"test:unit": "vitest",
|
"test:unit": "vitest",
|
||||||
"test:unit:ci": "vitest run"
|
"test:unit:ci": "vitest run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quasar/cli": "^2.2.1",
|
"@quasar/cli": "^2.3.0",
|
||||||
"@quasar/extras": "^1.16.4",
|
"@quasar/extras": "^1.16.4",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"chromium": "^3.0.3",
|
"chromium": "^3.0.3",
|
||||||
|
|
|
@ -66,7 +66,9 @@ module.exports = configure(function (/* ctx */) {
|
||||||
// publicPath: '/',
|
// publicPath: '/',
|
||||||
// analyze: true,
|
// analyze: true,
|
||||||
// env: {},
|
// env: {},
|
||||||
// rawDefine: {}
|
rawDefine: {
|
||||||
|
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
|
||||||
|
},
|
||||||
// ignorePublicFolder: true,
|
// ignorePublicFolder: true,
|
||||||
// minify: false,
|
// minify: false,
|
||||||
// polyfillModulePreload: true,
|
// polyfillModulePreload: true,
|
||||||
|
@ -89,11 +91,12 @@ module.exports = configure(function (/* ctx */) {
|
||||||
|
|
||||||
vitePlugins: [
|
vitePlugins: [
|
||||||
[
|
[
|
||||||
VueI18nPlugin,
|
VueI18nPlugin({
|
||||||
|
runtimeOnly: false
|
||||||
|
}),
|
||||||
{
|
{
|
||||||
// if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false`
|
// if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false`
|
||||||
// compositionOnly: false,
|
// compositionOnly: false,
|
||||||
|
|
||||||
// you need to set i18n resource including paths !
|
// you need to set i18n resource including paths !
|
||||||
include: path.resolve(__dirname, './src/i18n/**'),
|
include: path.resolve(__dirname, './src/i18n/**'),
|
||||||
},
|
},
|
||||||
|
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 400 168.6" style="enable-background:new 0 0 400 168.6;" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#3D3D3F;}
|
||||||
|
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#8EBB27;}
|
||||||
|
.st2{fill:#8EBB27;}
|
||||||
|
.st3{fill:#F19300;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M106.1,40L92.3,0h10.9l5.6,20.6l0.5,1.7c0.7,2.5,1.2,4.5,1.6,6.2c0.2-0.8,0.4-1.8,0.7-2.9 c0.3-1.1,0.7-2.6,1.2-4.3L118.7,0h10.8l-13.9,40H106.1z" style="fill: rgb(255, 255, 255);"/>
|
||||||
|
<path class="st1" d="M386.1,40h-9.8c0-0.5,0.1-1,0.1-1.5l0.2-1.6c-1.7,1.4-3.5,2.4-5.2,3c-1.7,0.6-3.5,1-5.3,1 c-2.8,0-4.9-0.8-6.1-2.3c-1.2-1.6-1.5-3.7-0.7-6.3c0.7-2.4,1.9-4.4,3.6-6c1.7-1.5,4-2.6,6.8-3.2c1.5-0.3,3.5-0.7,5.8-1.1 c3.5-0.5,5.4-1.3,5.7-2.4l0.2-0.7c0.2-0.9,0.1-1.5-0.4-2c-0.5-0.4-1.4-0.7-2.7-0.7c-1.4,0-2.6,0.3-3.5,0.8c-1,0.6-1.7,1.4-2.2,2.5 h-8.9c1.4-3.3,3.5-5.8,6.2-7.5c2.7-1.6,6.2-2.4,10.5-2.4c2.6,0,4.7,0.3,6.4,1c1.6,0.6,2.8,1.6,3.4,2.9c0.4,0.9,0.6,2,0.6,3.3 c-0.1,1.3-0.5,3.3-1.3,6.2l-3.1,11.2c-0.4,1.3-0.5,2.4-0.5,3.2c0,0.8,0.2,1.3,0.7,1.5L386.1,40z M379.4,26.1 c-0.9,0.5-2.3,0.9-4.3,1.3c-1,0.2-1.7,0.3-2.2,0.5c-1.3,0.3-2.2,0.7-2.8,1.2c-0.6,0.5-1.1,1.2-1.3,2c-0.3,1.1-0.2,1.9,0.3,2.5 c0.5,0.6,1.2,1,2.3,1c1.7,0,3.1-0.5,4.4-1.4c1.3-1,2.2-2.2,2.6-3.7L379.4,26.1z"/>
|
||||||
|
<path class="st1" d="M337.3,40l8.3-29.5h9.3l-1.4,5.2c1.6-2,3.3-3.5,5.1-4.4c1.8-0.9,3.9-1.4,6.3-1.5l-2.7,9.6 c-0.4-0.1-0.8-0.1-1.2-0.1c-0.4,0-0.8,0-1.1,0c-1.5,0-2.8,0.2-3.9,0.7c-1.1,0.4-2.1,1.1-2.9,2.1c-0.5,0.6-1,1.5-1.5,2.6 c-0.5,1.1-1.1,3-1.8,5.6l-2.8,9.9H337.3z"/>
|
||||||
|
<path class="st1" d="M340.8,10.5L332.5,40h-9.5l1.1-4.1c-1.6,1.6-3.3,2.9-4.9,3.6c-1.7,0.8-3.5,1.2-5.4,1.2 c-3.3,0-5.5-0.8-6.7-2.5c-1.2-1.7-1.3-4.2-0.4-7.4l5.7-20.3h9.7L317.6,27c-0.7,2.4-0.8,4.1-0.5,5c0.4,0.9,1.3,1.4,2.8,1.4 c1.7,0,3.1-0.6,4.1-1.7c1.1-1.1,2-2.9,2.7-5.5l4.4-15.8H340.8z"/>
|
||||||
|
<path class="st1" d="M290.1,16.3l1.6-5.8h4l2.3-8.3h9.7l-2.3,8.3h5l-1.6,5.8h-5l-3.6,12.8c-0.5,2-0.7,3.3-0.3,3.9 c0.3,0.6,1.2,1,2.6,1l0.7,0l0.5,0l-1.7,6.2c-1.1,0.2-2.1,0.3-3.1,0.5c-1,0.1-2,0.2-2.9,0.2c-3.4,0-5.4-0.8-6.2-2.5 c-0.8-1.6-0.4-5.1,1.1-10.5l3.2-11.4H290.1z"/>
|
||||||
|
<path class="st1" d="M283.5,40h-9.8c0-0.5,0.1-1,0.1-1.5L274,37c-1.7,1.4-3.5,2.4-5.2,3c-1.7,0.6-3.5,1-5.3,1 c-2.8,0-4.9-0.8-6.1-2.3c-1.2-1.6-1.5-3.7-0.7-6.3c0.7-2.4,1.9-4.4,3.6-6c1.7-1.5,4-2.6,6.8-3.2c1.5-0.3,3.5-0.7,5.8-1.1 c3.5-0.5,5.4-1.3,5.7-2.4l0.2-0.7c0.2-0.9,0.1-1.5-0.4-2c-0.5-0.4-1.4-0.7-2.7-0.7c-1.4,0-2.6,0.3-3.5,0.8c-1,0.6-1.7,1.4-2.2,2.5 H261c1.4-3.3,3.5-5.8,6.2-7.5c2.7-1.6,6.2-2.4,10.5-2.4c2.6,0,4.7,0.3,6.4,1c1.6,0.6,2.8,1.6,3.4,2.9c0.4,0.9,0.6,2,0.6,3.3 c-0.1,1.3-0.5,3.3-1.3,6.2l-3.1,11.2c-0.4,1.3-0.5,2.4-0.5,3.2c0,0.8,0.2,1.3,0.7,1.5L283.5,40z M276.7,26.1 c-0.9,0.5-2.3,0.9-4.3,1.3c-1,0.2-1.7,0.3-2.2,0.5c-1.3,0.3-2.2,0.7-2.8,1.2c-0.6,0.5-1.1,1.2-1.3,2c-0.3,1.1-0.2,1.9,0.3,2.5 c0.5,0.6,1.2,1,2.3,1c1.7,0,3.1-0.5,4.4-1.4c1.3-1,2.2-2.2,2.6-3.7L276.7,26.1z"/>
|
||||||
|
<path class="st0" d="M219.6,0l-11.2,40h-9.7l1.1-3.9c-1.5,1.6-3.1,2.8-4.8,3.6c-1.6,0.8-3.4,1.2-5.3,1.2c-3.7,0-6.3-1.4-7.8-4.3 c-1.5-2.9-1.6-6.6-0.3-11.2c1.3-4.7,3.5-8.4,6.7-11.4c3.1-2.9,6.5-4.4,10.1-4.4c1.9,0,3.6,0.4,4.8,1.2c1.3,0.8,2.2,1.9,2.8,3.5 L210,0H219.6z M189.8,24.9c-0.7,2.6-0.8,4.7-0.2,6.1c0.6,1.4,1.8,2.1,3.7,2.1c1.8,0,3.4-0.7,4.8-2.1c1.3-1.4,2.4-3.4,3.1-6.1 c0.7-2.5,0.7-4.4,0.1-5.8c-0.6-1.4-1.8-2-3.7-2c-1.7,0-3.3,0.7-4.7,2.1C191.5,20.6,190.4,22.5,189.8,24.9z" style="fill: rgb(255, 255, 255);"/>
|
||||||
|
<path class="st0" d="M153.6,40l8.3-29.5h9.3l-1.4,5.2c1.6-2,3.3-3.5,5.1-4.4c1.8-0.9,7.9-1.4,10.3-1.5l-2.7,9.6 c-0.4-0.1-0.8-0.1-1.2-0.1c-0.4,0-0.8,0-1.1,0c-1.5,0-6.8,0.2-7.9,0.7c-1.1,0.4-2.1,1.1-2.9,2.1c-0.5,0.6-1,1.5-1.5,2.6 c-0.5,1.1-1.1,3-1.8,5.6l-2.8,9.9H153.6z" style="fill: rgb(255, 255, 255);"/>
|
||||||
|
<path class="st0" d="M143.5,30.7h9.3c-1.8,3.2-4.2,5.7-7.2,7.5c-3,1.8-6.4,2.7-10.2,2.7c-4.6,0-7.8-1.4-9.7-4.2 c-1.9-2.8-2.2-6.6-0.8-11.4c1.4-4.9,3.8-8.8,7.3-11.6c3.5-2.9,7.5-4.3,12-4.3c4.7,0,8,1.5,9.8,4.3c1.9,2.9,2.1,6.9,0.7,12 l-0.3,1.1l-0.2,0.6h-20c-0.6,2.1-0.6,3.7,0,4.8c0.6,1.1,1.8,1.6,3.5,1.6c1.3,0,2.4-0.3,3.4-0.8C142.1,32.6,142.9,31.8,143.5,30.7z M135.4,22.1l11,0c0.5-1.9,0.4-3.4-0.3-4.4c-0.7-1.1-1.8-1.6-3.5-1.6c-1.6,0-3,0.5-4.3,1.6C137.1,18.6,136.1,20.1,135.4,22.1z" style="fill: rgb(255, 255, 255);"/>
|
||||||
|
<path class="st2" d="M241.2,40.4l-8.4-24.6l-8.5,24.6h-9.6l12.6-40h10.8L244,21l0.5,1.7c0.7,2.5,1.2,4.5,1.6,6.2l0.7-2.9 c0.3-1.1,0.7-2.6,1.2-4.3l5.9-21.2h10.8l-13.9,40H241.2z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st3" d="M106.1,54.4h4.8l48.9,113.9h-5.9L137,129H79.9l-16.8,39.3H57L106.1,54.4z M135.3,124.2l-26.8-62.7l-26.9,62.7 H135.3z"/>
|
||||||
|
<path class="st3" d="M178.1,168.3V54.4h5.6v108.7h69.8v5.1H178.1z"/>
|
||||||
|
<path class="st3" d="M271.1,168.3V54.4h5.6v113.9H271.1z"/>
|
||||||
|
<path class="st3" d="M300.2,54.4l42,53.6l42-53.6h6.4l-45.4,57.7l44.1,56.1H383l-40.7-52l-40.7,52h-6.7l44.1-56.1l-45.4-57.7 H300.2z"/>
|
||||||
|
<g>
|
||||||
|
<path class="st3" d="M5.8,168.3L5.3,163l0.2,2.7L5.3,163c0.4,0,10.4-1.1,18.9-11.8c10.5-13.1,14.1-35.2,10.5-63.9 C31,57.7,35.4,34.8,47.6,19.1C60.3,3,76.6,0.9,77.3,0.8l0.6,5.3c-0.1,0-11.9,1.6-22.4,12.1c-14,14-19.3,37.7-15.5,68.4 c3.8,30.7-0.1,53.6-11.8,68.1C18.3,167.1,6.3,168.2,5.8,168.3z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
@ -0,0 +1,158 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="226.229px"
|
||||||
|
height="31.038px"
|
||||||
|
viewBox="0 0 226.229 31.038"
|
||||||
|
enable-background="new 0 0 226.229 31.038"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
|
||||||
|
sodipodi:docname="logo.svg"><metadata
|
||||||
|
id="metadata61"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs59">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1016"
|
||||||
|
id="namedview57"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="4.8159974"
|
||||||
|
inkscape:cx="90.91814"
|
||||||
|
inkscape:cy="16.509992"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2"
|
||||||
|
inkscape:document-rotation="0" />
|
||||||
|
<g
|
||||||
|
id="Background">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Guides">
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M 10.417,30.321 0,0 h 8.233 l 4.26,15.582 0.349,1.276 c 0.521,1.866 0.918,3.431 1.191,4.693 0.15,-0.618 0.335,-1.345 0.555,-2.182 0.219,-0.837 0.528,-1.935 0.925,-3.293 L 19.981,0 h 8.19 l -10.5,30.321 z"
|
||||||
|
id="path11"
|
||||||
|
style="fill:#1a1a1a;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 139.809,19.787 c -0.665,0.357 -1.748,0.686 -3.25,0.988 -0.727,0.137 -1.283,0.254 -1.667,0.35 -0.95,0.247 -1.661,0.563 -2.134,0.947 -0.472,0.384 -0.799,0.899 -0.979,1.544 -0.223,0.796 -0.155,1.438 0.204,1.925 0.359,0.488 0.945,0.731 1.757,0.731 1.252,0 2.375,-0.36 3.369,-1.081 0.994,-0.721 1.653,-1.665 1.98,-2.831 z m 5.106,10.534 h -7.458 c 0.017,-0.356 0.048,-0.726 0.094,-1.11 l 0.159,-1.192 c -1.318,1.026 -2.627,1.786 -3.927,2.279 -1.299,0.493 -2.643,0.739 -4.031,0.739 -2.158,0 -3.7,-0.593 -4.625,-1.779 -0.925,-1.187 -1.106,-2.788 -0.542,-4.804 0.519,-1.851 1.431,-3.356 2.737,-4.515 1.307,-1.159 3.021,-1.972 5.142,-2.438 1.169,-0.247 2.641,-0.515 4.413,-0.803 2.646,-0.412 4.082,-1.016 4.304,-1.812 l 0.151,-0.539 c 0.182,-0.65 0.076,-1.145 -0.317,-1.483 -0.393,-0.339 -1.071,-0.508 -2.033,-0.508 -1.045,0 -1.934,0.214 -2.666,0.643 -0.731,0.428 -1.289,1.058 -1.673,1.887 h -6.748 c 1.065,-2.53 2.64,-4.413 4.723,-5.65 2.083,-1.237 4.724,-1.856 7.923,-1.856 1.991,0 3.602,0.241 4.833,0.722 1.231,0.481 2.095,1.209 2.59,2.185 0.339,0.701 0.483,1.536 0.432,2.504 -0.052,0.969 -0.377,2.525 -0.978,4.669 l -2.375,8.483 c -0.284,1.014 -0.416,1.812 -0.396,2.395 0.02,0.583 0.188,0.962 0.503,1.141 z"
|
||||||
|
id="path15"
|
||||||
|
style="fill:#97d700;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 185.7,30.321 6.27,-22.393 h 7.049 l -1.097,3.918 c 1.213,-1.537 2.502,-2.659 3.867,-3.366 1.365,-0.707 2.951,-1.074 4.758,-1.101 l -2.03,7.25 c -0.304,-0.042 -0.608,-0.072 -0.912,-0.093 -0.303,-0.02 -0.592,-0.03 -0.867,-0.03 -1.126,0 -2.104,0.168 -2.932,0.504 -0.829,0.336 -1.561,0.854 -2.197,1.555 -0.406,0.467 -0.789,1.136 -1.149,2.007 -0.361,0.872 -0.814,2.282 -1.359,4.232 l -2.104,7.516 H 185.7 Z"
|
||||||
|
id="path19"
|
||||||
|
style="fill:#97d700;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 217.631,19.787 c -0.664,0.357 -1.748,0.686 -3.25,0.988 -0.727,0.137 -1.282,0.254 -1.667,0.35 -0.95,0.247 -1.661,0.563 -2.134,0.947 -0.472,0.384 -0.799,0.899 -0.979,1.544 -0.223,0.796 -0.155,1.438 0.205,1.925 0.359,0.488 0.945,0.731 1.757,0.731 1.252,0 2.375,-0.36 3.369,-1.081 0.994,-0.721 1.654,-1.665 1.98,-2.831 z m 5.106,10.534 h -7.458 c 0.017,-0.356 0.048,-0.726 0.094,-1.11 l 0.159,-1.192 c -1.318,1.026 -2.627,1.786 -3.927,2.279 -1.299,0.493 -2.643,0.739 -4.031,0.739 -2.158,0 -3.7,-0.593 -4.625,-1.779 -0.926,-1.187 -1.106,-2.788 -0.542,-4.804 0.519,-1.851 1.431,-3.356 2.737,-4.515 1.306,-1.159 3.02,-1.972 5.142,-2.438 1.169,-0.247 2.641,-0.515 4.413,-0.803 2.647,-0.412 4.082,-1.016 4.304,-1.812 l 0.151,-0.539 c 0.182,-0.65 0.077,-1.145 -0.317,-1.483 -0.393,-0.339 -1.071,-0.508 -2.033,-0.508 -1.045,0 -1.934,0.214 -2.666,0.643 -0.731,0.428 -1.289,1.058 -1.672,1.887 h -6.748 c 1.065,-2.53 2.64,-4.413 4.723,-5.65 2.083,-1.237 4.724,-1.856 7.923,-1.856 1.99,0 3.601,0.241 4.833,0.722 1.232,0.481 2.095,1.209 2.591,2.185 0.339,0.701 0.483,1.536 0.431,2.504 -0.051,0.969 -0.377,2.525 -0.978,4.669 l -2.375,8.483 c -0.284,1.014 -0.416,1.812 -0.396,2.395 0.02,0.583 0.188,0.962 0.503,1.141 z"
|
||||||
|
id="path23"
|
||||||
|
style="fill:#97d700;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 188.386,7.928 -6.269,22.393 h -7.174 l 0.864,-3.085 c -1.227,1.246 -2.476,2.163 -3.746,2.751 -1.27,0.588 -2.625,0.882 -4.067,0.882 -2.471,0 -4.154,-0.634 -5.048,-1.901 -0.895,-1.268 -0.993,-3.149 -0.294,-5.644 l 4.31,-15.396 h 7.338 l -3.508,12.53 c -0.516,1.842 -0.641,3.109 -0.375,3.803 0.266,0.694 0.967,1.041 2.105,1.041 1.275,0 2.323,-0.422 3.142,-1.267 0.819,-0.845 1.497,-2.223 2.031,-4.133 l 3.353,-11.974 z"
|
||||||
|
id="path27"
|
||||||
|
style="fill:#97d700;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 149.937,12.356 1.239,-4.428 h 2.995 l 1.771,-6.326 h 7.338 l -1.771,6.326 h 3.753 l -1.24,4.428 h -3.753 l -2.716,9.702 c -0.416,1.483 -0.498,2.465 -0.247,2.946 0.25,0.48 0.905,0.721 1.964,0.721 l 0.549,-0.011 0.39,-0.031 -1.31,4.678 c -0.811,0.148 -1.596,0.263 -2.354,0.344 -0.758,0.081 -1.48,0.122 -2.167,0.122 -2.543,0 -4.108,-0.621 -4.695,-1.863 -0.587,-1.242 -0.313,-3.887 0.82,-7.936 l 2.428,-8.672 z"
|
||||||
|
id="path31"
|
||||||
|
style="fill:#97d700;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#ffffff"
|
||||||
|
d="m 73.875,18.896 c -0.561,2.004 -0.616,3.537 -0.167,4.601 0.449,1.064 1.375,1.595 2.774,1.595 1.399,0 2.605,-0.524 3.62,-1.574 1.015,-1.05 1.806,-2.59 2.375,-4.622 0.526,-1.879 0.556,-3.334 0.09,-4.363 -0.466,-1.029 -1.393,-1.543 -2.778,-1.543 -1.304,0 -2.487,0.528 -3.551,1.585 -1.064,1.057 -1.852,2.496 -2.363,4.321 z M 96.513,0 88.024,30.321 h -7.337 l 0.824,-2.944 c -1.166,1.22 -2.369,2.121 -3.61,2.703 -1.241,0.582 -2.583,0.874 -4.025,0.874 -2.802,0 -4.772,-1.081 -5.912,-3.243 -1.139,-2.162 -1.218,-4.993 -0.238,-8.493 0.988,-3.528 2.668,-6.404 5.042,-8.627 2.374,-2.224 4.927,-3.336 7.661,-3.336 1.47,0 2.695,0.296 3.676,0.887 0.981,0.591 1.681,1.465 2.099,2.62 L 89.217,0 Z"
|
||||||
|
id="path35" /><g
|
||||||
|
id="g37">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="m 73.875,18.896 c -0.561,2.004 -0.616,3.537 -0.167,4.601 0.449,1.064 1.375,1.595 2.774,1.595 1.399,0 2.605,-0.524 3.62,-1.574 1.015,-1.05 1.806,-2.59 2.375,-4.622 0.526,-1.879 0.556,-3.334 0.09,-4.363 -0.466,-1.029 -1.393,-1.543 -2.778,-1.543 -1.304,0 -2.487,0.528 -3.551,1.585 -1.064,1.057 -1.852,2.496 -2.363,4.321 z M 96.513,0 88.024,30.321 h -7.337 l 0.824,-2.944 c -1.166,1.22 -2.369,2.121 -3.61,2.703 -1.241,0.582 -2.583,0.874 -4.025,0.874 -2.802,0 -4.772,-1.081 -5.912,-3.243 -1.139,-2.162 -1.218,-4.993 -0.238,-8.493 0.988,-3.528 2.668,-6.404 5.042,-8.627 2.374,-2.224 4.927,-3.336 7.661,-3.336 1.47,0 2.695,0.296 3.676,0.887 0.981,0.591 1.681,1.465 2.099,2.62 L 89.217,0 Z"
|
||||||
|
id="path39"
|
||||||
|
style="fill:#1a1a1a;fill-opacity:1" />
|
||||||
|
</g><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M 46.488,30.321 52.757,7.928 h 7.049 l -1.098,3.918 C 59.921,10.309 61.21,9.187 62.576,8.48 63.942,7.773 68.591,7.406 70.398,7.379 l -2.03,7.25 c -0.304,-0.042 -0.608,-0.072 -0.911,-0.093 -0.304,-0.02 -0.592,-0.03 -0.867,-0.03 -1.126,0 -5.167,0.168 -5.997,0.504 -0.829,0.336 -1.561,0.854 -2.196,1.555 -0.406,0.467 -0.789,1.136 -1.149,2.007 -0.361,0.872 -0.814,2.282 -1.36,4.232 l -2.104,7.516 h -7.296 z"
|
||||||
|
id="path43"
|
||||||
|
style="fill:#1a1a1a;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#ffffff"
|
||||||
|
d="m 32.673,16.742 8.351,-0.021 c 0.375,-1.436 0.308,-2.558 -0.201,-3.365 -0.509,-0.807 -1.402,-1.211 -2.68,-1.211 -1.209,0 -2.285,0.397 -3.229,1.19 -0.944,0.793 -1.69,1.93 -2.241,3.407 z m 6.144,6.536 h 7.043 c -1.347,2.456 -3.172,4.356 -5.477,5.7 -2.305,1.345 -4.885,2.017 -7.74,2.017 -3.473,0 -5.923,-1.054 -7.351,-3.161 -1.427,-2.107 -1.632,-4.98 -0.613,-8.618 1.038,-3.707 2.875,-6.641 5.512,-8.803 2.637,-2.163 5.678,-3.244 9.123,-3.244 3.555,0 6.04,1.099 7.456,3.298 1.417,2.198 1.582,5.234 0.498,9.109 l -0.239,0.814 -0.167,0.484 H 31.721 c -0.441,1.575 -0.438,2.777 0.01,3.606 0.448,0.829 1.332,1.244 2.65,1.244 0.975,0 1.836,-0.206 2.583,-0.617 0.747,-0.411 1.366,-1.021 1.853,-1.829 z"
|
||||||
|
id="path47" /><g
|
||||||
|
id="g49"
|
||||||
|
style="fill:#1a1a1a;fill-opacity:1">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="m 32.673,16.742 8.351,-0.021 c 0.375,-1.436 0.308,-2.558 -0.201,-3.365 -0.509,-0.807 -1.402,-1.211 -2.68,-1.211 -1.209,0 -2.285,0.397 -3.229,1.19 -0.944,0.793 -1.69,1.93 -2.241,3.407 z m 6.144,6.536 h 7.043 c -1.347,2.456 -3.172,4.356 -5.477,5.7 -2.305,1.345 -4.885,2.017 -7.74,2.017 -3.473,0 -5.923,-1.054 -7.351,-3.161 -1.427,-2.107 -1.632,-4.98 -0.613,-8.618 1.038,-3.707 2.875,-6.641 5.512,-8.803 2.637,-2.163 5.678,-3.244 9.123,-3.244 3.555,0 6.04,1.099 7.456,3.298 1.417,2.198 1.582,5.234 0.498,9.109 l -0.239,0.814 -0.167,0.484 H 31.721 c -0.441,1.575 -0.438,2.777 0.01,3.606 0.448,0.829 1.332,1.244 2.65,1.244 0.975,0 1.836,-0.206 2.583,-0.617 0.747,-0.411 1.366,-1.021 1.853,-1.829 z"
|
||||||
|
id="path51"
|
||||||
|
style="fill:#1a1a1a;fill-opacity:1" />
|
||||||
|
</g><path
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 112.881,30.643 -6.404,-18.639 -6.455,18.639 h -7.254 l 9.565,-30.321 h 8.19 l 4.434,15.582 0.35,1.276 c 0.521,1.866 0.917,3.431 1.191,4.693 l 0.555,-2.182 c 0.219,-0.837 0.528,-1.935 0.925,-3.293 l 4.468,-16.076 h 8.19 l -10.501,30.321 z"
|
||||||
|
id="path55"
|
||||||
|
style="fill:#97d700;fill-opacity:1" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,161 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="226.229px"
|
||||||
|
height="31.038px"
|
||||||
|
viewBox="0 0 226.229 31.038"
|
||||||
|
enable-background="new 0 0 226.229 31.038"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
|
||||||
|
sodipodi:docname="logo-dark.svg"><metadata
|
||||||
|
id="metadata61"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs59">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</defs><sodipodi:namedview
|
||||||
|
pagecolor="#1a1a1a"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1016"
|
||||||
|
id="namedview57"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="3.4054244"
|
||||||
|
inkscape:cx="112.21891"
|
||||||
|
inkscape:cy="27.15689"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2"
|
||||||
|
inkscape:document-rotation="0" />
|
||||||
|
<g
|
||||||
|
id="Background">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Guides">
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M 10.417,30.321 0,0 h 8.233 l 4.26,15.582 0.349,1.276 c 0.521,1.866 0.918,3.431 1.191,4.693 0.15,-0.618 0.335,-1.345 0.555,-2.182 0.219,-0.837 0.528,-1.935 0.925,-3.293 L 19.981,0 h 8.19 l -10.5,30.321 z"
|
||||||
|
id="path11"
|
||||||
|
style="fill:#ffffff;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 139.809,19.787 c -0.665,0.357 -1.748,0.686 -3.25,0.988 -0.727,0.137 -1.283,0.254 -1.667,0.35 -0.95,0.247 -1.661,0.563 -2.134,0.947 -0.472,0.384 -0.799,0.899 -0.979,1.544 -0.223,0.796 -0.155,1.438 0.204,1.925 0.359,0.488 0.945,0.731 1.757,0.731 1.252,0 2.375,-0.36 3.369,-1.081 0.994,-0.721 1.653,-1.665 1.98,-2.831 z m 5.106,10.534 h -7.458 c 0.017,-0.356 0.048,-0.726 0.094,-1.11 l 0.159,-1.192 c -1.318,1.026 -2.627,1.786 -3.927,2.279 -1.299,0.493 -2.643,0.739 -4.031,0.739 -2.158,0 -3.7,-0.593 -4.625,-1.779 -0.925,-1.187 -1.106,-2.788 -0.542,-4.804 0.519,-1.851 1.431,-3.356 2.737,-4.515 1.307,-1.159 3.021,-1.972 5.142,-2.438 1.169,-0.247 2.641,-0.515 4.413,-0.803 2.646,-0.412 4.082,-1.016 4.304,-1.812 l 0.151,-0.539 c 0.182,-0.65 0.076,-1.145 -0.317,-1.483 -0.393,-0.339 -1.071,-0.508 -2.033,-0.508 -1.045,0 -1.934,0.214 -2.666,0.643 -0.731,0.428 -1.289,1.058 -1.673,1.887 h -6.748 c 1.065,-2.53 2.64,-4.413 4.723,-5.65 2.083,-1.237 4.724,-1.856 7.923,-1.856 1.991,0 3.602,0.241 4.833,0.722 1.231,0.481 2.095,1.209 2.59,2.185 0.339,0.701 0.483,1.536 0.432,2.504 -0.052,0.969 -0.377,2.525 -0.978,4.669 l -2.375,8.483 c -0.284,1.014 -0.416,1.812 -0.396,2.395 0.02,0.583 0.188,0.962 0.503,1.141 z"
|
||||||
|
id="path15"
|
||||||
|
style="fill:#97d700;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 185.7,30.321 6.27,-22.393 h 7.049 l -1.097,3.918 c 1.213,-1.537 2.502,-2.659 3.867,-3.366 1.365,-0.707 2.951,-1.074 4.758,-1.101 l -2.03,7.25 c -0.304,-0.042 -0.608,-0.072 -0.912,-0.093 -0.303,-0.02 -0.592,-0.03 -0.867,-0.03 -1.126,0 -2.104,0.168 -2.932,0.504 -0.829,0.336 -1.561,0.854 -2.197,1.555 -0.406,0.467 -0.789,1.136 -1.149,2.007 -0.361,0.872 -0.814,2.282 -1.359,4.232 l -2.104,7.516 H 185.7 Z"
|
||||||
|
id="path19"
|
||||||
|
style="fill:#97d700;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 217.631,19.787 c -0.664,0.357 -1.748,0.686 -3.25,0.988 -0.727,0.137 -1.282,0.254 -1.667,0.35 -0.95,0.247 -1.661,0.563 -2.134,0.947 -0.472,0.384 -0.799,0.899 -0.979,1.544 -0.223,0.796 -0.155,1.438 0.205,1.925 0.359,0.488 0.945,0.731 1.757,0.731 1.252,0 2.375,-0.36 3.369,-1.081 0.994,-0.721 1.654,-1.665 1.98,-2.831 z m 5.106,10.534 h -7.458 c 0.017,-0.356 0.048,-0.726 0.094,-1.11 l 0.159,-1.192 c -1.318,1.026 -2.627,1.786 -3.927,2.279 -1.299,0.493 -2.643,0.739 -4.031,0.739 -2.158,0 -3.7,-0.593 -4.625,-1.779 -0.926,-1.187 -1.106,-2.788 -0.542,-4.804 0.519,-1.851 1.431,-3.356 2.737,-4.515 1.306,-1.159 3.02,-1.972 5.142,-2.438 1.169,-0.247 2.641,-0.515 4.413,-0.803 2.647,-0.412 4.082,-1.016 4.304,-1.812 l 0.151,-0.539 c 0.182,-0.65 0.077,-1.145 -0.317,-1.483 -0.393,-0.339 -1.071,-0.508 -2.033,-0.508 -1.045,0 -1.934,0.214 -2.666,0.643 -0.731,0.428 -1.289,1.058 -1.672,1.887 h -6.748 c 1.065,-2.53 2.64,-4.413 4.723,-5.65 2.083,-1.237 4.724,-1.856 7.923,-1.856 1.99,0 3.601,0.241 4.833,0.722 1.232,0.481 2.095,1.209 2.591,2.185 0.339,0.701 0.483,1.536 0.431,2.504 -0.051,0.969 -0.377,2.525 -0.978,4.669 l -2.375,8.483 c -0.284,1.014 -0.416,1.812 -0.396,2.395 0.02,0.583 0.188,0.962 0.503,1.141 z"
|
||||||
|
id="path23"
|
||||||
|
style="fill:#97d700;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 188.386,7.928 -6.269,22.393 h -7.174 l 0.864,-3.085 c -1.227,1.246 -2.476,2.163 -3.746,2.751 -1.27,0.588 -2.625,0.882 -4.067,0.882 -2.471,0 -4.154,-0.634 -5.048,-1.901 -0.895,-1.268 -0.993,-3.149 -0.294,-5.644 l 4.31,-15.396 h 7.338 l -3.508,12.53 c -0.516,1.842 -0.641,3.109 -0.375,3.803 0.266,0.694 0.967,1.041 2.105,1.041 1.275,0 2.323,-0.422 3.142,-1.267 0.819,-0.845 1.497,-2.223 2.031,-4.133 l 3.353,-11.974 z"
|
||||||
|
id="path27"
|
||||||
|
style="fill:#97d700;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 149.937,12.356 1.239,-4.428 h 2.995 l 1.771,-6.326 h 7.338 l -1.771,6.326 h 3.753 l -1.24,4.428 h -3.753 l -2.716,9.702 c -0.416,1.483 -0.498,2.465 -0.247,2.946 0.25,0.48 0.905,0.721 1.964,0.721 l 0.549,-0.011 0.39,-0.031 -1.31,4.678 c -0.811,0.148 -1.596,0.263 -2.354,0.344 -0.758,0.081 -1.48,0.122 -2.167,0.122 -2.543,0 -4.108,-0.621 -4.695,-1.863 -0.587,-1.242 -0.313,-3.887 0.82,-7.936 l 2.428,-8.672 z"
|
||||||
|
id="path31"
|
||||||
|
style="fill:#97d700;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#ffffff"
|
||||||
|
d="m 73.875,18.896 c -0.561,2.004 -0.616,3.537 -0.167,4.601 0.449,1.064 1.375,1.595 2.774,1.595 1.399,0 2.605,-0.524 3.62,-1.574 1.015,-1.05 1.806,-2.59 2.375,-4.622 0.526,-1.879 0.556,-3.334 0.09,-4.363 -0.466,-1.029 -1.393,-1.543 -2.778,-1.543 -1.304,0 -2.487,0.528 -3.551,1.585 -1.064,1.057 -1.852,2.496 -2.363,4.321 z M 96.513,0 88.024,30.321 h -7.337 l 0.824,-2.944 c -1.166,1.22 -2.369,2.121 -3.61,2.703 -1.241,0.582 -2.583,0.874 -4.025,0.874 -2.802,0 -4.772,-1.081 -5.912,-3.243 -1.139,-2.162 -1.218,-4.993 -0.238,-8.493 0.988,-3.528 2.668,-6.404 5.042,-8.627 2.374,-2.224 4.927,-3.336 7.661,-3.336 1.47,0 2.695,0.296 3.676,0.887 0.981,0.591 1.681,1.465 2.099,2.62 L 89.217,0 Z"
|
||||||
|
id="path35"
|
||||||
|
style="fill:#ffffff;fill-opacity:1" /><g
|
||||||
|
id="g37"
|
||||||
|
style="fill:#ffffff;fill-opacity:1">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="m 73.875,18.896 c -0.561,2.004 -0.616,3.537 -0.167,4.601 0.449,1.064 1.375,1.595 2.774,1.595 1.399,0 2.605,-0.524 3.62,-1.574 1.015,-1.05 1.806,-2.59 2.375,-4.622 0.526,-1.879 0.556,-3.334 0.09,-4.363 -0.466,-1.029 -1.393,-1.543 -2.778,-1.543 -1.304,0 -2.487,0.528 -3.551,1.585 -1.064,1.057 -1.852,2.496 -2.363,4.321 z M 96.513,0 88.024,30.321 h -7.337 l 0.824,-2.944 c -1.166,1.22 -2.369,2.121 -3.61,2.703 -1.241,0.582 -2.583,0.874 -4.025,0.874 -2.802,0 -4.772,-1.081 -5.912,-3.243 -1.139,-2.162 -1.218,-4.993 -0.238,-8.493 0.988,-3.528 2.668,-6.404 5.042,-8.627 2.374,-2.224 4.927,-3.336 7.661,-3.336 1.47,0 2.695,0.296 3.676,0.887 0.981,0.591 1.681,1.465 2.099,2.62 L 89.217,0 Z"
|
||||||
|
id="path39"
|
||||||
|
style="fill:#ffffff;fill-opacity:1" />
|
||||||
|
</g><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M 46.488,30.321 52.757,7.928 h 7.049 l -1.098,3.918 C 59.921,10.309 61.21,9.187 62.576,8.48 63.942,7.773 68.591,7.406 70.398,7.379 l -2.03,7.25 c -0.304,-0.042 -0.608,-0.072 -0.911,-0.093 -0.304,-0.02 -0.592,-0.03 -0.867,-0.03 -1.126,0 -5.167,0.168 -5.997,0.504 -0.829,0.336 -1.561,0.854 -2.196,1.555 -0.406,0.467 -0.789,1.136 -1.149,2.007 -0.361,0.872 -0.814,2.282 -1.36,4.232 l -2.104,7.516 h -7.296 z"
|
||||||
|
id="path43"
|
||||||
|
style="fill:#ffffff;fill-opacity:1" /><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#ffffff"
|
||||||
|
d="m 32.673,16.742 8.351,-0.021 c 0.375,-1.436 0.308,-2.558 -0.201,-3.365 -0.509,-0.807 -1.402,-1.211 -2.68,-1.211 -1.209,0 -2.285,0.397 -3.229,1.19 -0.944,0.793 -1.69,1.93 -2.241,3.407 z m 6.144,6.536 h 7.043 c -1.347,2.456 -3.172,4.356 -5.477,5.7 -2.305,1.345 -4.885,2.017 -7.74,2.017 -3.473,0 -5.923,-1.054 -7.351,-3.161 -1.427,-2.107 -1.632,-4.98 -0.613,-8.618 1.038,-3.707 2.875,-6.641 5.512,-8.803 2.637,-2.163 5.678,-3.244 9.123,-3.244 3.555,0 6.04,1.099 7.456,3.298 1.417,2.198 1.582,5.234 0.498,9.109 l -0.239,0.814 -0.167,0.484 H 31.721 c -0.441,1.575 -0.438,2.777 0.01,3.606 0.448,0.829 1.332,1.244 2.65,1.244 0.975,0 1.836,-0.206 2.583,-0.617 0.747,-0.411 1.366,-1.021 1.853,-1.829 z"
|
||||||
|
id="path47"
|
||||||
|
style="fill:#ffffff;fill-opacity:1" /><g
|
||||||
|
id="g49"
|
||||||
|
style="fill:#ffffff;fill-opacity:1">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="m 32.673,16.742 8.351,-0.021 c 0.375,-1.436 0.308,-2.558 -0.201,-3.365 -0.509,-0.807 -1.402,-1.211 -2.68,-1.211 -1.209,0 -2.285,0.397 -3.229,1.19 -0.944,0.793 -1.69,1.93 -2.241,3.407 z m 6.144,6.536 h 7.043 c -1.347,2.456 -3.172,4.356 -5.477,5.7 -2.305,1.345 -4.885,2.017 -7.74,2.017 -3.473,0 -5.923,-1.054 -7.351,-3.161 -1.427,-2.107 -1.632,-4.98 -0.613,-8.618 1.038,-3.707 2.875,-6.641 5.512,-8.803 2.637,-2.163 5.678,-3.244 9.123,-3.244 3.555,0 6.04,1.099 7.456,3.298 1.417,2.198 1.582,5.234 0.498,9.109 l -0.239,0.814 -0.167,0.484 H 31.721 c -0.441,1.575 -0.438,2.777 0.01,3.606 0.448,0.829 1.332,1.244 2.65,1.244 0.975,0 1.836,-0.206 2.583,-0.617 0.747,-0.411 1.366,-1.021 1.853,-1.829 z"
|
||||||
|
id="path51"
|
||||||
|
style="fill:#ffffff;fill-opacity:1" />
|
||||||
|
</g><path
|
||||||
|
fill="#A0CE67"
|
||||||
|
d="m 112.881,30.643 -6.404,-18.639 -6.455,18.639 h -7.254 l 9.565,-30.321 h 8.19 l 4.434,15.582 0.35,1.276 c 0.521,1.866 0.917,3.431 1.191,4.693 l 0.555,-2.182 c 0.219,-0.837 0.528,-1.935 0.925,-3.293 l 4.468,-16.076 h 8.19 l -10.501,30.321 z"
|
||||||
|
id="path55"
|
||||||
|
style="fill:#97d700;fill-opacity:1" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,72 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="187"
|
||||||
|
height="187"
|
||||||
|
viewBox="0 0 187 187"
|
||||||
|
enable-background="new 0 0 595 842"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="logo.svg"><metadata
|
||||||
|
id="metadata21"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs19" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1016"
|
||||||
|
id="namedview17"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:zoom="4.6900433"
|
||||||
|
inkscape:cx="83.335292"
|
||||||
|
inkscape:cy="99.203526"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" /><g
|
||||||
|
id="Background"
|
||||||
|
transform="translate(-211.7456,-282.24899)" /><g
|
||||||
|
id="Guides"
|
||||||
|
transform="translate(-211.7456,-282.24899)" /><g
|
||||||
|
id="g7"
|
||||||
|
transform="matrix(1.0030446,0,0,1.0030446,-212.39029,-288.74375)"
|
||||||
|
style="fill:#8ed300;fill-opacity:1"><path
|
||||||
|
style="fill:#8ed300;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path9"
|
||||||
|
d="m 339.611,301.147 c 1.324,-0.375 2.663,-0.656 4.017,-0.838 l 54.55,-7.391 -1.039,30.924 c -0.862,25.488 -15.732,48.394 -34.025,53.571 -1.319,0.374 -2.654,0.654 -4.003,0.837 l -54.551,7.379 1.038,-30.923 c 0.864,-25.481 15.725,-48.38 34.013,-53.559 z" /><path
|
||||||
|
style="fill:#8ed300;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path11"
|
||||||
|
d="m 304.353,399.358 27.265,-3.692 c 10.052,-1.368 17.809,8.612 17.351,22.267 l -0.523,15.469 -27.265,3.692 c -10.041,1.366 -17.811,-8.612 -17.354,-22.279 l 0.526,-15.457 z" /><path
|
||||||
|
style="fill:#8ed300;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path13"
|
||||||
|
d="m 212.72,326.05 49.089,-6.647 c 18.083,-2.444 32.068,15.502 31.238,40.103 l -0.941,27.826 -49.09,6.647 c -18.081,2.456 -32.057,-15.506 -31.236,-40.09 l 0.94,-27.839 z" /><path
|
||||||
|
style="fill:#8ed300;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path15"
|
||||||
|
d="m 248.296,407.657 c 0.966,-0.272 1.943,-0.478 2.93,-0.611 l 39.76,-5.383 -0.75,22.539 c -0.624,18.584 -11.458,35.275 -24.792,39.049 -0.966,0.272 -1.943,0.48 -2.931,0.613 l -39.772,5.385 0.761,-22.542 c 0.625,-18.573 11.461,-35.274 24.794,-39.05 z" /></g></svg>
|
After Width: | Height: | Size: 3.2 KiB |
|
@ -46,7 +46,7 @@ const onResponseError = (error) => {
|
||||||
message = responseError.message;
|
message = responseError.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (response.status) {
|
switch (response?.status) {
|
||||||
case 500:
|
case 500:
|
||||||
message = 'errors.statusInternalServerError';
|
message = 'errors.statusInternalServerError';
|
||||||
break;
|
break;
|
||||||
|
@ -58,7 +58,7 @@ const onResponseError = (error) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.isLoggedIn() && response.status === 401) {
|
if (session.isLoggedIn() && response?.status === 401) {
|
||||||
session.destroy();
|
session.destroy();
|
||||||
const hash = window.location.hash;
|
const hash = window.location.hash;
|
||||||
const url = hash.slice(1);
|
const url = hash.slice(1);
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { useStateStore } from 'stores/useStateStore';
|
||||||
import VnPaginate from 'components/ui/VnPaginate.vue';
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
import SkeletonTable from 'components/ui/SkeletonTable.vue';
|
import SkeletonTable from 'components/ui/SkeletonTable.vue';
|
||||||
|
import { tMobile } from 'src/composables/tMobile';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
|
@ -62,9 +63,10 @@ const hasChanges = ref(false);
|
||||||
const originalData = ref();
|
const originalData = ref();
|
||||||
const vnPaginateRef = ref();
|
const vnPaginateRef = ref();
|
||||||
const formData = ref();
|
const formData = ref();
|
||||||
|
const saveButtonRef = ref(null);
|
||||||
const formUrl = computed(() => $props.url);
|
const formUrl = computed(() => $props.url);
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch', 'update:selected']);
|
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
reload,
|
reload,
|
||||||
|
@ -73,12 +75,9 @@ defineExpose({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
reset,
|
reset,
|
||||||
hasChanges,
|
hasChanges,
|
||||||
|
saveChanges,
|
||||||
});
|
});
|
||||||
|
|
||||||
function tMobile(...args) {
|
|
||||||
if (!quasar.platform.is.mobile) return t(...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetch(data) {
|
async function fetch(data) {
|
||||||
if (data && Array.isArray(data)) {
|
if (data && Array.isArray(data)) {
|
||||||
let $index = 0;
|
let $index = 0;
|
||||||
|
@ -90,6 +89,7 @@ async function fetch(data) {
|
||||||
watch(formData, () => (hasChanges.value = true), { deep: true });
|
watch(formData, () => (hasChanges.value = true), { deep: true });
|
||||||
|
|
||||||
emit('onFetch', data);
|
emit('onFetch', data);
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reset() {
|
async function reset() {
|
||||||
|
@ -135,6 +135,7 @@ async function saveChanges(data) {
|
||||||
|
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
emit('saveChanges', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function insert() {
|
async function insert() {
|
||||||
|
@ -268,8 +269,8 @@ watch(formUrl, async () => {
|
||||||
</VnPaginate>
|
</VnPaginate>
|
||||||
<SkeletonTable v-if="!formData" />
|
<SkeletonTable v-if="!formData" />
|
||||||
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
||||||
<QBtnGroup push class="q-gutter-x-sm">
|
<QBtnGroup push style="column-gap: 10px">
|
||||||
<slot name="moreActions" />
|
<slot name="moreBeforeActions" />
|
||||||
<QBtn
|
<QBtn
|
||||||
:label="tMobile('globals.remove')"
|
:label="tMobile('globals.remove')"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -292,6 +293,7 @@ watch(formUrl, async () => {
|
||||||
/>
|
/>
|
||||||
<QBtn
|
<QBtn
|
||||||
:label="tMobile('globals.save')"
|
:label="tMobile('globals.save')"
|
||||||
|
ref="saveButtonRef"
|
||||||
color="primary"
|
color="primary"
|
||||||
icon="save"
|
icon="save"
|
||||||
@click="onSubmit"
|
@click="onSubmit"
|
||||||
|
@ -299,6 +301,7 @@ watch(formUrl, async () => {
|
||||||
:title="t('globals.save')"
|
:title="t('globals.save')"
|
||||||
v-if="$props.defaultSave"
|
v-if="$props.defaultSave"
|
||||||
/>
|
/>
|
||||||
|
<slot name="moreAfterActions" />
|
||||||
</QBtnGroup>
|
</QBtnGroup>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<QInnerLoading
|
<QInnerLoading
|
||||||
|
|
|
@ -122,9 +122,24 @@ watch(formUrl, async () => {
|
||||||
<QIcon name="warning" size="md" class="q-mr-md" />
|
<QIcon name="warning" size="md" class="q-mr-md" />
|
||||||
<span>{{ t('globals.changesToSave') }}</span>
|
<span>{{ t('globals.changesToSave') }}</span>
|
||||||
</QBanner>
|
</QBanner>
|
||||||
<QForm v-if="formData" @submit="save" @reset="reset" class="q-pa-md">
|
<div class="column items-center">
|
||||||
<slot name="form" :data="formData" :validate="validate" :filter="filter"></slot>
|
<QForm
|
||||||
</QForm>
|
v-if="formData"
|
||||||
|
@submit="save"
|
||||||
|
@reset="reset"
|
||||||
|
class="q-pa-md"
|
||||||
|
id="formModel"
|
||||||
|
>
|
||||||
|
<QCard>
|
||||||
|
<slot
|
||||||
|
name="form"
|
||||||
|
:data="formData"
|
||||||
|
:validate="validate"
|
||||||
|
:filter="filter"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
</QForm>
|
||||||
|
</div>
|
||||||
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
||||||
<div v-if="$props.defaultActions">
|
<div v-if="$props.defaultActions">
|
||||||
<QBtnGroup push class="q-gutter-x-sm">
|
<QBtnGroup push class="q-gutter-x-sm">
|
||||||
|
@ -156,3 +171,12 @@ watch(formUrl, async () => {
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
#formModel {
|
||||||
|
max-width: 800px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.q-card {
|
||||||
|
padding: 32px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import PinnedModules from './PinnedModules.vue';
|
import PinnedModules from './PinnedModules.vue';
|
||||||
import UserPanel from 'components/UserPanel.vue';
|
import UserPanel from 'components/UserPanel.vue';
|
||||||
|
import VnBreadcrumbs from './common/VnBreadcrumbs.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
|
@ -24,30 +25,20 @@ const pinnedModulesRef = ref();
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QHeader class="bg-dark" color="white" elevated>
|
<QHeader class="bg-dark" color="white" elevated>
|
||||||
<QToolbar class="q-py-sm q-px-md">
|
<QToolbar
|
||||||
<QBtn
|
class="q-py-sm q-px-md"
|
||||||
@click="stateStore.toggleLeftDrawer()"
|
:class="{ 'q-gutter-x-sm': !quasar.platform.is.mobile }"
|
||||||
icon="menu"
|
>
|
||||||
class="q-mr-sm"
|
<QBtn @click="stateStore.toggleLeftDrawer()" icon="menu" round dense flat>
|
||||||
round
|
|
||||||
dense
|
|
||||||
flat
|
|
||||||
>
|
|
||||||
<QTooltip bottom anchor="bottom right">
|
<QTooltip bottom anchor="bottom right">
|
||||||
{{ t('globals.collapseMenu') }}
|
{{ t('globals.collapseMenu') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
<RouterLink to="/">
|
<RouterLink to="/">
|
||||||
<QBtn
|
<QBtn color="primary" flat round v-if="!quasar.platform.is.mobile">
|
||||||
class="q-ml-xs"
|
|
||||||
color="primary"
|
|
||||||
flat
|
|
||||||
round
|
|
||||||
v-if="!quasar.platform.is.mobile"
|
|
||||||
>
|
|
||||||
<QAvatar square size="md">
|
<QAvatar square size="md">
|
||||||
<QImg
|
<QImg
|
||||||
src="~/assets/logo_icon.svg"
|
src="~/assets/salix_icon.svg"
|
||||||
:alt="appName"
|
:alt="appName"
|
||||||
spinner-color="primary"
|
spinner-color="primary"
|
||||||
/>
|
/>
|
||||||
|
@ -57,10 +48,7 @@ const pinnedModulesRef = ref();
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<QToolbarTitle shrink class="text-weight-bold" v-if="$q.screen.gt.sm">
|
<VnBreadcrumbs v-if="$q.screen.gt.sm" />
|
||||||
{{ appName }}
|
|
||||||
<QBadge label="Beta" align="top" />
|
|
||||||
</QToolbarTitle>
|
|
||||||
<QSpace />
|
<QSpace />
|
||||||
<div id="searchbar" class="searchbar"></div>
|
<div id="searchbar" class="searchbar"></div>
|
||||||
<QSpace />
|
<QSpace />
|
||||||
|
@ -112,6 +100,7 @@ const pinnedModulesRef = ref();
|
||||||
<div id="actions-append"></div>
|
<div id="actions-append"></div>
|
||||||
</div>
|
</div>
|
||||||
</QToolbar>
|
</QToolbar>
|
||||||
|
<VnBreadcrumbs v-if="$q.screen.lt.md" class="q-ml-md" />
|
||||||
</QHeader>
|
</QHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
<script setup>
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { ref, watchEffect } from 'vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useCamelCase } from 'src/composables/useCamelCase';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
let matched = ref([]);
|
||||||
|
let breadcrumbs = ref([]);
|
||||||
|
let root = ref(null);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
matched.value = router.currentRoute.value.matched.filter(
|
||||||
|
(matched) => Object.keys(matched.meta).length
|
||||||
|
);
|
||||||
|
breadcrumbs.value.length = 0;
|
||||||
|
|
||||||
|
if (matched.value[0].name != 'Dashboard') {
|
||||||
|
root.value = useCamelCase(matched.value[0].path.substring(1).toLowerCase());
|
||||||
|
|
||||||
|
for (let index in matched.value)
|
||||||
|
breadcrumbs.value.push(getBreadcrumb(matched.value[index]));
|
||||||
|
|
||||||
|
breadcrumbs.value[breadcrumbs.value.length - 1].path = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getBreadcrumb(param) {
|
||||||
|
const breadcrumb = {
|
||||||
|
icon: param.meta.icon,
|
||||||
|
path: param.path,
|
||||||
|
root: root.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (quasar.screen.gt.sm) {
|
||||||
|
breadcrumb.name = param.name;
|
||||||
|
breadcrumb.title = useCamelCase(param.meta.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
return breadcrumb;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QBreadcrumbs v-if="breadcrumbs.length && $q.screen.gt.sm" class="q-pa-xs">
|
||||||
|
<QBreadcrumbsEl
|
||||||
|
v-for="(breadcrumb, index) of breadcrumbs"
|
||||||
|
:key="index"
|
||||||
|
:icon="breadcrumb.icon"
|
||||||
|
:label="t(`${breadcrumb.root}.pageTitles.${breadcrumb.title}`)"
|
||||||
|
:to="breadcrumb.path"
|
||||||
|
/>
|
||||||
|
</QBreadcrumbs>
|
||||||
|
<QBreadcrumbs v-else class="q-pa-xs">
|
||||||
|
<QBreadcrumbsEl
|
||||||
|
v-for="(breadcrumb, index) of breadcrumbs"
|
||||||
|
:key="index"
|
||||||
|
:icon="breadcrumb.icon"
|
||||||
|
:to="breadcrumb.path"
|
||||||
|
/>
|
||||||
|
</QBreadcrumbs>
|
||||||
|
</template>
|
||||||
|
<style lang="scss">
|
||||||
|
.q-breadcrumbs {
|
||||||
|
&__el,
|
||||||
|
> div {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: $breakpoint-md) {
|
||||||
|
.q-breadcrumbs {
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__el:not(:first-child):not(:last-child) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -4,7 +4,7 @@ const emit = defineEmits(['update:modelValue', 'update:options']);
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: [String, Number],
|
type: [String, Number, Object],
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
|
@ -15,10 +15,16 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
isClearable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const { optionLabel, options } = toRefs($props);
|
const { optionLabel, options } = toRefs($props);
|
||||||
const myOptions = ref([]);
|
const myOptions = ref([]);
|
||||||
const myOptionsOriginal = ref([]);
|
const myOptionsOriginal = ref([]);
|
||||||
|
const vnSelectRef = ref(null);
|
||||||
|
|
||||||
function setOptions(data) {
|
function setOptions(data) {
|
||||||
myOptions.value = JSON.parse(JSON.stringify(data));
|
myOptions.value = JSON.parse(JSON.stringify(data));
|
||||||
myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
|
myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
|
||||||
|
@ -29,6 +35,7 @@ const filter = (val, options) => {
|
||||||
const search = val.toLowerCase();
|
const search = val.toLowerCase();
|
||||||
|
|
||||||
if (val === '') return options;
|
if (val === '') return options;
|
||||||
|
|
||||||
return options.filter((row) => {
|
return options.filter((row) => {
|
||||||
const id = row.id;
|
const id = row.id;
|
||||||
const name = row[$props.optionLabel].toLowerCase();
|
const name = row[$props.optionLabel].toLowerCase();
|
||||||
|
@ -41,9 +48,17 @@ const filter = (val, options) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterHandler = (val, update) => {
|
const filterHandler = (val, update) => {
|
||||||
update(() => {
|
update(
|
||||||
myOptions.value = filter(val, myOptionsOriginal.value);
|
() => {
|
||||||
});
|
myOptions.value = filter(val, myOptionsOriginal.value);
|
||||||
|
},
|
||||||
|
(ref) => {
|
||||||
|
if (val !== '' && ref.options.length > 0) {
|
||||||
|
ref.setOptionIndex(-1);
|
||||||
|
ref.moveOptionSelection(1, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(options, (newValue) => {
|
watch(options, (newValue) => {
|
||||||
|
@ -70,7 +85,14 @@ const value = computed({
|
||||||
map-options
|
map-options
|
||||||
use-input
|
use-input
|
||||||
@filter="filterHandler"
|
@filter="filterHandler"
|
||||||
clearable
|
fill-input
|
||||||
clear-icon="close"
|
ref="vnSelectRef"
|
||||||
/>
|
>
|
||||||
|
<template v-if="isClearable" #append>
|
||||||
|
<QIcon name="close" @click.stop="value = null" class="cursor-pointer" />
|
||||||
|
</template>
|
||||||
|
<template v-for="(_, slotName) in $slots" #[slotName]="slotData">
|
||||||
|
<slot :name="slotName" v-bind="slotData" />
|
||||||
|
</template>
|
||||||
|
</QSelect>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, useSlots, ref, watch } from 'vue';
|
import { onMounted, useSlots, ref, watch, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
||||||
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
url: {
|
url: {
|
||||||
|
@ -25,33 +26,37 @@ const $props = defineProps({
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const entity = ref();
|
const entity = computed(() => useArrayData($props.dataKey).store.data);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await fetch();
|
await getData();
|
||||||
|
watch(
|
||||||
|
() => $props.url,
|
||||||
|
async (newUrl, lastUrl) => {
|
||||||
|
if (newUrl == lastUrl) return;
|
||||||
|
entity.value = null;
|
||||||
|
await getData();
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch']);
|
async function getData() {
|
||||||
|
const arrayData = useArrayData($props.dataKey, {
|
||||||
async function fetch() {
|
url: $props.url,
|
||||||
const params = {};
|
filter: $props.filter,
|
||||||
|
skip: 0,
|
||||||
if ($props.filter) params.filter = JSON.stringify($props.filter);
|
});
|
||||||
|
const { data } = await arrayData.fetch({ append: false });
|
||||||
const { data } = await axios.get($props.url, { params });
|
|
||||||
entity.value = data;
|
|
||||||
|
|
||||||
emit('onFetch', data);
|
emit('onFetch', data);
|
||||||
}
|
}
|
||||||
|
const emit = defineEmits(['onFetch']);
|
||||||
watch($props, async () => {
|
|
||||||
entity.value = null;
|
|
||||||
await fetch();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -20,6 +20,10 @@ const props = defineProps({
|
||||||
required: false,
|
required: false,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
showAll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['refresh', 'clear']);
|
const emit = defineEmits(['refresh', 'clear']);
|
||||||
|
@ -29,31 +33,30 @@ const store = arrayData.store;
|
||||||
const userParams = ref({});
|
const userParams = ref({});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.params) userParams.value = props.params;
|
if (props.params) userParams.value = JSON.parse(JSON.stringify(props.params));
|
||||||
const params = store.userParams;
|
if (Object.keys(store.userParams).length > 0) {
|
||||||
if (Object.keys(params).length > 0) {
|
userParams.value = JSON.parse(JSON.stringify(store.userParams));
|
||||||
userParams.value = Object.assign({}, params);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
async function search() {
|
async function search() {
|
||||||
const params = userParams.value;
|
|
||||||
for (const param in params) {
|
|
||||||
if (params[param] === '' || params[param] === null) {
|
|
||||||
delete userParams.value[param];
|
|
||||||
delete store.userParams[param];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
await arrayData.addFilter({ params });
|
const params = { ...userParams.value };
|
||||||
|
const { params: newParams } = await arrayData.addFilter({ params });
|
||||||
|
userParams.value = newParams;
|
||||||
|
|
||||||
|
if (!props.showAll && !Object.values(params).length) store.data = [];
|
||||||
|
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reload() {
|
async function reload() {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
const params = Object.values(userParams.value).filter((param) => param);
|
||||||
|
|
||||||
await arrayData.fetch({ append: false });
|
await arrayData.fetch({ append: false });
|
||||||
|
if (!props.showAll && !params.length) store.data = [];
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
emit('refresh');
|
emit('refresh');
|
||||||
}
|
}
|
||||||
|
@ -62,6 +65,7 @@ async function clearFilters() {
|
||||||
userParams.value = {};
|
userParams.value = {};
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
await arrayData.applyFilter({ params: {} });
|
await arrayData.applyFilter({ params: {} });
|
||||||
|
if (!props.showAll) store.data = [];
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
|
||||||
emit('clear');
|
emit('clear');
|
||||||
|
@ -70,10 +74,11 @@ async function clearFilters() {
|
||||||
const tags = computed(() => {
|
const tags = computed(() => {
|
||||||
const params = [];
|
const params = [];
|
||||||
|
|
||||||
for (const param in store.userParams) {
|
for (const param in userParams.value) {
|
||||||
|
if (!userParams.value[param]) continue;
|
||||||
params.push({
|
params.push({
|
||||||
label: param,
|
label: param,
|
||||||
value: store.userParams[param],
|
value: userParams.value[param],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,8 +86,7 @@ const tags = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function remove(key) {
|
async function remove(key) {
|
||||||
delete userParams.value[key];
|
userParams.value[key] = null;
|
||||||
delete store.userParams[key];
|
|
||||||
await search();
|
await search();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<script setup>
|
||||||
|
import { Dark } from 'quasar';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
logo: {
|
||||||
|
type: String,
|
||||||
|
default: 'salix',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const src = computed({
|
||||||
|
get() {
|
||||||
|
return new URL(
|
||||||
|
`../../assets/${$props.logo}${Dark.isActive ? '_dark' : ''}.svg`,
|
||||||
|
import.meta.url
|
||||||
|
).href;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QImg :src="src" v-bind="$attrs" />
|
||||||
|
</template>
|
|
@ -50,6 +50,10 @@ const props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
exprBuilder: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch', 'onPaginate']);
|
const emit = defineEmits(['onFetch', 'onPaginate']);
|
||||||
|
@ -68,6 +72,7 @@ const arrayData = useArrayData(props.dataKey, {
|
||||||
limit: props.limit,
|
limit: props.limit,
|
||||||
order: props.order,
|
order: props.order,
|
||||||
userParams: props.userParams,
|
userParams: props.userParams,
|
||||||
|
exprBuilder: props.exprBuilder,
|
||||||
});
|
});
|
||||||
const store = arrayData.store;
|
const store = arrayData.store;
|
||||||
|
|
||||||
|
@ -140,14 +145,6 @@ async function onLoad(...params) {
|
||||||
{{ t('No data to display') }}
|
{{ t('No data to display') }}
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-if="store.data && store.data.length === 0 && !isLoading"
|
|
||||||
class="info-row q-pa-md text-center"
|
|
||||||
>
|
|
||||||
<h5>
|
|
||||||
{{ t('No results found') }}
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-if="props.skeleton && props.autoLoad && !store.data"
|
v-if="props.skeleton && props.autoLoad && !store.data"
|
||||||
class="card-list q-gutter-y-md"
|
class="card-list q-gutter-y-md"
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<template>
|
||||||
|
<div id="row">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scopped>
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
#row {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -105,7 +105,11 @@ async function search() {
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<QIcon v-if="props.info" name="info" class="cursor-info">
|
<QIcon
|
||||||
|
v-if="props.info && $q.screen.gt.xs"
|
||||||
|
name="info"
|
||||||
|
class="cursor-info"
|
||||||
|
>
|
||||||
<QTooltip>{{ props.info }}</QTooltip>
|
<QTooltip>{{ props.info }}</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
export async function getUrl(route, appName = 'salix') {
|
export async function getUrl(route, appName = 'salix') {
|
||||||
let url;
|
|
||||||
const filter = {
|
const filter = {
|
||||||
where: { and: [{ appName: appName }, { environment: process.env.NODE_ENV }] },
|
where: { and: [{ appName: appName }, { environment: process.env.NODE_ENV }] },
|
||||||
};
|
};
|
||||||
|
|
||||||
await axios.get('Urls/findOne', { params: { filter } }).then((res) => {
|
const { data } = await axios.get('Urls/findOne', { params: { filter } });
|
||||||
url = res.data.url + route;
|
const url = data.url;
|
||||||
});
|
return route ? url + route : url;
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
export function tMobile(...args) {
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { t } = useI18n();
|
||||||
|
if (!quasar.platform.is.mobile) return t(...args);
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import { onMounted, ref, computed } from 'vue';
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useArrayDataStore } from 'stores/useArrayDataStore';
|
import { useArrayDataStore } from 'stores/useArrayDataStore';
|
||||||
|
import { buildFilter } from 'filters/filterPanel';
|
||||||
|
|
||||||
const arrayDataStore = useArrayDataStore();
|
const arrayDataStore = useArrayDataStore();
|
||||||
|
|
||||||
|
@ -29,6 +30,10 @@ export function useArrayData(key, userOptions) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (key && userOptions) {
|
||||||
|
setOptions();
|
||||||
|
}
|
||||||
|
|
||||||
function setOptions() {
|
function setOptions() {
|
||||||
const allowedOptions = [
|
const allowedOptions = [
|
||||||
'url',
|
'url',
|
||||||
|
@ -39,10 +44,11 @@ export function useArrayData(key, userOptions) {
|
||||||
'skip',
|
'skip',
|
||||||
'userParams',
|
'userParams',
|
||||||
'userFilter',
|
'userFilter',
|
||||||
|
'exprBuilder',
|
||||||
];
|
];
|
||||||
if (typeof userOptions === 'object') {
|
if (typeof userOptions === 'object') {
|
||||||
for (const option in userOptions) {
|
for (const option in userOptions) {
|
||||||
const isEmpty = userOptions[option] == null || userOptions[option] == '';
|
const isEmpty = userOptions[option] == null || userOptions[option] === '';
|
||||||
if (isEmpty || !allowedOptions.includes(option)) continue;
|
if (isEmpty || !allowedOptions.includes(option)) continue;
|
||||||
|
|
||||||
if (Object.prototype.hasOwnProperty.call(store, option)) {
|
if (Object.prototype.hasOwnProperty.call(store, option)) {
|
||||||
|
@ -64,16 +70,27 @@ export function useArrayData(key, userOptions) {
|
||||||
skip: store.skip,
|
skip: store.skip,
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.assign(filter, store.userFilter);
|
let exprFilter;
|
||||||
Object.assign(store.filter, filter);
|
let userParams = { ...store.userParams };
|
||||||
|
if (store?.exprBuilder) {
|
||||||
|
const where = buildFilter(userParams, (param, value) => {
|
||||||
|
const res = store.exprBuilder(param, value);
|
||||||
|
if (res) delete userParams[param];
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
exprFilter = where ? { where } : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(filter, store.userFilter, exprFilter);
|
||||||
|
Object.assign(store.filter, filter);
|
||||||
const params = {
|
const params = {
|
||||||
filter: JSON.stringify(store.filter),
|
filter: JSON.stringify(store.filter),
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.assign(params, store.userParams);
|
Object.assign(params, userParams);
|
||||||
|
|
||||||
store.isLoading = true;
|
store.isLoading = true;
|
||||||
|
|
||||||
const response = await axios.get(store.url, {
|
const response = await axios.get(store.url, {
|
||||||
signal: canceller.signal,
|
signal: canceller.signal,
|
||||||
params,
|
params,
|
||||||
|
@ -97,6 +114,7 @@ export function useArrayData(key, userOptions) {
|
||||||
store.isLoading = false;
|
store.isLoading = false;
|
||||||
|
|
||||||
canceller = null;
|
canceller = null;
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroy() {
|
function destroy() {
|
||||||
|
@ -121,9 +139,30 @@ export function useArrayData(key, userOptions) {
|
||||||
|
|
||||||
async function addFilter({ filter, params }) {
|
async function addFilter({ filter, params }) {
|
||||||
if (filter) store.userFilter = Object.assign(store.userFilter, filter);
|
if (filter) store.userFilter = Object.assign(store.userFilter, filter);
|
||||||
if (params) store.userParams = Object.assign(store.userParams, params);
|
|
||||||
|
let userParams = Object.assign({}, store.userParams, params);
|
||||||
|
userParams = sanitizerParams(userParams, store?.exprBuilder);
|
||||||
|
|
||||||
|
store.userParams = userParams;
|
||||||
|
|
||||||
await fetch({ append: false });
|
await fetch({ append: false });
|
||||||
|
return { filter, params };
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizerParams(params) {
|
||||||
|
for (const param in params) {
|
||||||
|
if (params[param] === '' || params[param] === null) {
|
||||||
|
delete store.userParams[param];
|
||||||
|
delete params[param];
|
||||||
|
if (store.filter?.where) {
|
||||||
|
delete store.filter.where[Object.keys(store?.exprBuilder(param))[0]];
|
||||||
|
if (Object.keys(store.filter.where).length === 0) {
|
||||||
|
delete store.filter.where;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadMore() {
|
async function loadMore() {
|
||||||
|
@ -136,7 +175,7 @@ export function useArrayData(key, userOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refresh() {
|
async function refresh() {
|
||||||
await fetch({ append: false });
|
if (Object.values(store.userParams).length) await fetch({ append: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStateParams() {
|
function updateStateParams() {
|
||||||
|
@ -147,10 +186,11 @@ export function useArrayData(key, userOptions) {
|
||||||
if (store.userParams && Object.keys(store.userParams).length !== 0)
|
if (store.userParams && Object.keys(store.userParams).length !== 0)
|
||||||
query.params = JSON.stringify(store.userParams);
|
query.params = JSON.stringify(store.userParams);
|
||||||
|
|
||||||
router.replace({
|
if (router)
|
||||||
path: route.path,
|
router.replace({
|
||||||
query: query,
|
path: route.path,
|
||||||
});
|
query: query,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalRows = computed(() => (store.data && store.data.length) || 0);
|
const totalRows = computed(() => (store.data && store.data.length) || 0);
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function useCamelCase(value) {
|
||||||
|
return value.replace(/[-_](.)/g, (_, char) => char.toUpperCase());
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function useFirstUpper(str) {
|
||||||
|
return str && str.charAt(0).toUpperCase() + str.substr(1);
|
||||||
|
}
|
|
@ -45,3 +45,9 @@ body.body--dark {
|
||||||
.bg-vn-dark {
|
.bg-vn-dark {
|
||||||
background-color: var(--vn-dark);
|
background-color: var(--vn-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vn-card {
|
||||||
|
background-color: var(--vn-gray);
|
||||||
|
color: var(--vn-text);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ $positive: #21ba45;
|
||||||
$negative: #c10015;
|
$negative: #c10015;
|
||||||
$info: #31ccec;
|
$info: #31ccec;
|
||||||
$warning: #f2c037;
|
$warning: #f2c037;
|
||||||
|
$vnColor: #8ebb27;
|
||||||
|
|
||||||
// Pendiente de cuadrar con la base de datos
|
// Pendiente de cuadrar con la base de datos
|
||||||
$success: $positive;
|
$success: $positive;
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
* Passes a loopback fields filter to an object.
|
||||||
|
*
|
||||||
|
* @param {Object} fields The fields object or array
|
||||||
|
* @return {Object} The fields as object
|
||||||
|
*/
|
||||||
|
function fieldsToObject(fields) {
|
||||||
|
let fieldsObj = {};
|
||||||
|
|
||||||
|
if (Array.isArray(fields)) {
|
||||||
|
for (let field of fields) fieldsObj[field] = true;
|
||||||
|
} else if (typeof fields == 'object') {
|
||||||
|
for (let field in fields) {
|
||||||
|
if (fields[field]) fieldsObj[field] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldsObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two loopback fields filters.
|
||||||
|
*
|
||||||
|
* @param {Object|Array} src The source fields
|
||||||
|
* @param {Object|Array} dst The destination fields
|
||||||
|
* @return {Array} The merged fields as an array
|
||||||
|
*/
|
||||||
|
function mergeFields(src, dst) {
|
||||||
|
let fields = {};
|
||||||
|
Object.assign(fields, fieldsToObject(src), fieldsToObject(dst));
|
||||||
|
return Object.keys(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two loopback where filters.
|
||||||
|
*
|
||||||
|
* @param {Object|Array} src The source where
|
||||||
|
* @param {Object|Array} dst The destination where
|
||||||
|
* @return {Array} The merged wheres
|
||||||
|
*/
|
||||||
|
function mergeWhere(src, dst) {
|
||||||
|
let and = [];
|
||||||
|
if (src) and.push(src);
|
||||||
|
if (dst) and.push(dst);
|
||||||
|
return simplifyOperation(and, 'and');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two loopback filters returning the merged filter.
|
||||||
|
*
|
||||||
|
* @param {Object} src The source filter
|
||||||
|
* @param {Object} dst The destination filter
|
||||||
|
* @return {Object} The result filter
|
||||||
|
*/
|
||||||
|
function mergeFilters(src, dst) {
|
||||||
|
let res = Object.assign({}, dst);
|
||||||
|
|
||||||
|
if (!src) return res;
|
||||||
|
|
||||||
|
if (src.fields) res.fields = mergeFields(src.fields, res.fields);
|
||||||
|
if (src.where) res.where = mergeWhere(res.where, src.where);
|
||||||
|
if (src.include) res.include = src.include;
|
||||||
|
if (src.order) res.order = src.order;
|
||||||
|
if (src.limit) res.limit = src.limit;
|
||||||
|
if (src.offset) res.offset = src.offset;
|
||||||
|
if (src.skip) res.skip = src.skip;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function simplifyOperation(operation, operator) {
|
||||||
|
switch (operation.length) {
|
||||||
|
case 0:
|
||||||
|
return undefined;
|
||||||
|
case 1:
|
||||||
|
return operation[0];
|
||||||
|
default:
|
||||||
|
return { [operator]: operation };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFilter(params, builderFunc) {
|
||||||
|
let and = [];
|
||||||
|
|
||||||
|
for (let param in params) {
|
||||||
|
let value = params[param];
|
||||||
|
if (value == null) continue;
|
||||||
|
let expr = builderFunc(param, value);
|
||||||
|
if (expr) and.push(expr);
|
||||||
|
}
|
||||||
|
return simplifyOperation(and, 'and');
|
||||||
|
}
|
||||||
|
|
||||||
|
export { fieldsToObject, mergeFields, mergeWhere, mergeFilters, buildFilter };
|
|
@ -69,6 +69,11 @@ export default {
|
||||||
twoFactor: 'Two-Factor',
|
twoFactor: 'Two-Factor',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
verifyEmail: {
|
||||||
|
pageTitles: {
|
||||||
|
verifyEmail: 'Email verification',
|
||||||
|
},
|
||||||
|
},
|
||||||
dashboard: {
|
dashboard: {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
dashboard: 'Dashboard',
|
dashboard: 'Dashboard',
|
||||||
|
@ -270,6 +275,7 @@ export default {
|
||||||
development: 'Development',
|
development: 'Development',
|
||||||
log: 'Audit logs',
|
log: 'Audit logs',
|
||||||
notes: 'Notes',
|
notes: 'Notes',
|
||||||
|
action: 'Action',
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
customer: 'Customer',
|
customer: 'Customer',
|
||||||
|
@ -444,6 +450,7 @@ export default {
|
||||||
typesList: 'Types List',
|
typesList: 'Types List',
|
||||||
typeCreate: 'Create type',
|
typeCreate: 'Create type',
|
||||||
typeEdit: 'Edit type',
|
typeEdit: 'Edit type',
|
||||||
|
wagonCounter: 'Trolley counter',
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
|
|
|
@ -69,6 +69,11 @@ export default {
|
||||||
twoFactor: 'Doble factor',
|
twoFactor: 'Doble factor',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
verifyEmail: {
|
||||||
|
pageTitles: {
|
||||||
|
verifyEmail: 'Verificación de correo',
|
||||||
|
},
|
||||||
|
},
|
||||||
dashboard: {
|
dashboard: {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
dashboard: 'Tablón',
|
dashboard: 'Tablón',
|
||||||
|
@ -269,6 +274,7 @@ export default {
|
||||||
photos: 'Fotos',
|
photos: 'Fotos',
|
||||||
log: 'Registros de auditoría',
|
log: 'Registros de auditoría',
|
||||||
notes: 'Notas',
|
notes: 'Notas',
|
||||||
|
action: 'Acción',
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
customer: 'Cliente',
|
customer: 'Cliente',
|
||||||
|
@ -444,6 +450,7 @@ export default {
|
||||||
typesList: 'Listado tipos',
|
typesList: 'Listado tipos',
|
||||||
typeCreate: 'Crear tipo',
|
typeCreate: 'Crear tipo',
|
||||||
typeEdit: 'Editar tipo',
|
typeEdit: 'Editar tipo',
|
||||||
|
wagonCounter: 'Contador de carros',
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
name: 'Nombre',
|
name: 'Nombre',
|
||||||
|
|
|
@ -80,7 +80,7 @@ const langs = ['en', 'es'];
|
||||||
<QPageContainer>
|
<QPageContainer>
|
||||||
<QPage>
|
<QPage>
|
||||||
<div class="form">
|
<div class="form">
|
||||||
<QCard class="q-pa-lg formCard">
|
<QCard class="flex flex-center">
|
||||||
<RouterView></RouterView>
|
<RouterView></RouterView>
|
||||||
</QCard>
|
</QCard>
|
||||||
</div>
|
</div>
|
||||||
|
@ -97,15 +97,4 @@ const langs = ['en', 'es'];
|
||||||
min-height: inherit;
|
min-height: inherit;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.formCard {
|
|
||||||
max-width: 350px;
|
|
||||||
min-width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $breakpoint-xs-max) {
|
|
||||||
.formCard {
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,518 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted } from 'vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useStateStore } from 'src/stores/useStateStore';
|
||||||
|
import { toDate, toPercentage, toCurrency } from 'filters/index';
|
||||||
|
import { tMobile } from 'src/composables/tMobile';
|
||||||
|
import CrudModel from 'src/components/CrudModel.vue';
|
||||||
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||||
|
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const stateStore = computed(() => useStateStore());
|
||||||
|
const claim = ref(null);
|
||||||
|
const claimRef = ref();
|
||||||
|
const claimId = route.params.id;
|
||||||
|
const dialogDestination = ref(false);
|
||||||
|
const claimDestinationFk = ref(null);
|
||||||
|
const resolvedStateId = ref(null);
|
||||||
|
const claimActionsForm = ref();
|
||||||
|
const rows = ref([]);
|
||||||
|
const selectedRows = ref([]);
|
||||||
|
const destinationTypes = ref([]);
|
||||||
|
const totalClaimed = ref(null);
|
||||||
|
const DEFAULT_MAX_RESPONSABILITY = 5;
|
||||||
|
const DEFAULT_MIN_RESPONSABILITY = 1;
|
||||||
|
const arrayData = useArrayData('claimData');
|
||||||
|
const marker_labels = [
|
||||||
|
{ value: DEFAULT_MIN_RESPONSABILITY, label: t('claim.summary.company') },
|
||||||
|
{ value: DEFAULT_MAX_RESPONSABILITY, label: t('claim.summary.person') },
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
name: 'Id',
|
||||||
|
label: t('Id item'),
|
||||||
|
field: (row) => row.itemFk,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ticket',
|
||||||
|
label: t('Ticket'),
|
||||||
|
field: (row) => row.ticketFk,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'destination',
|
||||||
|
label: t('Destination'),
|
||||||
|
field: (row) => row.claimDestinationFk,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Landed',
|
||||||
|
label: t('Landed'),
|
||||||
|
field: (row) => toDate(row.landed),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'quantity',
|
||||||
|
label: t('Quantity'),
|
||||||
|
field: (row) => row.quantity,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'concept',
|
||||||
|
label: t('Description'),
|
||||||
|
field: (row) => row.concept,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'price',
|
||||||
|
label: t('Price'),
|
||||||
|
field: (row) => row.price,
|
||||||
|
format: (value) => value,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'discount',
|
||||||
|
label: t('Discount'),
|
||||||
|
field: (row) => row.discount,
|
||||||
|
format: (value) => toPercentage(value / 100),
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'total',
|
||||||
|
label: t('Total'),
|
||||||
|
field: (row) => row.total,
|
||||||
|
format: (value) => value,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'delete',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getTotal();
|
||||||
|
});
|
||||||
|
|
||||||
|
function setData(data) {
|
||||||
|
rows.value = data;
|
||||||
|
getTotal();
|
||||||
|
}
|
||||||
|
function getTotal() {
|
||||||
|
if (rows.value.length) {
|
||||||
|
totalClaimed.value = rows.value.reduce((total, row) => total + row.total, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateDestinations(claimDestinationFk) {
|
||||||
|
await updateDestination(claimDestinationFk, selectedRows.value, { reload: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateDestination(claimDestinationFk, row, options = {}) {
|
||||||
|
if (claimDestinationFk) {
|
||||||
|
await axios.post('Claims/updateClaimDestination', {
|
||||||
|
claimDestinationFk,
|
||||||
|
rows: Array.isArray(row) ? row : [row],
|
||||||
|
});
|
||||||
|
options.reload && claimActionsForm.value.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function regularizeClaim() {
|
||||||
|
const query = `Claims/${claimId}/regularizeClaim`;
|
||||||
|
|
||||||
|
await axios.post(query);
|
||||||
|
if (claim.value.responsibility >= Math.ceil(DEFAULT_MAX_RESPONSABILITY) / 2) {
|
||||||
|
await claimRef.value.fetch();
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('confirmGreuges'),
|
||||||
|
message: t('confirmGreugesMessage'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(async () => await onUpdateGreugeAccept());
|
||||||
|
} else {
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataSaved'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await arrayData.fetch({ append: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateGreuge(greuges) {
|
||||||
|
const { data } = await axios.post(`Greuges`, greuges);
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataSaved'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onUpdateGreugeAccept() {
|
||||||
|
const greugeTypeFreightId = await getGreugeTypeId();
|
||||||
|
const freightPickUpPrice = await getGreugeConfig();
|
||||||
|
|
||||||
|
await updateGreuge({
|
||||||
|
clientFk: claim.value.clientFk,
|
||||||
|
description: `${t('ClaimGreugeDescription')} ${claimId}`.toUpperCase(),
|
||||||
|
amount: freightPickUpPrice,
|
||||||
|
greugeTypeFk: greugeTypeFreightId,
|
||||||
|
ticketFk: claim.value.ticketFk,
|
||||||
|
});
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataSaved'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getGreugeTypeId() {
|
||||||
|
const params = { filter: { where: { code: 'freightPickUp' } } };
|
||||||
|
const query = `GreugeTypes/findOne`;
|
||||||
|
const { data } = await axios.get(query, { params });
|
||||||
|
return data.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getGreugeConfig() {
|
||||||
|
const query = `GreugeConfigs/findOne`;
|
||||||
|
const { data } = await axios.get(query);
|
||||||
|
return data.freightPickUpPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save(data) {
|
||||||
|
const query = `Claims/${claimId}/updateClaimAction`;
|
||||||
|
await axios.patch(query, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function importToNewRefundTicket() {
|
||||||
|
const query = `ClaimBeginnings/${claimId}/importToNewRefundTicket`;
|
||||||
|
await axios.post(query);
|
||||||
|
claimActionsForm.value.reload();
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataSaved'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
ref="claimRef"
|
||||||
|
:url="`Claims/${claimId}`"
|
||||||
|
@on-fetch="(data) => (claim = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="ClaimStates/findOne"
|
||||||
|
@on-fetch="(data) => (resolvedStateId = data.id)"
|
||||||
|
auto-load
|
||||||
|
:where="{ code: 'resolved' }"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="ClaimDestinations"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (destinationTypes = data)"
|
||||||
|
/>
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#actions-append">
|
||||||
|
<div class="row q-gutter-x-sm">
|
||||||
|
<QBtn
|
||||||
|
flat
|
||||||
|
@click="stateStore.toggleRightDrawer()"
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="menu"
|
||||||
|
>
|
||||||
|
<QTooltip bottom anchor="bottom right">
|
||||||
|
{{ t('globals.collapseMenu') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
<QDrawer
|
||||||
|
v-model="stateStore.rightDrawer"
|
||||||
|
side="right"
|
||||||
|
:width="300"
|
||||||
|
show-if-above
|
||||||
|
v-if="claim"
|
||||||
|
>
|
||||||
|
<QCard class="totalClaim vn-card q-my-md q-pa-sm">
|
||||||
|
{{ `${t('Total claimed')}: ${toCurrency(totalClaimed)}` }}
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-card q-mb-md q-pa-sm">
|
||||||
|
<QItem class="justify-between">
|
||||||
|
<QItemLabel class="slider-container">
|
||||||
|
<p class="text-primary">
|
||||||
|
{{ t('claim.summary.actions') }}
|
||||||
|
</p>
|
||||||
|
<QSlider
|
||||||
|
class="responsibility { 'background-color:primary': quasar.platform.is.mobile }"
|
||||||
|
v-model="claim.responsibility"
|
||||||
|
:label-value="t('claim.summary.responsibility')"
|
||||||
|
@change="(value) => save({ responsibility: value })"
|
||||||
|
label-always
|
||||||
|
color="primary"
|
||||||
|
markers
|
||||||
|
:marker-labels="marker_labels"
|
||||||
|
:min="DEFAULT_MIN_RESPONSABILITY"
|
||||||
|
:max="DEFAULT_MAX_RESPONSABILITY"
|
||||||
|
/>
|
||||||
|
</QItemLabel>
|
||||||
|
</QItem>
|
||||||
|
</QCard>
|
||||||
|
<QItemLabel class="mana q-mb-md">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="claim.isChargedToMana"
|
||||||
|
@update:model-value="(value) => save({ isChargedToMana: value })"
|
||||||
|
/>
|
||||||
|
<span>{{ t('mana') }}</span>
|
||||||
|
</QItemLabel>
|
||||||
|
</QDrawer>
|
||||||
|
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()"> </Teleport>
|
||||||
|
<CrudModel
|
||||||
|
v-if="claim"
|
||||||
|
data-key="ClaimEnds"
|
||||||
|
url="ClaimEnds/filter"
|
||||||
|
save-url="ClaimEnds/crud"
|
||||||
|
ref="claimActionsForm"
|
||||||
|
v-model:selected="selectedRows"
|
||||||
|
:filter="{ where: { claimFk: claimId } }"
|
||||||
|
:default-remove="true"
|
||||||
|
:default-save="false"
|
||||||
|
:default-reset="false"
|
||||||
|
@on-fetch="setData"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body>
|
||||||
|
<QTable
|
||||||
|
:columns="columns"
|
||||||
|
:rows="rows"
|
||||||
|
:dense="$q.screen.lt.md"
|
||||||
|
row-key="id"
|
||||||
|
selection="multiple"
|
||||||
|
v-model:selected="selectedRows"
|
||||||
|
:grid="$q.screen.lt.md"
|
||||||
|
>
|
||||||
|
<template #body-cell-ticket="{ value }">
|
||||||
|
<QTd align="center">
|
||||||
|
<span class="link">
|
||||||
|
{{ value }}
|
||||||
|
<TicketDescriptorProxy :id="value" />
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-destination="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<VnSelectFilter
|
||||||
|
v-model="row.claimDestinationFk"
|
||||||
|
:options="destinationTypes"
|
||||||
|
option-label="description"
|
||||||
|
option-value="id"
|
||||||
|
:autofocus="true"
|
||||||
|
dense
|
||||||
|
input-debounce="0"
|
||||||
|
hide-selected
|
||||||
|
@update:model-value="(value) => updateDestination(value, row)"
|
||||||
|
/>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-price="{ value }">
|
||||||
|
<QTd align="center">
|
||||||
|
{{ toCurrency(value) }}
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-total="{ value }">
|
||||||
|
<QTd align="center">
|
||||||
|
{{ toCurrency(value) }}
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<!-- View for grid mode -->
|
||||||
|
<template #item="props">
|
||||||
|
<div class="q-mb-md col-12 grid-style-transition">
|
||||||
|
<QCard>
|
||||||
|
<QCardSection class="row justify-between">
|
||||||
|
<QCheckbox v-model="props.selected" />
|
||||||
|
<QBtn color="primary" icon="delete" flat round />
|
||||||
|
</QCardSection>
|
||||||
|
|
||||||
|
<QSeparator inset />
|
||||||
|
<QList dense>
|
||||||
|
<QItem v-for="column of props.cols" :key="column.name">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ column.label }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection side>
|
||||||
|
<QItemLabel v-if="column.name === 'destination'">
|
||||||
|
{{ column.value.description }}
|
||||||
|
</QItemLabel>
|
||||||
|
<QItemLabel v-else>
|
||||||
|
{{ column.value }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</template>
|
||||||
|
<template #moreBeforeActions>
|
||||||
|
<QBtn
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
:unelevated="true"
|
||||||
|
:label="tMobile('Regularize')"
|
||||||
|
:title="t('Regularize')"
|
||||||
|
icon="check"
|
||||||
|
@click="regularizeClaim"
|
||||||
|
:disable="claim.claimStateFk == resolvedStateId"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<QBtn
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
:unelevated="true"
|
||||||
|
:disable="!selectedRows.length"
|
||||||
|
:label="tMobile('Change destination')"
|
||||||
|
:title="t('Change destination')"
|
||||||
|
icon="swap_horiz"
|
||||||
|
@click="dialogDestination = !dialogDestination"
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
:unelevated="true"
|
||||||
|
:label="tMobile('Import claim')"
|
||||||
|
:title="t('Import claim')"
|
||||||
|
icon="Upload"
|
||||||
|
@click="importToNewRefundTicket"
|
||||||
|
:disable="claim.claimStateFk == resolvedStateId"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</CrudModel>
|
||||||
|
<QDialog v-model="dialogDestination">
|
||||||
|
<QCard>
|
||||||
|
<QCardSection>
|
||||||
|
<QItem class="q-pa-sm">
|
||||||
|
<span class="q-dialog__title text-white">
|
||||||
|
{{ t('dialog title') }}
|
||||||
|
</span>
|
||||||
|
<QBtn icon="close" flat round dense v-close-popup />
|
||||||
|
</QItem>
|
||||||
|
</QCardSection>
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
class="q-pa-sm"
|
||||||
|
v-model="claimDestinationFk"
|
||||||
|
:options="destinationTypes"
|
||||||
|
option-label="description"
|
||||||
|
option-value="id"
|
||||||
|
:autofocus="true"
|
||||||
|
dense
|
||||||
|
input-debounce="0"
|
||||||
|
hide-selected
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
<QCardActions class="justify-end q-mr-sm">
|
||||||
|
<QBtn flat :label="t('globals.close')" color="primary" v-close-popup />
|
||||||
|
<QBtn
|
||||||
|
:disable="!claimDestinationFk"
|
||||||
|
:label="t('globals.save')"
|
||||||
|
color="primary"
|
||||||
|
v-close-popup
|
||||||
|
@click="updateDestinations(claimDestinationFk)"
|
||||||
|
/>
|
||||||
|
</QCardActions>
|
||||||
|
</QCard>
|
||||||
|
</QDialog>
|
||||||
|
<!-- <QDialog v-model="dialogGreuge">
|
||||||
|
<QCardSection>
|
||||||
|
<QItem class="q-pa-sm">
|
||||||
|
<span class="q-pa-sm q-dialog__title text-white">
|
||||||
|
{{ t('dialogGreuge title') }}
|
||||||
|
</span>
|
||||||
|
<QBtn class="q-pa-sm" icon="close" flat round dense v-close-popup />
|
||||||
|
</QItem>
|
||||||
|
<QCardActions class="justify-end q-mr-sm">
|
||||||
|
<QBtn flat :label="t('globals.close')" color="primary" v-close-popup />
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.save')"
|
||||||
|
color="primary"
|
||||||
|
v-close-popup
|
||||||
|
@click="onUpdateGreugeAccept"
|
||||||
|
/>
|
||||||
|
</QCardActions>
|
||||||
|
</QCardSection>
|
||||||
|
</QDialog> -->
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.slider-container {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
@media (max-width: $breakpoint-xs) {
|
||||||
|
.slider-container {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.q-table {
|
||||||
|
.q-item {
|
||||||
|
min-height: min-content;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.q-dialog {
|
||||||
|
.q-btn {
|
||||||
|
height: min-content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.responsibility {
|
||||||
|
max-width: 100%;
|
||||||
|
margin-left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mana {
|
||||||
|
float: inline-start;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
mana: Is paid with mana
|
||||||
|
dialog title: Change destination to all selected rows
|
||||||
|
confirmGreuges: Do you want to insert complaints?
|
||||||
|
confirmGreugesMessage: Insert complaints into the client's record
|
||||||
|
|
||||||
|
es:
|
||||||
|
mana: Cargado al maná
|
||||||
|
Delivered: Descripción
|
||||||
|
Quantity: Cantidad
|
||||||
|
Claimed: Rec
|
||||||
|
Description: Descripción
|
||||||
|
Price: Precio
|
||||||
|
Discount: Dto.
|
||||||
|
Destination: Destino
|
||||||
|
Landed: F.entrega
|
||||||
|
Remove line: Eliminar línea
|
||||||
|
Total claimed: Total reclamado
|
||||||
|
Regularize: Regularizar
|
||||||
|
Change destination: Cambiar destino
|
||||||
|
Import claim: Importar reclamación
|
||||||
|
dialog title: Cambiar destino en todas las filas seleccionadas
|
||||||
|
Remove: Eliminar
|
||||||
|
dialogGreuge title: Insertar greuges en la ficha del cliente
|
||||||
|
ClaimGreugeDescription: Id reclamación
|
||||||
|
Id item: Id artículo
|
||||||
|
confirmGreuges: ¿Desea insertar greuges?
|
||||||
|
confirmGreugesMessage: Insertar greuges en la ficha del cliente
|
||||||
|
</i18n>
|
|
@ -6,6 +6,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import FormModel from 'components/FormModel.vue';
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -90,138 +91,119 @@ const statesFilter = {
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<FetchData url="ClaimStates" @on-fetch="setClaimStates" auto-load />
|
<FetchData url="ClaimStates" @on-fetch="setClaimStates" auto-load />
|
||||||
|
<FormModel
|
||||||
<div class="column items-center">
|
:url="`Claims/${route.params.id}`"
|
||||||
<QCard>
|
:url-update="`Claims/updateClaim/${route.params.id}`"
|
||||||
<FormModel
|
:filter="claimFilter"
|
||||||
:url="`Claims/${route.params.id}`"
|
model="claim"
|
||||||
:url-update="`Claims/updateClaim/${route.params.id}`"
|
>
|
||||||
:filter="claimFilter"
|
<template #form="{ data, validate, filter }">
|
||||||
model="claim"
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
>
|
<div class="col">
|
||||||
<template #form="{ data, validate, filter }">
|
<QInput
|
||||||
<div class="row q-gutter-md q-mb-md">
|
v-model="data.client.name"
|
||||||
<div class="col">
|
:label="t('claim.basicData.customer')"
|
||||||
<QInput
|
disable
|
||||||
v-model="data.client.name"
|
/>
|
||||||
:label="t('claim.basicData.customer')"
|
</div>
|
||||||
disable
|
<div class="col">
|
||||||
/>
|
<QInput
|
||||||
</div>
|
v-model="data.created"
|
||||||
<div class="col">
|
mask="####-##-##"
|
||||||
<QInput
|
fill-mask="_"
|
||||||
v-model="data.created"
|
autofocus
|
||||||
mask="####-##-##"
|
>
|
||||||
fill-mask="_"
|
<template #append>
|
||||||
autofocus
|
<QIcon name="event" class="cursor-pointer">
|
||||||
>
|
<QPopupProxy
|
||||||
<template #append>
|
cover
|
||||||
<QIcon name="event" class="cursor-pointer">
|
transition-show="scale"
|
||||||
<QPopupProxy
|
transition-hide="scale"
|
||||||
cover
|
>
|
||||||
transition-show="scale"
|
<QDate v-model="data.created" mask="YYYY-MM-DD">
|
||||||
transition-hide="scale"
|
<div class="row items-center justify-end">
|
||||||
>
|
<QBtn
|
||||||
<QDate
|
v-close-popup
|
||||||
v-model="data.created"
|
label="Close"
|
||||||
mask="YYYY-MM-DD"
|
color="primary"
|
||||||
>
|
flat
|
||||||
<div class="row items-center justify-end">
|
/>
|
||||||
<QBtn
|
</div>
|
||||||
v-close-popup
|
</QDate>
|
||||||
label="Close"
|
</QPopupProxy>
|
||||||
color="primary"
|
</QIcon>
|
||||||
flat
|
</template>
|
||||||
/>
|
</QInput>
|
||||||
</div>
|
</div>
|
||||||
</QDate>
|
</VnRow>
|
||||||
</QPopupProxy>
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
</QIcon>
|
<div class="col">
|
||||||
</template>
|
<QSelect
|
||||||
</QInput>
|
v-model="data.workerFk"
|
||||||
</div>
|
:options="workers"
|
||||||
</div>
|
option-value="id"
|
||||||
<div class="row q-gutter-md q-mb-md">
|
option-label="name"
|
||||||
<div class="col">
|
emit-value
|
||||||
<QSelect
|
:label="t('claim.basicData.assignedTo')"
|
||||||
v-model="data.workerFk"
|
map-options
|
||||||
:options="workers"
|
use-input
|
||||||
option-value="id"
|
@filter="(value, update) => filter(value, update, workerFilter)"
|
||||||
option-label="name"
|
:rules="validate('claim.claimStateFk')"
|
||||||
emit-value
|
:input-debounce="0"
|
||||||
:label="t('claim.basicData.assignedTo')"
|
>
|
||||||
map-options
|
<template #before>
|
||||||
use-input
|
<QAvatar color="orange">
|
||||||
@filter="
|
<QImg
|
||||||
(value, update) => filter(value, update, workerFilter)
|
v-if="data.workerFk"
|
||||||
"
|
:src="`/api/Images/user/160x160/${data.workerFk}/download?access_token=${token}`"
|
||||||
:rules="validate('claim.claimStateFk')"
|
spinner-color="white"
|
||||||
:input-debounce="0"
|
/>
|
||||||
>
|
</QAvatar>
|
||||||
<template #before>
|
</template>
|
||||||
<QAvatar color="orange">
|
</QSelect>
|
||||||
<QImg
|
</div>
|
||||||
v-if="data.workerFk"
|
<div class="col">
|
||||||
:src="`/api/Images/user/160x160/${data.workerFk}/download?access_token=${token}`"
|
<QSelect
|
||||||
spinner-color="white"
|
v-model="data.claimStateFk"
|
||||||
/>
|
:options="claimStates"
|
||||||
</QAvatar>
|
option-value="id"
|
||||||
</template>
|
option-label="description"
|
||||||
</QSelect>
|
emit-value
|
||||||
</div>
|
:label="t('claim.basicData.state')"
|
||||||
<div class="col">
|
map-options
|
||||||
<QSelect
|
use-input
|
||||||
v-model="data.claimStateFk"
|
@filter="(value, update) => filter(value, update, statesFilter)"
|
||||||
:options="claimStates"
|
:rules="validate('claim.claimStateFk')"
|
||||||
option-value="id"
|
:input-debounce="0"
|
||||||
option-label="description"
|
>
|
||||||
emit-value
|
</QSelect>
|
||||||
:label="t('claim.basicData.state')"
|
</div>
|
||||||
map-options
|
</VnRow>
|
||||||
use-input
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
@filter="
|
<div class="col">
|
||||||
(value, update) => filter(value, update, statesFilter)
|
<QInput
|
||||||
"
|
v-model.number="data.packages"
|
||||||
:rules="validate('claim.claimStateFk')"
|
:label="t('claim.basicData.packages')"
|
||||||
:input-debounce="0"
|
:rules="validate('claim.packages')"
|
||||||
>
|
type="number"
|
||||||
</QSelect>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="col">
|
||||||
<div class="row q-gutter-md q-mb-md">
|
<QInput
|
||||||
<div class="col">
|
v-model="data.rma"
|
||||||
<QInput
|
:label="t('claim.basicData.returnOfMaterial')"
|
||||||
v-model.number="data.packages"
|
:rules="validate('claim.rma')"
|
||||||
:label="t('claim.basicData.packages')"
|
/>
|
||||||
:rules="validate('claim.packages')"
|
</div>
|
||||||
type="number"
|
</VnRow>
|
||||||
/>
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
</div>
|
<div class="col">
|
||||||
<div class="col">
|
<QCheckbox
|
||||||
<QInput
|
v-model="data.hasToPickUp"
|
||||||
v-model="data.rma"
|
:label="t('claim.basicData.picked')"
|
||||||
:label="t('claim.basicData.returnOfMaterial')"
|
/>
|
||||||
:rules="validate('claim.rma')"
|
</div>
|
||||||
/>
|
</VnRow>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</FormModel>
|
||||||
<div class="row q-gutter-md q-mb-md">
|
|
||||||
<div class="col">
|
|
||||||
<QCheckbox
|
|
||||||
v-model="data.hasToPickUp"
|
|
||||||
:label="t('claim.basicData.picked')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</FormModel>
|
|
||||||
</QCard>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.q-card {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 60em;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -22,11 +22,6 @@ const $props = defineProps({
|
||||||
const entityId = computed(() => {
|
const entityId = computed(() => {
|
||||||
return $props.id || route.params.id;
|
return $props.id || route.params.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
let salixUrl;
|
|
||||||
onMounted(async () => {
|
|
||||||
salixUrl = await getUrl(`claim/${entityId.value}`);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||||
|
@ -42,18 +37,6 @@ onMounted(async () => {
|
||||||
<ClaimDescriptor />
|
<ClaimDescriptor />
|
||||||
<QSeparator />
|
<QSeparator />
|
||||||
<LeftMenu source="card" />
|
<LeftMenu source="card" />
|
||||||
<QSeparator />
|
|
||||||
<QList>
|
|
||||||
<QItem
|
|
||||||
active-class="text-primary"
|
|
||||||
clickable
|
|
||||||
v-ripple
|
|
||||||
:href="`${salixUrl}/action`"
|
|
||||||
>
|
|
||||||
<QItemSection avatar><QIcon name="vn:actions"></QIcon></QItemSection>
|
|
||||||
<QItemSection>{{ t('Action') }}</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</QList>
|
|
||||||
</QScrollArea>
|
</QScrollArea>
|
||||||
</QDrawer>
|
</QDrawer>
|
||||||
<QPageContainer>
|
<QPageContainer>
|
||||||
|
|
|
@ -62,13 +62,18 @@ const filter = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const STATE_COLOR = {
|
||||||
|
pending: 'positive',
|
||||||
|
managed: 'warning',
|
||||||
|
resolved: 'negative',
|
||||||
|
};
|
||||||
|
|
||||||
function stateColor(code) {
|
function stateColor(code) {
|
||||||
if (code === 'pending') return 'positive';
|
return STATE_COLOR[code];
|
||||||
if (code === 'managed') return 'warning';
|
|
||||||
if (code === 'resolved') return 'negative';
|
|
||||||
}
|
}
|
||||||
const data = ref(useCardDescription());
|
const data = ref(useCardDescription());
|
||||||
const setData = (entity) => {
|
const setData = (entity) => {
|
||||||
|
if (!entity) return;
|
||||||
data.value = useCardDescription(entity.client.name, entity.id);
|
data.value = useCardDescription(entity.client.name, entity.id);
|
||||||
state.set('ClaimDescriptor', entity);
|
state.set('ClaimDescriptor', entity);
|
||||||
};
|
};
|
||||||
|
@ -83,6 +88,7 @@ const setData = (entity) => {
|
||||||
:title="data.title"
|
:title="data.title"
|
||||||
:subtitle="data.subtitle"
|
:subtitle="data.subtitle"
|
||||||
@on-fetch="setData"
|
@on-fetch="setData"
|
||||||
|
data-key="claimData"
|
||||||
>
|
>
|
||||||
<template #menu="{ entity }">
|
<template #menu="{ entity }">
|
||||||
<ClaimDescriptorMenu :claim="entity" />
|
<ClaimDescriptorMenu :claim="entity" />
|
||||||
|
@ -120,16 +126,16 @@ const setData = (entity) => {
|
||||||
<VnLv :label="t('claim.card.commercial')">
|
<VnLv :label="t('claim.card.commercial')">
|
||||||
<template #value>
|
<template #value>
|
||||||
<span class="link">
|
<span class="link">
|
||||||
{{ entity.client.salesPersonUser.name }}
|
{{ entity.client?.salesPersonUser?.name }}
|
||||||
<WorkerDescriptorProxy :id="entity.client.salesPersonFk" />
|
<WorkerDescriptorProxy :id="entity.client?.salesPersonFk" />
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</VnLv>
|
</VnLv>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('claim.card.province')"
|
:label="t('claim.card.province')"
|
||||||
:value="entity.ticket.address.province.name"
|
:value="entity.ticket?.address?.province?.name"
|
||||||
/>
|
/>
|
||||||
<VnLv :label="t('claim.card.zone')" :value="entity.ticket.zone.name" />
|
<VnLv :label="t('claim.card.zone')" :value="entity.ticket?.zone?.name" />
|
||||||
</template>
|
</template>
|
||||||
<template #actions="{ entity }">
|
<template #actions="{ entity }">
|
||||||
<QCardActions>
|
<QCardActions>
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import CrudModel from 'components/CrudModel.vue';
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import { getUrl } from 'composables/getUrl';
|
||||||
|
import { tMobile } from 'composables/tMobile';
|
||||||
|
import router from 'src/router';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const claimDevelopmentForm = ref();
|
const claimDevelopmentForm = ref();
|
||||||
|
@ -16,6 +20,12 @@ const claimResponsibles = ref([]);
|
||||||
const claimRedeliveries = ref([]);
|
const claimRedeliveries = ref([]);
|
||||||
const workers = ref([]);
|
const workers = ref([]);
|
||||||
const selected = ref([]);
|
const selected = ref([]);
|
||||||
|
const saveButtonRef = ref();
|
||||||
|
let salixUrl;
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
salixUrl = await getUrl(`claim/${route.params.id}`);
|
||||||
|
});
|
||||||
|
|
||||||
const developmentsFilter = {
|
const developmentsFilter = {
|
||||||
fields: [
|
fields: [
|
||||||
|
@ -43,6 +53,7 @@ const columns = computed(() => [
|
||||||
model: 'claimReasonFk',
|
model: 'claimReasonFk',
|
||||||
optionValue: 'id',
|
optionValue: 'id',
|
||||||
optionLabel: 'description',
|
optionLabel: 'description',
|
||||||
|
tabIndex: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'claimResult',
|
name: 'claimResult',
|
||||||
|
@ -54,6 +65,7 @@ const columns = computed(() => [
|
||||||
model: 'claimResultFk',
|
model: 'claimResultFk',
|
||||||
optionValue: 'id',
|
optionValue: 'id',
|
||||||
optionLabel: 'description',
|
optionLabel: 'description',
|
||||||
|
tabIndex: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'claimResponsible',
|
name: 'claimResponsible',
|
||||||
|
@ -65,6 +77,7 @@ const columns = computed(() => [
|
||||||
model: 'claimResponsibleFk',
|
model: 'claimResponsibleFk',
|
||||||
optionValue: 'id',
|
optionValue: 'id',
|
||||||
optionLabel: 'description',
|
optionLabel: 'description',
|
||||||
|
tabIndex: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'worker',
|
name: 'worker',
|
||||||
|
@ -75,6 +88,7 @@ const columns = computed(() => [
|
||||||
model: 'workerFk',
|
model: 'workerFk',
|
||||||
optionValue: 'id',
|
optionValue: 'id',
|
||||||
optionLabel: 'nickname',
|
optionLabel: 'nickname',
|
||||||
|
tabIndex: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'claimRedelivery',
|
name: 'claimRedelivery',
|
||||||
|
@ -86,6 +100,7 @@ const columns = computed(() => [
|
||||||
model: 'claimRedeliveryFk',
|
model: 'claimRedeliveryFk',
|
||||||
optionValue: 'id',
|
optionValue: 'id',
|
||||||
optionLabel: 'description',
|
optionLabel: 'description',
|
||||||
|
tabIndex: 5,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
</script>
|
</script>
|
||||||
|
@ -115,8 +130,9 @@ const columns = computed(() => [
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<FetchData
|
<FetchData
|
||||||
url="Workers/activeWithInheritedRole"
|
url="Workers/search"
|
||||||
:where="{ role: 'employee' }"
|
:where="{ active: 1 }"
|
||||||
|
order="name ASC"
|
||||||
@on-fetch="(data) => (workers = data)"
|
@on-fetch="(data) => (workers = data)"
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
|
@ -129,6 +145,8 @@ const columns = computed(() => [
|
||||||
:data-required="{ claimFk: route.params.id }"
|
:data-required="{ claimFk: route.params.id }"
|
||||||
v-model:selected="selected"
|
v-model:selected="selected"
|
||||||
auto-load
|
auto-load
|
||||||
|
@save-changes="$router.push(`/claim/${route.params.id}/action`)"
|
||||||
|
:default-save="false"
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<QTable
|
<QTable
|
||||||
|
@ -142,19 +160,41 @@ const columns = computed(() => [
|
||||||
:grid="$q.screen.lt.md"
|
:grid="$q.screen.lt.md"
|
||||||
>
|
>
|
||||||
<template #body-cell="{ row, col }">
|
<template #body-cell="{ row, col }">
|
||||||
<QTd auto-width>
|
<QTd
|
||||||
|
auto-width
|
||||||
|
@keyup.ctrl.enter.stop="claimDevelopmentForm.saveChanges()"
|
||||||
|
>
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="col.label"
|
:label="col.label"
|
||||||
v-model="row[col.model]"
|
v-model="row[col.model]"
|
||||||
:options="col.options"
|
:options="col.options"
|
||||||
:option-value="col.optionValue"
|
:option-value="col.optionValue"
|
||||||
:option-label="col.optionLabel"
|
:option-label="col.optionLabel"
|
||||||
/>
|
:autofocus="col.tabIndex == 1"
|
||||||
|
input-debounce="0"
|
||||||
|
hide-selected
|
||||||
|
>
|
||||||
|
<template #option="scope" v-if="col.name == 'worker'">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ scope.opt?.nickname }}
|
||||||
|
{{ scope.opt?.code }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #item="props">
|
<template #item="props">
|
||||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||||
<QCard bordered flat>
|
<QCard
|
||||||
|
bordered
|
||||||
|
flat
|
||||||
|
@keyup.ctrl.enter.stop="claimDevelopmentForm?.saveChanges()"
|
||||||
|
>
|
||||||
<QCardSection>
|
<QCardSection>
|
||||||
<QCheckbox v-model="props.selected" dense />
|
<QCheckbox v-model="props.selected" dense />
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
|
@ -169,6 +209,9 @@ const columns = computed(() => [
|
||||||
:option-value="col.optionValue"
|
:option-value="col.optionValue"
|
||||||
:option-label="col.optionLabel"
|
:option-label="col.optionLabel"
|
||||||
dense
|
dense
|
||||||
|
input-debounce="0"
|
||||||
|
:autofocus="col.tabIndex == 1"
|
||||||
|
hide-selected
|
||||||
/>
|
/>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
|
@ -178,9 +221,26 @@ const columns = computed(() => [
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
</template>
|
</template>
|
||||||
|
<template #moreAfterActions>
|
||||||
|
<QBtn
|
||||||
|
:label="tMobile('globals.save')"
|
||||||
|
ref="saveButtonRef"
|
||||||
|
color="primary"
|
||||||
|
icon="save"
|
||||||
|
:disable="!claimDevelopmentForm?.hasChanges"
|
||||||
|
@click="claimDevelopmentForm?.onSubmit"
|
||||||
|
:title="t('globals.save')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</CrudModel>
|
</CrudModel>
|
||||||
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
||||||
<QBtn fab color="primary" icon="add" @click="claimDevelopmentForm.insert()" />
|
<QBtn
|
||||||
|
fab
|
||||||
|
color="primary"
|
||||||
|
icon="add"
|
||||||
|
@keydown.tab.prevent="saveButtonRef.$el.focus()"
|
||||||
|
@click="claimDevelopmentForm.insert()"
|
||||||
|
/>
|
||||||
</QPageSticky>
|
</QPageSticky>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -188,9 +248,6 @@ const columns = computed(() => [
|
||||||
.grid-style-transition {
|
.grid-style-transition {
|
||||||
transition: transform 0.28s, background-color 0.28s;
|
transition: transform 0.28s, background-color 0.28s;
|
||||||
}
|
}
|
||||||
.maxwidth {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
|
|
|
@ -40,14 +40,13 @@ const claimLinesForm = ref();
|
||||||
const claim = ref(null);
|
const claim = ref(null);
|
||||||
async function onFetchClaim(data) {
|
async function onFetchClaim(data) {
|
||||||
claim.value = data;
|
claim.value = data;
|
||||||
|
|
||||||
fetchMana();
|
fetchMana();
|
||||||
}
|
}
|
||||||
|
|
||||||
const amount = ref(0);
|
const amount = ref(0);
|
||||||
const amountClaimed = ref(0);
|
const amountClaimed = ref(0);
|
||||||
async function onFetch(rows) {
|
async function onFetch(rows) {
|
||||||
if (!rows || rows.length) return;
|
if (!rows || !rows.length) return;
|
||||||
amount.value = rows.reduce(
|
amount.value = rows.reduce(
|
||||||
(acumulator, { sale }) => acumulator + sale.price * sale.quantity,
|
(acumulator, { sale }) => acumulator + sale.price * sale.quantity,
|
||||||
0
|
0
|
||||||
|
@ -147,13 +146,16 @@ function showImportDialog() {
|
||||||
quasar
|
quasar
|
||||||
.dialog({
|
.dialog({
|
||||||
component: ClaimLinesImport,
|
component: ClaimLinesImport,
|
||||||
|
componentProps: {
|
||||||
|
ticketId: claim.value.ticketFk,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.onOk(() => arrayData.refresh());
|
.onOk(() => claimLinesForm.value.reload());
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()">
|
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()">
|
||||||
<QToolbar class="bg-dark text-white">
|
<QToolbar>
|
||||||
<div class="row q-gutter-md">
|
<div class="row q-gutter-md">
|
||||||
<div>
|
<div>
|
||||||
{{ t('Amount') }}
|
{{ t('Amount') }}
|
||||||
|
|
|
@ -14,6 +14,13 @@ const route = useRoute();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
ticketId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
name: 'delivered',
|
name: 'delivered',
|
||||||
|
@ -99,7 +106,7 @@ function cancel() {
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
url="Sales/getClaimableFromTicket?ticketFk=16"
|
:url="`Sales/getClaimableFromTicket?ticketFk=${$props.ticketId}`"
|
||||||
@on-fetch="(data) => (claimableSales = data)"
|
@on-fetch="(data) => (claimableSales = data)"
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -85,10 +85,15 @@ const detailsColumns = ref([
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const STATE_COLOR = {
|
||||||
|
pending: 'positive',
|
||||||
|
|
||||||
|
managed: 'warning',
|
||||||
|
|
||||||
|
resolved: 'negative',
|
||||||
|
};
|
||||||
function stateColor(code) {
|
function stateColor(code) {
|
||||||
if (code === 'pending') return 'green';
|
return STATE_COLOR[code];
|
||||||
if (code === 'managed') return 'orange';
|
|
||||||
if (code === 'resolved') return 'red';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const developmentColumns = ref([
|
const developmentColumns = ref([
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -60,7 +61,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers">
|
<QItemSection v-if="workers">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Salesperson')"
|
:label="t('Salesperson')"
|
||||||
v-model="params.salesPersonFk"
|
v-model="params.salesPersonFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -79,7 +80,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers">
|
<QItemSection v-if="workers">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Attender')"
|
:label="t('Attender')"
|
||||||
v-model="params.attenderFk"
|
v-model="params.attenderFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -98,7 +99,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers">
|
<QItemSection v-if="workers">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Responsible')"
|
:label="t('Responsible')"
|
||||||
v-model="params.claimResponsibleFk"
|
v-model="params.claimResponsibleFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -117,7 +118,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="states">
|
<QItemSection v-if="states">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('State')"
|
:label="t('State')"
|
||||||
v-model="params.claimStateFk"
|
v-model="params.claimStateFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
|
|
@ -17,12 +17,14 @@ const router = useRouter();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const STATE_COLOR = {
|
||||||
|
pending: 'positive',
|
||||||
|
managed: 'warning',
|
||||||
|
resolved: 'negative',
|
||||||
|
};
|
||||||
function stateColor(code) {
|
function stateColor(code) {
|
||||||
if (code === 'pending') return 'green';
|
return STATE_COLOR[code];
|
||||||
if (code === 'managed') return 'orange';
|
|
||||||
if (code === 'resolved') return 'red';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigate(id) {
|
function navigate(id) {
|
||||||
router.push({ path: `/claim/${id}` });
|
router.push({ path: `/claim/${id}` });
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import FormModel from 'components/FormModel.vue';
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -58,121 +59,109 @@ const filterOptions = {
|
||||||
@on-fetch="(data) => (businessTypes = data)"
|
@on-fetch="(data) => (businessTypes = data)"
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<div class="column items-center">
|
|
||||||
<QCard>
|
|
||||||
<FormModel :url="`Clients/${route.params.id}`" model="customer">
|
|
||||||
<template #form="{ data, validate, filter }">
|
|
||||||
<div class="row q-gutter-md q-mb-md">
|
|
||||||
<div class="col">
|
|
||||||
<QInput
|
|
||||||
v-model="data.socialName"
|
|
||||||
:label="t('customer.basicData.socialName')"
|
|
||||||
:rules="validate('client.socialName')"
|
|
||||||
autofocus
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<QSelect
|
|
||||||
v-model="data.businessTypeFk"
|
|
||||||
:options="businessTypes"
|
|
||||||
option-value="code"
|
|
||||||
option-label="description"
|
|
||||||
emit-value
|
|
||||||
:label="t('customer.basicData.businessType')"
|
|
||||||
map-options
|
|
||||||
:rules="validate('client.businessTypeFk')"
|
|
||||||
:input-debounce="0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row q-gutter-md q-mb-md">
|
|
||||||
<div class="col">
|
|
||||||
<QInput
|
|
||||||
v-model="data.contact"
|
|
||||||
:label="t('customer.basicData.contact')"
|
|
||||||
:rules="validate('client.contact')"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<QInput
|
|
||||||
v-model="data.email"
|
|
||||||
type="email"
|
|
||||||
:label="t('customer.basicData.email')"
|
|
||||||
:rules="validate('client.email')"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row q-gutter-md q-mb-md">
|
|
||||||
<div class="col">
|
|
||||||
<QInput
|
|
||||||
v-model="data.phone"
|
|
||||||
:label="t('customer.basicData.phone')"
|
|
||||||
:rules="validate('client.phone')"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<QInput
|
|
||||||
v-model="data.mobile"
|
|
||||||
:label="t('customer.basicData.mobile')"
|
|
||||||
:rules="validate('client.mobile')"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row q-gutter-md q-mb-md">
|
|
||||||
<div class="col">
|
|
||||||
<QSelect
|
|
||||||
v-model="data.salesPersonFk"
|
|
||||||
:options="workers"
|
|
||||||
option-value="id"
|
|
||||||
option-label="name"
|
|
||||||
emit-value
|
|
||||||
:label="t('customer.basicData.salesPerson')"
|
|
||||||
map-options
|
|
||||||
use-input
|
|
||||||
@filter="
|
|
||||||
(value, update) =>
|
|
||||||
filter(value, update, filterOptions)
|
|
||||||
"
|
|
||||||
:rules="validate('client.salesPersonFk')"
|
|
||||||
:input-debounce="0"
|
|
||||||
>
|
|
||||||
<template #prepend>
|
|
||||||
<QAvatar color="orange">
|
|
||||||
<QImg
|
|
||||||
v-if="data.salesPersonFk"
|
|
||||||
:src="`/api/Images/user/160x160/${data.salesPersonFk}/download?access_token=${token}`"
|
|
||||||
spinner-color="white"
|
|
||||||
/>
|
|
||||||
</QAvatar>
|
|
||||||
</template>
|
|
||||||
</QSelect>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<QSelect
|
|
||||||
v-model="data.contactChannelFk"
|
|
||||||
:options="contactChannels"
|
|
||||||
option-value="id"
|
|
||||||
option-label="name"
|
|
||||||
emit-value
|
|
||||||
:label="t('customer.basicData.contactChannel')"
|
|
||||||
map-options
|
|
||||||
:rules="validate('client.contactChannelFk')"
|
|
||||||
:input-debounce="0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</FormModel>
|
|
||||||
</QCard>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<FormModel :url="`Clients/${route.params.id}`" model="customer">
|
||||||
.q-card {
|
<template #form="{ data, validate, filter }">
|
||||||
width: 800px;
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
}
|
<div class="col">
|
||||||
</style>
|
<QInput
|
||||||
|
v-model="data.socialName"
|
||||||
|
:label="t('customer.basicData.socialName')"
|
||||||
|
:rules="validate('client.socialName')"
|
||||||
|
autofocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSelect
|
||||||
|
v-model="data.businessTypeFk"
|
||||||
|
:options="businessTypes"
|
||||||
|
option-value="code"
|
||||||
|
option-label="description"
|
||||||
|
emit-value
|
||||||
|
:label="t('customer.basicData.businessType')"
|
||||||
|
map-options
|
||||||
|
:rules="validate('client.businessTypeFk')"
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
v-model="data.contact"
|
||||||
|
:label="t('customer.basicData.contact')"
|
||||||
|
:rules="validate('client.contact')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
v-model="data.email"
|
||||||
|
type="email"
|
||||||
|
:label="t('customer.basicData.email')"
|
||||||
|
:rules="validate('client.email')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
v-model="data.phone"
|
||||||
|
:label="t('customer.basicData.phone')"
|
||||||
|
:rules="validate('client.phone')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
v-model="data.mobile"
|
||||||
|
:label="t('customer.basicData.mobile')"
|
||||||
|
:rules="validate('client.mobile')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QSelect
|
||||||
|
v-model="data.salesPersonFk"
|
||||||
|
:options="workers"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
:label="t('customer.basicData.salesPerson')"
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
@filter="(value, update) => filter(value, update, filterOptions)"
|
||||||
|
:rules="validate('client.salesPersonFk')"
|
||||||
|
:input-debounce="0"
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<QAvatar color="orange">
|
||||||
|
<QImg
|
||||||
|
v-if="data.salesPersonFk"
|
||||||
|
:src="`/api/Images/user/160x160/${data.salesPersonFk}/download?access_token=${token}`"
|
||||||
|
spinner-color="white"
|
||||||
|
/>
|
||||||
|
</QAvatar>
|
||||||
|
</template>
|
||||||
|
</QSelect>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSelect
|
||||||
|
v-model="data.contactChannelFk"
|
||||||
|
:options="contactChannels"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
:label="t('customer.basicData.contactChannel')"
|
||||||
|
map-options
|
||||||
|
:rules="validate('client.contactChannelFk')"
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
||||||
|
|
|
@ -34,6 +34,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity
|
||||||
:title="data.title"
|
:title="data.title"
|
||||||
:subtitle="data.subtitle"
|
:subtitle="data.subtitle"
|
||||||
@on-fetch="setData"
|
@on-fetch="setData"
|
||||||
|
data-key="customerData"
|
||||||
>
|
>
|
||||||
<template #body="{ entity }">
|
<template #body="{ entity }">
|
||||||
<VnLv v-if="entity.salesPersonUser" :label="t('customer.card.salesPerson')">
|
<VnLv v-if="entity.salesPersonUser" :label="t('customer.card.salesPerson')">
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import { reactive, watch } from 'vue';
|
|
||||||
|
|
||||||
const customer = reactive({
|
|
||||||
name: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(() => customer.name);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<QPage class="q-pa-md">
|
|
||||||
<QCard class="q-pa-md">
|
|
||||||
<QForm @submit="onSubmit" @reset="onReset" class="q-gutter-md">
|
|
||||||
<QInput
|
|
||||||
filled
|
|
||||||
v-model="customer.name"
|
|
||||||
label="Your name *"
|
|
||||||
hint="Name and surname"
|
|
||||||
lazy-rules
|
|
||||||
:rules="[(val) => (val && val.length > 0) || 'Please type something']"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<QInput
|
|
||||||
filled
|
|
||||||
type="number"
|
|
||||||
v-model="age"
|
|
||||||
label="Your age *"
|
|
||||||
lazy-rules
|
|
||||||
:rules="[
|
|
||||||
(val) => (val !== null && val !== '') || 'Please type your age',
|
|
||||||
(val) => (val > 0 && val < 100) || 'Please type a real age',
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<QBtn label="Submit" type="submit" color="primary" />
|
|
||||||
<QBtn
|
|
||||||
label="Reset"
|
|
||||||
type="reset"
|
|
||||||
color="primary"
|
|
||||||
flat
|
|
||||||
class="q-ml-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</QForm>
|
|
||||||
</QCard>
|
|
||||||
</QPage>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.card {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 60em;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -3,6 +3,7 @@ import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -63,7 +64,7 @@ const zones = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers">
|
<QItemSection v-if="workers">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Salesperson')"
|
:label="t('Salesperson')"
|
||||||
v-model="params.salesPersonFk"
|
v-model="params.salesPersonFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -82,7 +83,7 @@ const zones = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="provinces">
|
<QItemSection v-if="provinces">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Province')"
|
:label="t('Province')"
|
||||||
v-model="params.provinceFk"
|
v-model="params.provinceFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -91,6 +92,7 @@ const zones = ref();
|
||||||
option-label="name"
|
option-label="name"
|
||||||
emit-value
|
emit-value
|
||||||
map-options
|
map-options
|
||||||
|
:input-debounce="0"
|
||||||
/>
|
/>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
|
@ -124,7 +126,7 @@ const zones = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="zones">
|
<QItemSection v-if="zones">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Zone')"
|
:label="t('Zone')"
|
||||||
v-model="params.zoneFk"
|
v-model="params.zoneFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
|
|
@ -110,12 +110,18 @@ function stateColor(row) {
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
<QDrawer
|
||||||
|
v-model="stateStore.rightDrawer"
|
||||||
|
side="right"
|
||||||
|
:width="256"
|
||||||
|
show-if-above
|
||||||
|
:breakpoint="1600"
|
||||||
|
>
|
||||||
<QScrollArea class="fit text-grey-8">
|
<QScrollArea class="fit text-grey-8">
|
||||||
<CustomerPaymentsFilter data-key="CustomerTransactions" />
|
<CustomerPaymentsFilter data-key="CustomerTransactions" />
|
||||||
</QScrollArea>
|
</QScrollArea>
|
||||||
</QDrawer>
|
</QDrawer>
|
||||||
<QPage class="column items-center q-pa-md">
|
<QPage class="column items-center q-pa-md customer-payments">
|
||||||
<div class="card-list">
|
<div class="card-list">
|
||||||
<QToolbar class="q-pa-none">
|
<QToolbar class="q-pa-none">
|
||||||
<QToolbarTitle>{{ t('Web Payments') }}</QToolbarTitle>
|
<QToolbarTitle>{{ t('Web Payments') }}</QToolbarTitle>
|
||||||
|
@ -138,7 +144,7 @@ function stateColor(row) {
|
||||||
order="created DESC"
|
order="created DESC"
|
||||||
:limit="20"
|
:limit="20"
|
||||||
:offset="50"
|
:offset="50"
|
||||||
auto-load
|
:auto-load="!!$route?.query.params"
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<QTable
|
<QTable
|
||||||
|
@ -148,7 +154,7 @@ function stateColor(row) {
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
:grid="grid || $q.screen.lt.sm"
|
:grid="grid || $q.screen.lt.sm"
|
||||||
class="q-mt-xs"
|
class="q-mt-xs custom-table"
|
||||||
hide-pagination
|
hide-pagination
|
||||||
>
|
>
|
||||||
<template #body-cell-actions="{ row }">
|
<template #body-cell-actions="{ row }">
|
||||||
|
@ -167,6 +173,13 @@ function stateColor(row) {
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
|
<template #body-cell-id="{ row }">
|
||||||
|
<QTd auto-width align="right">
|
||||||
|
<span>
|
||||||
|
{{ row.id }}
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
<template #body-cell-customerId="{ row }">
|
<template #body-cell-customerId="{ row }">
|
||||||
<QTd align="right">
|
<QTd align="right">
|
||||||
<span class="link">
|
<span class="link">
|
||||||
|
@ -175,6 +188,13 @@ function stateColor(row) {
|
||||||
</span>
|
</span>
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
|
<template #body-cell-customer="{ row }">
|
||||||
|
<QTd auto-width align="left" :title="row.customerName">
|
||||||
|
<span>
|
||||||
|
{{ row.customerName }}
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
<template #body-cell-state="{ row }">
|
<template #body-cell-state="{ row }">
|
||||||
<QTd auto-width class="text-center">
|
<QTd auto-width class="text-center">
|
||||||
<QBadge :color="stateColor(row)">
|
<QBadge :color="stateColor(row)">
|
||||||
|
@ -188,9 +208,9 @@ function stateColor(row) {
|
||||||
</template>
|
</template>
|
||||||
<template #item="{ cols, row }">
|
<template #item="{ cols, row }">
|
||||||
<div class="q-mb-md col-12">
|
<div class="q-mb-md col-12">
|
||||||
<QCard>
|
<QCard class="q-pa-none">
|
||||||
<QItem class="q-pa-none items-start">
|
<QItem class="q-pa-none items-start">
|
||||||
<QItemSection class="q-pa-md">
|
<QItemSection class="q-pa-none">
|
||||||
<QList>
|
<QList>
|
||||||
<template
|
<template
|
||||||
v-for="col of cols"
|
v-for="col of cols"
|
||||||
|
@ -257,10 +277,21 @@ function stateColor(row) {
|
||||||
</QPage>
|
</QPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss">
|
||||||
.card-list {
|
.customer-payments {
|
||||||
width: 100%;
|
.card-list {
|
||||||
max-width: 60em;
|
width: 100%;
|
||||||
|
max-width: 60em;
|
||||||
|
|
||||||
|
.q-table--dense .q-table th:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
max-width: 130px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,14 @@ const props = defineProps({
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function isValidNumber(value) {
|
||||||
|
return /^(\d|\d+(\.|,)?\d+)$/.test(value);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
<VnFilterPanel :data-key="props.dataKey" :search-button="true" :show-all="false">
|
||||||
<template #tags="{ tag, formatFn }">
|
<template #tags="{ tag, formatFn }">
|
||||||
<div class="q-gutter-x-xs">
|
<div class="q-gutter-x-xs">
|
||||||
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||||
|
@ -49,9 +53,99 @@ const props = defineProps({
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem>
|
<QItem>
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
<QInput :label="t('Amount')" v-model="params.amount" lazy-rules>
|
<QInput
|
||||||
|
:label="t('Amount')"
|
||||||
|
v-model="params.amount"
|
||||||
|
lazy-rules
|
||||||
|
@update:model-value="
|
||||||
|
(value) => {
|
||||||
|
if (value.includes(','))
|
||||||
|
params.amount = params.amount.replace(',', '.');
|
||||||
|
}
|
||||||
|
"
|
||||||
|
:rules="[
|
||||||
|
(val) =>
|
||||||
|
isValidNumber(val) || !val || 'Please type a number',
|
||||||
|
]"
|
||||||
|
>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<QIcon name="euro" size="sm"></QIcon>
|
<QIcon name="euro" size="sm" />
|
||||||
|
</template>
|
||||||
|
</QInput>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<QInput
|
||||||
|
v-model="params.from"
|
||||||
|
:label="t('From')"
|
||||||
|
mask="date"
|
||||||
|
placeholder="yyyy/mm/dd"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<QIcon name="event" class="cursor-pointer">
|
||||||
|
<QPopupProxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<QDate v-model="params.from" landscape>
|
||||||
|
<div
|
||||||
|
class="row items-center justify-end q-gutter-sm"
|
||||||
|
>
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</QDate>
|
||||||
|
</QPopupProxy>
|
||||||
|
</QIcon>
|
||||||
|
</template>
|
||||||
|
</QInput>
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>
|
||||||
|
<QInput
|
||||||
|
v-model="params.to"
|
||||||
|
:label="t('To')"
|
||||||
|
mask="date"
|
||||||
|
placeholder="yyyy/mm/dd"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<QIcon name="event" class="cursor-pointer">
|
||||||
|
<QPopupProxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<QDate v-model="params.to" landscape>
|
||||||
|
<div
|
||||||
|
class="row items-center justify-end q-gutter-sm"
|
||||||
|
>
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</QDate>
|
||||||
|
</QPopupProxy>
|
||||||
|
</QIcon>
|
||||||
</template>
|
</template>
|
||||||
</QInput>
|
</QInput>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
|
@ -67,12 +161,19 @@ en:
|
||||||
orderFk: Order
|
orderFk: Order
|
||||||
clientFk: Customer
|
clientFk: Customer
|
||||||
amount: Amount
|
amount: Amount
|
||||||
|
from: From
|
||||||
|
to: To
|
||||||
es:
|
es:
|
||||||
params:
|
params:
|
||||||
orderFk: Pedido
|
orderFk: Pedido
|
||||||
clientFk: Cliente
|
clientFk: Cliente
|
||||||
amount: Importe
|
amount: Importe
|
||||||
|
from: Desde
|
||||||
|
to: Hasta
|
||||||
Order ID: ID pedido
|
Order ID: ID pedido
|
||||||
Customer ID: ID cliente
|
Customer ID: ID cliente
|
||||||
Amount: Importe
|
Amount: Importe
|
||||||
|
Please type a number: Por favor, escriba un número
|
||||||
|
From: Desde
|
||||||
|
To: Hasta
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -57,6 +57,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
|
||||||
:title="data.title"
|
:title="data.title"
|
||||||
:subtitle="data.subtitle"
|
:subtitle="data.subtitle"
|
||||||
@on-fetch="setData"
|
@on-fetch="setData"
|
||||||
|
data-key="invoiceOutData"
|
||||||
>
|
>
|
||||||
<template #body="{ entity }">
|
<template #body="{ entity }">
|
||||||
<VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" />
|
<VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" />
|
||||||
|
|
|
@ -8,6 +8,8 @@ import axios from 'axios';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
import { useLogin } from 'src/composables/useLogin';
|
import { useLogin } from 'src/composables/useLogin';
|
||||||
|
|
||||||
|
import VnLogo from 'components/ui/VnLogo.vue';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const loginCache = useLogin();
|
const loginCache = useLogin();
|
||||||
|
@ -64,14 +66,8 @@ async function onSubmit() {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QImg
|
<QForm @submit="onSubmit" class="q-gutter-y-md q-pa-lg formCard">
|
||||||
src="~/assets/logo.svg"
|
<VnLogo alt="Logo" fit="contain" :ratio="16 / 9" class="q-mb-md" />
|
||||||
alt="Logo"
|
|
||||||
fit="contain"
|
|
||||||
:ratio="16 / 9"
|
|
||||||
class="q-mb-md"
|
|
||||||
/>
|
|
||||||
<QForm @submit="onSubmit" class="q-gutter-md q-pa-lg">
|
|
||||||
<QInput
|
<QInput
|
||||||
v-model="username"
|
v-model="username"
|
||||||
:label="t('login.username')"
|
:label="t('login.username')"
|
||||||
|
@ -100,4 +96,15 @@ async function onSubmit() {
|
||||||
</QForm>
|
</QForm>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
.formCard {
|
||||||
|
max-width: 350px;
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-xs-max) {
|
||||||
|
.formCard {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -48,7 +48,7 @@ async function onSubmit() {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QForm @submit="onSubmit" class="q-gutter-md q-pa-lg">
|
<QForm @submit="onSubmit" class="q-gutter-y-md q-pa-lg formCard">
|
||||||
<div class="column items-center">
|
<div class="column items-center">
|
||||||
<QIcon name="phonelink_lock" size="xl" color="primary" />
|
<QIcon name="phonelink_lock" size="xl" color="primary" />
|
||||||
<h5 class="text-center q-my-md">{{ t('twoFactor.insert') }}</h5>
|
<h5 class="text-center q-my-md">{{ t('twoFactor.insert') }}</h5>
|
||||||
|
@ -77,4 +77,15 @@ async function onSubmit() {
|
||||||
</div>
|
</div>
|
||||||
</QForm>
|
</QForm>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
.formCard {
|
||||||
|
max-width: 350px;
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-xs-max) {
|
||||||
|
.formCard {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import VnLogo from 'components/ui/VnLogo.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const redirectButtons = ref([]);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const params = route?.query;
|
||||||
|
const { data } = await axios.get(`Urls/${params.userId}/get-by-user`);
|
||||||
|
|
||||||
|
redirectButtons.value.push({
|
||||||
|
color: 'bg-vnColor',
|
||||||
|
icon: new URL(`../../assets/vn_icon.svg`, import.meta.url).href,
|
||||||
|
text: 'goToShop',
|
||||||
|
url: data.find((url) => url.appName == 'hedera').url,
|
||||||
|
});
|
||||||
|
|
||||||
|
const urls = data.filter((url) => url.appName != 'hedera');
|
||||||
|
if (urls.length) {
|
||||||
|
for (const url of urls) {
|
||||||
|
redirectButtons.value.push({
|
||||||
|
color: 'bg-primary',
|
||||||
|
icon: new URL(`../../assets/${url.appName}_icon.svg`, import.meta.url)
|
||||||
|
.href,
|
||||||
|
text: 'logIn',
|
||||||
|
url: url.url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="q-gutter-y-xl q-pa-lg formCard flex flex-center">
|
||||||
|
<VnLogo
|
||||||
|
logo="vn"
|
||||||
|
alt="Logo"
|
||||||
|
fit="fill"
|
||||||
|
class="q-px-xl q-py-md q-mb-xl logoWidth"
|
||||||
|
/>
|
||||||
|
<div class="text-h4 vnColor text-center text-weight-medium">
|
||||||
|
{{ t('verifyEmail') }}
|
||||||
|
</div>
|
||||||
|
<div class="q-gutter-md flex flex-center">
|
||||||
|
<QBtn
|
||||||
|
:class="button.color"
|
||||||
|
v-for="button of redirectButtons"
|
||||||
|
:key="button.id"
|
||||||
|
:href="button.url"
|
||||||
|
>
|
||||||
|
<div class="row items-center no-wrap q-gutter-md">
|
||||||
|
<div class="circle q-pa-sm" style="background-color: var(--vn-gray)">
|
||||||
|
<QImg :src="button.icon" class="q-pa-md" />
|
||||||
|
</div>
|
||||||
|
<div class="text-h5" style="color: var(--vn-gray)">
|
||||||
|
{{ t(button.text) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</QBtn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
{
|
||||||
|
"en": {
|
||||||
|
"verifyEmail": "Your email has been successfully verified. You can now log in to enjoy all the features of our platform.",
|
||||||
|
"goToShop": "Go to the shop",
|
||||||
|
"logIn": "Log In"
|
||||||
|
},
|
||||||
|
"es": {
|
||||||
|
"verifyEmail": "Su correo electrónico ha sido verificado con éxito. Ahora puede iniciar sesión para disfrutar de todas las funcionalidades de nuestra plataforma.",
|
||||||
|
"goToShop": "Ir a la tienda",
|
||||||
|
"logIn": "Iniciar sesión"
|
||||||
|
},
|
||||||
|
"fr": {
|
||||||
|
"verifyEmail": "Votre courrier électronique a été vérifié avec succès. Vous pouvez maintenant vous connecter pour profiter de toutes les fonctionnalités de notre plateforme.",
|
||||||
|
"goToShop": "Aller à la boutique",
|
||||||
|
"logIn": "Se connecter"
|
||||||
|
},
|
||||||
|
"pt": {
|
||||||
|
"verifyEmail": "Seu e-mail foi verificado com sucesso. Agora você pode fazer o login para aproveitar todas as funcionalidades da nossa plataforma.",
|
||||||
|
"goToShop": "Ir para a loja",
|
||||||
|
"logIn": "Fazer login"
|
||||||
|
},
|
||||||
|
"it": {
|
||||||
|
"verifyEmail": "La tua email è stata verificata con successo. Ora puoi accedere per godere di tutte le funzionalità della nostra piattaforma.",
|
||||||
|
"goToShop": "Vai al negozio",
|
||||||
|
"logIn": "Accedi"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</i18n>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.formCard {
|
||||||
|
max-width: 1500px;
|
||||||
|
min-width: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-xs-max) {
|
||||||
|
.formCard {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.vnColor {
|
||||||
|
color: $vnColor;
|
||||||
|
}
|
||||||
|
.bg-vnColor {
|
||||||
|
background-color: $vnColor;
|
||||||
|
}
|
||||||
|
.circle {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.logoWidth {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -81,6 +81,7 @@ const setData = (entity) =>
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
:title="data.title"
|
:title="data.title"
|
||||||
:subtitle="data.subtitle"
|
:subtitle="data.subtitle"
|
||||||
|
data-key="ticketData"
|
||||||
@on-fetch="setData"
|
@on-fetch="setData"
|
||||||
>
|
>
|
||||||
<template #menu="{ entity }">
|
<template #menu="{ entity }">
|
||||||
|
|
|
@ -87,7 +87,7 @@ function showSmsDialog(template, customData) {
|
||||||
componentProps: {
|
componentProps: {
|
||||||
phone: phone,
|
phone: phone,
|
||||||
template: template,
|
template: template,
|
||||||
locale: client.user.lang,
|
locale: client?.user?.lang ?? 'default_locale',
|
||||||
data: data,
|
data: data,
|
||||||
promise: sendSms,
|
promise: sendSms,
|
||||||
},
|
},
|
||||||
|
|
|
@ -173,7 +173,7 @@ async function changeState(value) {
|
||||||
:label="t('ticket.summary.agency')"
|
:label="t('ticket.summary.agency')"
|
||||||
:value="ticket.agencyMode.name"
|
:value="ticket.agencyMode.name"
|
||||||
/>
|
/>
|
||||||
<VnLv :label="t('ticket.summary.zone')" :value="ticket.zone.name" />
|
<VnLv :label="t('ticket.summary.zone')" :value="ticket?.zone?.name" />
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('ticket.summary.warehouse')"
|
:label="t('ticket.summary.warehouse')"
|
||||||
:value="ticket.warehouse?.name"
|
:value="ticket.warehouse?.name"
|
||||||
|
|
|
@ -68,7 +68,7 @@ function viewSummary(id) {
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256">
|
||||||
<QScrollArea class="fit text-grey-8">
|
<QScrollArea class="fit text-grey-8">
|
||||||
<TicketFilter data-key="TicketList" />
|
<TicketFilter data-key="TicketList" />
|
||||||
</QScrollArea>
|
</QScrollArea>
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
|
<QScrollArea class="fit">
|
||||||
|
<QSeparator />
|
||||||
|
<LeftMenu source="card" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<QPageContainer>
|
||||||
|
<QPage>
|
||||||
|
<div class="q-pa-md"><RouterView></RouterView></div>
|
||||||
|
</QPage>
|
||||||
|
</QPageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search customer: Buscar cliente
|
||||||
|
You can search by customer id or name: Puedes buscar por id o nombre del cliente
|
||||||
|
</i18n>
|
|
@ -0,0 +1,154 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const session = useSession();
|
||||||
|
const token = session.getToken();
|
||||||
|
|
||||||
|
const counters = ref({
|
||||||
|
alquilerBandeja: { count: 0, id: 96001, title: 'CC Bandeja', isTray: true },
|
||||||
|
bandejaRota: { count: 0, id: 88381, title: 'CC Bandeja Rota', isTray: true },
|
||||||
|
carryOficial: { count: 0, id: 96000, title: 'CC Carry OFICIAL TAG5' },
|
||||||
|
candadoRojo: { count: 0, id: 96002, title: 'CC Carry NO OFICIAL' },
|
||||||
|
sacadores: { count: 0, id: 142260, title: 'CC Sacadores' },
|
||||||
|
sinChapa: { count: 0, id: 2214, title: 'DC Carry Sin Placa CC' },
|
||||||
|
carroRoto: { count: 0, id: 142251, title: 'Carro Roto' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
add: (counter) => counter + 1,
|
||||||
|
subtract: (counter) => (counter ? counter - 1 : 0),
|
||||||
|
flush: () => 0,
|
||||||
|
addSpecific: (counter, amount) => counter + amount,
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const types = Object.keys(counters.value);
|
||||||
|
for (let type of types) {
|
||||||
|
const counter = localStorage.getItem(type);
|
||||||
|
counters.value[type].count = counter ? parseInt(counter) : 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getUrl(id) {
|
||||||
|
return `/api/Images/catalog/200x200/${id}/download?access_token=${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleEvent(type, action, amount) {
|
||||||
|
const counter = counters.value[type].count;
|
||||||
|
let isOk = true;
|
||||||
|
|
||||||
|
if (action == 'flush') isOk = await confirm();
|
||||||
|
|
||||||
|
if (isOk) {
|
||||||
|
counters.value[type].count = actions[action](counter, amount);
|
||||||
|
localStorage.setItem(type, counters.value[type].count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirm() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('Are you sure?'),
|
||||||
|
message: t('The counter will be reset to zero'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(() => resolve(true))
|
||||||
|
.onCancel(() => resolve(false));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QList class="row q-mx-auto q-mt-xl">
|
||||||
|
<QItem v-for="(props, name) in counters" :key="name" class="col-6">
|
||||||
|
<QItemSection>
|
||||||
|
<QImg
|
||||||
|
:src="getUrl(props.id)"
|
||||||
|
width="130px"
|
||||||
|
@click="handleEvent(name, 'add')"
|
||||||
|
/>
|
||||||
|
<p class="title">{{ props.title }}</p>
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection class="q-ma-none">
|
||||||
|
<QItemLabel class="text-h4">
|
||||||
|
{{ props.count }}
|
||||||
|
</QItemLabel>
|
||||||
|
<QItemLabel>
|
||||||
|
<QBtn
|
||||||
|
class="text-center q-mr-xs"
|
||||||
|
color="warning"
|
||||||
|
dense
|
||||||
|
size="sm"
|
||||||
|
v-if="props.isTray"
|
||||||
|
@click="handleEvent(name, 'addSpecific', 30)"
|
||||||
|
>
|
||||||
|
{{ t('Add 30') }}
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
class="text-center q-mr-xs"
|
||||||
|
color="warning"
|
||||||
|
dense
|
||||||
|
size="sm"
|
||||||
|
v-else
|
||||||
|
@click="handleEvent(name, 'addSpecific', 10)"
|
||||||
|
>
|
||||||
|
{{ t('Add 10') }}
|
||||||
|
</QBtn>
|
||||||
|
</QItemLabel>
|
||||||
|
<QItemLabel>
|
||||||
|
<QBtn
|
||||||
|
class="text-center q-mr-xs"
|
||||||
|
color="warning"
|
||||||
|
dense
|
||||||
|
size="sm"
|
||||||
|
@click="handleEvent(name, 'subtract')"
|
||||||
|
>
|
||||||
|
{{ t('Subtract 1') }}
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
class="text-center q-ml-xs"
|
||||||
|
color="red"
|
||||||
|
dense
|
||||||
|
size="sm"
|
||||||
|
@click="handleEvent(name, 'flush')"
|
||||||
|
>
|
||||||
|
{{ t('Flush') }}
|
||||||
|
</QBtn>
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
<QSeparator class="q-mt-sm q-mx-none" color="primary" />
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.q-list {
|
||||||
|
max-width: 50em;
|
||||||
|
}
|
||||||
|
@media (max-width: $breakpoint-sm) {
|
||||||
|
.q-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
min-height: 3em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Subtract 1: Quitar 1
|
||||||
|
Add 30: Añadir 30
|
||||||
|
Add 10: Añadir 10
|
||||||
|
Flush: Vaciar
|
||||||
|
Are you sure?: ¿Estás seguro?
|
||||||
|
It will set to 0: Se pondrá a 0
|
||||||
|
The counter will be reset to zero: Se pondrá el contador a cero
|
||||||
|
</i18n>
|
|
@ -49,19 +49,22 @@ const filter = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const sip = computed(() => worker.value.sip && worker.value.sip.extension);
|
const sip = computed(() => worker.value?.sip && worker.value.sip.extension);
|
||||||
|
|
||||||
function getWorkerAvatar() {
|
function getWorkerAvatar() {
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
return `/api/Images/user/160x160/${route.params.id}/download?access_token=${token}`;
|
return `/api/Images/user/160x160/${route.params.id}/download?access_token=${token}`;
|
||||||
}
|
}
|
||||||
const data = ref(useCardDescription());
|
const data = ref(useCardDescription());
|
||||||
const setData = (entity) =>
|
const setData = (entity) => {
|
||||||
(data.value = useCardDescription(entity.user.nickname, entity.id));
|
if (!entity) return;
|
||||||
|
data.value = useCardDescription(entity.user.nickname, entity.id);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<CardDescriptor
|
<CardDescriptor
|
||||||
module="Worker"
|
module="Worker"
|
||||||
|
data-key="workerData"
|
||||||
:url="`Workers/${entityId}`"
|
:url="`Workers/${entityId}`"
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
:title="data.title"
|
:title="data.title"
|
||||||
|
@ -92,8 +95,8 @@ const setData = (entity) =>
|
||||||
</QImg>
|
</QImg>
|
||||||
</template>
|
</template>
|
||||||
<template #body="{ entity }">
|
<template #body="{ entity }">
|
||||||
<VnLv :label="t('worker.card.name')" :value="entity.user.nickname" />
|
<VnLv :label="t('worker.card.name')" :value="entity.user?.nickname" />
|
||||||
<VnLv :label="t('worker.card.email')" :value="entity.user.email"> </VnLv>
|
<VnLv :label="t('worker.card.email')" :value="entity.user?.email"> </VnLv>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('worker.list.department')"
|
:label="t('worker.list.department')"
|
||||||
:value="entity.department ? entity.department.department.name : null"
|
:value="entity.department ? entity.department.department.name : null"
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { computed, onMounted, onUpdated, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
@ -12,131 +14,139 @@ const $props = defineProps({
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const entityId = computed(() => $props.id || route.params.id);
|
|
||||||
|
|
||||||
onMounted(() => fetch());
|
|
||||||
onUpdated(() => fetch());
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
const entityId = computed(() => $props.id || route.params.id);
|
||||||
const notifications = ref([]);
|
const URL_KEY = 'NotificationSubscriptions';
|
||||||
|
const active = ref();
|
||||||
async function fetch() {
|
const available = ref();
|
||||||
try {
|
|
||||||
await axios
|
|
||||||
.get(`NotificationSubscriptions/${entityId.value}/getList`)
|
|
||||||
.then(async (res) => {
|
|
||||||
if (res.data) {
|
|
||||||
notifications.value = res.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function disableNotification(notification) {
|
|
||||||
await axios
|
|
||||||
.delete(`NotificationSubscriptions/${notification.id}`)
|
|
||||||
.catch(() => (notification.active = true))
|
|
||||||
.then((res) => {
|
|
||||||
if (res.data) {
|
|
||||||
notification.id = null;
|
|
||||||
notification.active = false;
|
|
||||||
quasar.notify({
|
|
||||||
type: 'positive',
|
|
||||||
message: t('worker.notificationsManager.unsubscribed'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function toggleNotification(notification) {
|
async function toggleNotification(notification) {
|
||||||
if (!notification.active) {
|
try {
|
||||||
await disableNotification(notification);
|
if (!notification.active) {
|
||||||
} else {
|
await axios.delete(`${URL_KEY}/${notification.id}`);
|
||||||
await axios
|
swapEntry(active.value, available.value, notification.notificationFk);
|
||||||
.post(`NotificationSubscriptions`, {
|
} else {
|
||||||
|
const { data } = await axios.post(URL_KEY, {
|
||||||
notificationFk: notification.notificationFk,
|
notificationFk: notification.notificationFk,
|
||||||
userFk: entityId.value,
|
userFk: entityId.value,
|
||||||
})
|
|
||||||
.catch(() => (notification.active = false))
|
|
||||||
.then((res) => {
|
|
||||||
if (res.data) {
|
|
||||||
notification.id = res.data.id;
|
|
||||||
quasar.notify({
|
|
||||||
type: 'positive',
|
|
||||||
message: t('worker.notificationsManager.subscribed'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
notification.id = data.id;
|
||||||
|
|
||||||
|
swapEntry(available.value, active.value, notification.notificationFk);
|
||||||
|
}
|
||||||
|
|
||||||
|
quasar.notify({
|
||||||
|
type: 'positive',
|
||||||
|
message: t(
|
||||||
|
`worker.notificationsManager.${notification.active ? '' : 'un'}subscribed`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
notification.active = !notification.active;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const swapEntry = (from, to, key) => {
|
||||||
|
const element = from.get(key);
|
||||||
|
to.set(key, element);
|
||||||
|
from.delete(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
function setNotifications(data) {
|
||||||
|
active.value = new Map(data.active);
|
||||||
|
available.value = new Map(data.available);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QPage>
|
<CrudModel
|
||||||
<QCard class="q-pa-md">
|
auto-load
|
||||||
<QList>
|
:data-key="URL_KEY"
|
||||||
<div
|
:url="`${URL_KEY}/${entityId}/getList`"
|
||||||
v-show="
|
:default-reset="false"
|
||||||
notifications.filter(
|
:default-remove="false"
|
||||||
(notification) => notification.active == true
|
:default-save="false"
|
||||||
).length
|
@on-fetch="setNotifications"
|
||||||
"
|
>
|
||||||
>
|
<template #body>
|
||||||
<QItemLabel header class="text-h6">
|
<div
|
||||||
{{ t('worker.notificationsManager.activeNotifications') }}
|
v-for="(notifications, index) in [
|
||||||
</QItemLabel>
|
[...active.values()],
|
||||||
<QItem>
|
[...available.values()],
|
||||||
<div
|
]"
|
||||||
v-for="notification in notifications.filter(
|
:key="notifications"
|
||||||
(notification) => notification.active == true
|
>
|
||||||
)"
|
<QList class="notificationList">
|
||||||
:key="notification.id"
|
<TransitionGroup>
|
||||||
>
|
<QCard
|
||||||
<QChip
|
|
||||||
:key="notification.id"
|
|
||||||
:label="notification.name"
|
|
||||||
text-color="white"
|
|
||||||
color="primary"
|
|
||||||
class="q-mr-sm"
|
|
||||||
removable
|
|
||||||
@remove="disableNotification(notification)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</QItem>
|
|
||||||
</div>
|
|
||||||
<div v-show="notifications.length">
|
|
||||||
<QItemLabel header class="text-h6">
|
|
||||||
{{ t('worker.notificationsManager.availableNotifications') }}
|
|
||||||
</QItemLabel>
|
|
||||||
<div class="row">
|
|
||||||
<QItem
|
|
||||||
class="col-3"
|
|
||||||
:key="notification.notificationFk"
|
|
||||||
v-for="notification in notifications"
|
v-for="notification in notifications"
|
||||||
|
:key="notification.notificationFk"
|
||||||
|
class="q-pa-md"
|
||||||
>
|
>
|
||||||
<QItemSection>
|
<QItem>
|
||||||
<QItemLabel>{{ notification.name }}</QItemLabel>
|
<QItemSection avatar>
|
||||||
<QItemLabel caption>{{
|
<QBtn
|
||||||
notification.description
|
round
|
||||||
}}</QItemLabel>
|
icon="mail"
|
||||||
</QItemSection>
|
:color="notification.active ? 'green' : 'grey'"
|
||||||
<QItemSection side top>
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ notification.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ notification.description }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
<QToggle
|
<QToggle
|
||||||
checked-icon="check"
|
checked-icon="check"
|
||||||
unchecked-icon="close"
|
unchecked-icon="close"
|
||||||
indeterminate-icon="block"
|
|
||||||
v-model="notification.active"
|
v-model="notification.active"
|
||||||
|
color="green"
|
||||||
@update:model-value="toggleNotification(notification)"
|
@update:model-value="toggleNotification(notification)"
|
||||||
/>
|
/>
|
||||||
</QItemSection>
|
</QItem>
|
||||||
</QItem>
|
</QCard>
|
||||||
</div>
|
</TransitionGroup>
|
||||||
</div>
|
</QList>
|
||||||
</QList>
|
<QSeparator
|
||||||
</QCard>
|
color="primary"
|
||||||
</QPage>
|
class="q-my-lg"
|
||||||
|
v-if="!index && available.size && active.size"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</CrudModel>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.notificationList {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
grid-gap: 10px;
|
||||||
|
|
||||||
|
.v-enter-active,
|
||||||
|
.v-leave-active {
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-enter-from,
|
||||||
|
.v-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-md) {
|
||||||
|
.notificationList {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-xs) {
|
||||||
|
.notificationList {
|
||||||
|
grid-template-columns: repeat(1, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -45,7 +45,7 @@ export { Router };
|
||||||
export default route(function (/* { store, ssrContext } */) {
|
export default route(function (/* { store, ssrContext } */) {
|
||||||
Router.beforeEach(async (to, from, next) => {
|
Router.beforeEach(async (to, from, next) => {
|
||||||
const { isLoggedIn } = session;
|
const { isLoggedIn } = session;
|
||||||
const outLayout = ['Login', 'TwoFactor'];
|
const outLayout = ['Login', 'TwoFactor', 'VerifyEmail'];
|
||||||
if (!isLoggedIn() && !outLayout.includes(to.name)) {
|
if (!isLoggedIn() && !outLayout.includes(to.name)) {
|
||||||
return next({ name: 'Login', query: { redirect: to.fullPath } });
|
return next({ name: 'Login', query: { redirect: to.fullPath } });
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ export default {
|
||||||
'ClaimLog',
|
'ClaimLog',
|
||||||
'ClaimNotes',
|
'ClaimNotes',
|
||||||
'ClaimDevelopment',
|
'ClaimDevelopment',
|
||||||
|
'ClaimAction',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
@ -130,6 +131,15 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Claim/Card/ClaimNotes.vue'),
|
component: () => import('src/pages/Claim/Card/ClaimNotes.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'ClaimAction',
|
||||||
|
path: 'action',
|
||||||
|
meta: {
|
||||||
|
title: 'action',
|
||||||
|
icon: 'vn:actions',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Claim/Card/ClaimAction.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default {
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'CustomerMain' },
|
redirect: { name: 'CustomerMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['CustomerList', 'CustomerPayments', 'CustomerCreate'],
|
main: ['CustomerList', 'CustomerPayments'],
|
||||||
card: ['CustomerBasicData'],
|
card: ['CustomerBasicData'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
@ -27,7 +27,7 @@ export default {
|
||||||
title: 'list',
|
title: 'list',
|
||||||
icon: 'view_list',
|
icon: 'view_list',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Customer/CustomerList.vue')
|
component: () => import('src/pages/Customer/CustomerList.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'payments',
|
path: 'payments',
|
||||||
|
@ -36,17 +36,7 @@ export default {
|
||||||
title: 'webPayments',
|
title: 'webPayments',
|
||||||
icon: 'vn:onlinepayment',
|
icon: 'vn:onlinepayment',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Customer/CustomerPayments.vue')
|
component: () => import('src/pages/Customer/CustomerPayments.vue'),
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'create',
|
|
||||||
name: 'CustomerCreate',
|
|
||||||
meta: {
|
|
||||||
title: 'createCustomer',
|
|
||||||
icon: 'vn:addperson',
|
|
||||||
roles: ['developer'],
|
|
||||||
},
|
|
||||||
component: () => import('src/pages/Customer/CustomerCreate.vue'),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -63,7 +53,8 @@ export default {
|
||||||
title: 'summary',
|
title: 'summary',
|
||||||
icon: 'launch',
|
icon: 'launch',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Customer/Card/CustomerSummary.vue'),
|
component: () =>
|
||||||
|
import('src/pages/Customer/Card/CustomerSummary.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'basic-data',
|
path: 'basic-data',
|
||||||
|
@ -72,7 +63,8 @@ export default {
|
||||||
title: 'basicData',
|
title: 'basicData',
|
||||||
icon: 'vn:settings',
|
icon: 'vn:settings',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Customer/Card/CustomerBasicData.vue'),
|
component: () =>
|
||||||
|
import('src/pages/Customer/Card/CustomerBasicData.vue'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,13 +21,13 @@ export default {
|
||||||
redirect: { name: 'CmrList' },
|
redirect: { name: 'CmrList' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'cmr/list',
|
path: 'cmr',
|
||||||
name: 'CmrList',
|
name: 'CmrList',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'cmrsList',
|
title: 'cmrsList',
|
||||||
icon: 'fact_check',
|
icon: 'fact_check',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Route/Cmr/CmrList.vue')
|
component: () => import('src/pages/Route/Cmr/CmrList.vue'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default {
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'WagonMain' },
|
redirect: { name: 'WagonMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['WagonList', 'WagonTypeList'],
|
main: ['WagonList', 'WagonTypeList', 'WagonCounter'],
|
||||||
card: [],
|
card: [],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
@ -27,7 +27,7 @@ export default {
|
||||||
title: 'wagonsList',
|
title: 'wagonsList',
|
||||||
icon: 'vn:trolley',
|
icon: 'vn:trolley',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Wagon/WagonList.vue')
|
component: () => import('src/pages/Wagon/WagonList.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'create',
|
path: 'create',
|
||||||
|
@ -36,7 +36,7 @@ export default {
|
||||||
title: 'wagonCreate',
|
title: 'wagonCreate',
|
||||||
icon: 'create',
|
icon: 'create',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Wagon/WagonCreate.vue')
|
component: () => import('src/pages/Wagon/WagonCreate.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':id/edit',
|
path: ':id/edit',
|
||||||
|
@ -45,7 +45,16 @@ export default {
|
||||||
title: 'wagonEdit',
|
title: 'wagonEdit',
|
||||||
icon: 'edit',
|
icon: 'edit',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Wagon/WagonCreate.vue')
|
component: () => import('src/pages/Wagon/WagonCreate.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'counter',
|
||||||
|
name: 'WagonCounter',
|
||||||
|
meta: {
|
||||||
|
title: 'wagonCounter',
|
||||||
|
icon: 'add_circle',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Wagon/WagonCounter.vue'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -62,7 +71,7 @@ export default {
|
||||||
title: 'typesList',
|
title: 'typesList',
|
||||||
icon: 'view_list',
|
icon: 'view_list',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Wagon/Type/WagonTypeList.vue')
|
component: () => import('src/pages/Wagon/Type/WagonTypeList.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'create',
|
path: 'create',
|
||||||
|
@ -71,7 +80,7 @@ export default {
|
||||||
title: 'typeCreate',
|
title: 'typeCreate',
|
||||||
icon: 'create',
|
icon: 'create',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Wagon/Type/WagonTypeCreate.vue')
|
component: () => import('src/pages/Wagon/Type/WagonTypeCreate.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':id/edit',
|
path: ':id/edit',
|
||||||
|
@ -80,9 +89,9 @@ export default {
|
||||||
title: 'typeEdit',
|
title: 'typeEdit',
|
||||||
icon: 'edit',
|
icon: 'edit',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Wagon/Type/WagonTypeCreate.vue')
|
component: () => import('src/pages/Wagon/Type/WagonTypeCreate.vue'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default {
|
||||||
redirect: { name: 'WorkerMain' },
|
redirect: { name: 'WorkerMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['WorkerList'],
|
main: ['WorkerList'],
|
||||||
// card: ['WorkerNotificationsManager'],
|
card: ['WorkerNotificationsManager'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -46,15 +46,16 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Worker/Card/WorkerSummary.vue'),
|
component: () => import('src/pages/Worker/Card/WorkerSummary.vue'),
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// name: 'WorkerNotificationsManager',
|
name: 'WorkerNotificationsManager',
|
||||||
// path: 'notifications',
|
path: 'notifications',
|
||||||
// meta: {
|
meta: {
|
||||||
// title: 'notifications',
|
title: 'notifications',
|
||||||
// icon: 'notifications',
|
icon: 'notifications',
|
||||||
// },
|
},
|
||||||
// component: () => import('src/pages/Worker/Card/WorkerNotificationsManager.vue'),
|
component: () =>
|
||||||
// },
|
import('src/pages/Worker/Card/WorkerNotificationsManager.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -23,6 +23,12 @@ const routes = [
|
||||||
meta: { title: 'twoFactor' },
|
meta: { title: 'twoFactor' },
|
||||||
component: () => import('../pages/Login/TwoFactor.vue'),
|
component: () => import('../pages/Login/TwoFactor.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/verifyEmail',
|
||||||
|
name: 'VerifyEmail',
|
||||||
|
meta: { title: 'verifyEmail' },
|
||||||
|
component: () => import('../pages/Login/VerifyEmail.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,7 +18,8 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
|
||||||
skip: 0,
|
skip: 0,
|
||||||
order: '',
|
order: '',
|
||||||
data: ref(),
|
data: ref(),
|
||||||
isLoading: false
|
isLoading: false,
|
||||||
|
exprBuilder: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
describe('ClaimAction', () => {
|
||||||
|
const claimId = 2;
|
||||||
|
|
||||||
|
const firstRow = 'tbody > :nth-child(1)';
|
||||||
|
const destinationRow = '.q-item__section > .q-field';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.viewport(1920, 1080);
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit(`/#/claim/${claimId}/action`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should import claim', () => {
|
||||||
|
cy.get('[title="Import claim"]').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change destination', () => {
|
||||||
|
const rowData = [true, null, null, 'Bueno'];
|
||||||
|
cy.fillRow(firstRow, rowData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change destination from other button', () => {
|
||||||
|
const rowData = [true];
|
||||||
|
|
||||||
|
cy.fillRow(firstRow, rowData);
|
||||||
|
cy.get('[title="Change destination"]').click();
|
||||||
|
cy.selectOption(destinationRow, 'Confeccion');
|
||||||
|
cy.get('.q-card > .q-card__actions > .q-btn--standard').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should regularize', () => {
|
||||||
|
cy.get('[title="Regularize"]').click();
|
||||||
|
cy.clickConfirm();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove the line', () => {
|
||||||
|
cy.fillRow(firstRow, [true]);
|
||||||
|
cy.removeCard();
|
||||||
|
cy.clickConfirm();
|
||||||
|
|
||||||
|
cy.reload();
|
||||||
|
cy.get(firstRow).should('not.exist');
|
||||||
|
});
|
||||||
|
});
|
|
@ -13,15 +13,17 @@ describe('ClaimDevelopment', () => {
|
||||||
it('should reset line', () => {
|
it('should reset line', () => {
|
||||||
cy.selectOption(firstLineReason, 'Novato');
|
cy.selectOption(firstLineReason, 'Novato');
|
||||||
cy.resetCard();
|
cy.resetCard();
|
||||||
cy.getValue(firstLineReason).should('have.text', 'Prisas');
|
cy.getValue(firstLineReason).should('have.value', 'Prisas');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should edit line', () => {
|
it('should edit line', () => {
|
||||||
cy.selectOption(firstLineReason, 'Novato');
|
cy.selectOption(firstLineReason, 'Novato');
|
||||||
cy.saveCard();
|
|
||||||
|
|
||||||
cy.reload();
|
cy.saveCard();
|
||||||
cy.getValue(firstLineReason).should('have.text', 'Novato');
|
cy.login('developer');
|
||||||
|
cy.visit(`/#/claim/${claimId}/development`);
|
||||||
|
|
||||||
|
cy.getValue(firstLineReason).should('have.value', 'Novato');
|
||||||
|
|
||||||
//Restart data
|
//Restart data
|
||||||
cy.selectOption(firstLineReason, 'Prisas');
|
cy.selectOption(firstLineReason, 'Prisas');
|
||||||
|
@ -29,13 +31,16 @@ describe('ClaimDevelopment', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add and remove new line', () => {
|
it('should add and remove new line', () => {
|
||||||
//add row
|
|
||||||
cy.addCard();
|
cy.addCard();
|
||||||
cy.get(thirdRow).should('exist');
|
cy.get(thirdRow).should('exist');
|
||||||
|
|
||||||
const rowData = [false, 'Novato', 'Roces', 'Compradores', 'employeeNick', 'Tour'];
|
const rowData = [false, 'Novato', 'Roces', 'Compradores', 'employeeNick', 'Tour'];
|
||||||
cy.fillRow(thirdRow, rowData);
|
cy.fillRow(thirdRow, rowData);
|
||||||
|
|
||||||
cy.saveCard();
|
cy.saveCard();
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit(`/#/claim/${claimId}/development`);
|
||||||
|
|
||||||
cy.validateRow(thirdRow, rowData);
|
cy.validateRow(thirdRow, rowData);
|
||||||
|
|
||||||
cy.reload();
|
cy.reload();
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
describe('VnBreadcrumbs', () => {
|
||||||
|
const firstCard = '.q-infinite-scroll > :nth-child(1)';
|
||||||
|
const lastBreadcrumb = '.q-breadcrumbs--last > .q-breadcrumbs__el';
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be breadcrumbs', () => {
|
||||||
|
cy.get('.q-breadcrumbs').should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the correct breadcrumbs', () => {
|
||||||
|
cy.visit('#/customer/list');
|
||||||
|
cy.get('.q-breadcrumbs__el').should('have.length', 2);
|
||||||
|
|
||||||
|
cy.get(firstCard).click();
|
||||||
|
cy.get(`${lastBreadcrumb} > .q-icon`).should('have.text', 'launch');
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,36 +1,79 @@
|
||||||
xdescribe('WorkerNotificationsManager', () => {
|
describe('WorkerNotificationsManager', () => {
|
||||||
|
const salesPersonId = 18;
|
||||||
|
const developerId = 9;
|
||||||
|
|
||||||
|
const activeList = ':nth-child(1) > .q-list';
|
||||||
|
const availableList = ':nth-child(2) > .q-list';
|
||||||
|
const firstActiveNotification =
|
||||||
|
':nth-child(1) > .q-list > :nth-child(1) > .q-item > .q-toggle > .q-toggle__inner';
|
||||||
|
const firstAvailableNotification =
|
||||||
|
':nth-child(2) > .q-list > :nth-child(1) > .q-item > .q-toggle > .q-toggle__inner';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const workerId = 1110;
|
|
||||||
cy.viewport(1280, 720);
|
cy.viewport(1280, 720);
|
||||||
cy.login('salesBoss');
|
|
||||||
cy.visit(`/#/worker/${workerId}/notifications`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should unsubscribe 2 notifications, check the unsubscription has been saved, subscribe to other one and should check the data has been saved', () => {
|
it('should throw an error if you try to change a notification that is not yours', () => {
|
||||||
cy.get('.q-chip').should('have.length', 3);
|
cy.login('developer');
|
||||||
cy.get('.q-toggle__thumb').eq(0).click();
|
cy.visit(`/#/worker/${salesPersonId}/notifications`);
|
||||||
cy.get('.q-notification__message').should(
|
cy.get(firstAvailableNotification).click();
|
||||||
'have.text',
|
cy.notificationHas(
|
||||||
'Unsubscribed from the notification'
|
'.q-notification__message',
|
||||||
|
'The notification subscription of this worker cant be modified'
|
||||||
);
|
);
|
||||||
cy.get('.q-chip > .q-icon').eq(0).click();
|
});
|
||||||
|
|
||||||
cy.reload();
|
it('should active a notification that is yours', () => {
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit(`/#/worker/${developerId}/notifications`);
|
||||||
|
cy.waitForElement(activeList);
|
||||||
|
cy.waitForElement(availableList);
|
||||||
|
|
||||||
cy.get('.q-chip').should('have.length', 1);
|
cy.get(activeList)
|
||||||
cy.get('.q-toggle__thumb').should('have.length', 3).eq(0).click();
|
.children()
|
||||||
cy.get('.q-notification__message').should(
|
.its('length')
|
||||||
'have.text',
|
.then((beforeSize) => {
|
||||||
'Subscribed to the notification'
|
cy.get(firstAvailableNotification).click();
|
||||||
);
|
cy.get(activeList)
|
||||||
cy.get('.q-toggle__thumb').should('have.length', 3).eq(1).click();
|
.children()
|
||||||
cy.get('.q-notification__message').should(
|
.should('have.length', beforeSize + 1);
|
||||||
'have.text',
|
});
|
||||||
'Subscribed to the notification'
|
});
|
||||||
);
|
|
||||||
|
|
||||||
cy.reload();
|
it('should deactivate a notification that is yours', () => {
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit(`/#/worker/${developerId}/notifications`);
|
||||||
|
cy.waitForElement(activeList);
|
||||||
|
cy.waitForElement(availableList);
|
||||||
|
|
||||||
cy.get('.q-chip').should('have.length', 3);
|
cy.get(availableList)
|
||||||
|
.children()
|
||||||
|
.its('length')
|
||||||
|
.then((beforeSize) => {
|
||||||
|
cy.get(firstActiveNotification).click();
|
||||||
|
cy.get(availableList)
|
||||||
|
.children()
|
||||||
|
.should('have.length', beforeSize + 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should active a notification if you are their boss', () => {
|
||||||
|
cy.login('salesBoss');
|
||||||
|
cy.visit(`/#/worker/${salesPersonId}/notifications`);
|
||||||
|
cy.waitForElement(activeList);
|
||||||
|
cy.waitForElement(availableList);
|
||||||
|
|
||||||
|
cy.get(activeList)
|
||||||
|
.children()
|
||||||
|
.its('length')
|
||||||
|
.then((beforeSize) => {
|
||||||
|
cy.get(firstAvailableNotification).click();
|
||||||
|
cy.get(activeList)
|
||||||
|
.children()
|
||||||
|
.should('have.length', beforeSize + 1);
|
||||||
|
|
||||||
|
//Rollback
|
||||||
|
cy.get(firstActiveNotification).click();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -54,7 +54,7 @@ Cypress.Commands.add('getValue', (selector) => {
|
||||||
else if ($el.find('.q-select__dropdown-icon').length) {
|
else if ($el.find('.q-select__dropdown-icon').length) {
|
||||||
return cy.get(
|
return cy.get(
|
||||||
selector +
|
selector +
|
||||||
'> .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native > span'
|
'> .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native > input'
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Puedes añadir un log o lanzar un error si el elemento no es reconocido
|
// Puedes añadir un log o lanzar un error si el elemento no es reconocido
|
||||||
|
@ -76,7 +76,6 @@ Cypress.Commands.add('checkOption', (selector) => {
|
||||||
// Global buttons
|
// Global buttons
|
||||||
Cypress.Commands.add('saveCard', () => {
|
Cypress.Commands.add('saveCard', () => {
|
||||||
cy.get('[title="Save"]').click();
|
cy.get('[title="Save"]').click();
|
||||||
cy.get('[title="Save"]').should('have.class', 'disabled');
|
|
||||||
});
|
});
|
||||||
Cypress.Commands.add('resetCard', () => {
|
Cypress.Commands.add('resetCard', () => {
|
||||||
cy.get('[title="Reset"]').click();
|
cy.get('[title="Reset"]').click();
|
||||||
|
@ -89,7 +88,12 @@ Cypress.Commands.add('addCard', () => {
|
||||||
cy.get('.q-page-sticky > div > .q-btn').click();
|
cy.get('.q-page-sticky > div > .q-btn').click();
|
||||||
});
|
});
|
||||||
Cypress.Commands.add('clickConfirm', () => {
|
Cypress.Commands.add('clickConfirm', () => {
|
||||||
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
|
cy.waitForElement('.q-dialog__inner > .q-card');
|
||||||
|
cy.get('.q-card__actions > .q-btn--unelevated > .q-btn__content > .block').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add('notificationHas', (selector, text) => {
|
||||||
|
cy.get(selector).should('have.text', text);
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('fillRow', (rowSelector, data) => {
|
Cypress.Commands.add('fillRow', (rowSelector, data) => {
|
||||||
|
@ -123,7 +127,7 @@ Cypress.Commands.add('validateRow', (rowSelector, expectedValues) => {
|
||||||
cy.getValue(`:nth-child(${index + 1})`).should(`${prefix}be.checked`);
|
cy.getValue(`:nth-child(${index + 1})`).should(`${prefix}be.checked`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
cy.getValue(`:nth-child(${index + 1})`).should('have.text', value);
|
cy.getValue(`:nth-child(${index + 1})`).should('have.value', value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
|
import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
|
||||||
import { createWrapper, axios } from 'app/test/vitest/helper';
|
import { createWrapper } from 'app/test/vitest/helper';
|
||||||
import WorkerNotificationsManager from 'src/pages/Worker/Card/WorkerNotificationsManager.vue';
|
import WorkerNotificationsManager from 'src/pages/Worker/Card/WorkerNotificationsManager.vue';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
describe('WorkerNotificationsManager', () => {
|
describe('WorkerNotificationsManager', () => {
|
||||||
let vm;
|
let vm;
|
||||||
const entityId = 1110;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
vm = createWrapper(WorkerNotificationsManager, {
|
vm = createWrapper(WorkerNotificationsManager, {
|
||||||
propsData: {
|
global: {
|
||||||
id: entityId,
|
stubs: ['CrudModel'],
|
||||||
},
|
},
|
||||||
}).vm;
|
}).vm;
|
||||||
});
|
});
|
||||||
|
@ -18,83 +18,16 @@ describe('WorkerNotificationsManager', () => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fetch()', () => {
|
describe('swapEntry()', () => {
|
||||||
it('should fetch notification subscriptions and role mappings', async () => {
|
it('should swap notification', async () => {
|
||||||
vi.spyOn(axios, 'get')
|
const from = ref(new Map());
|
||||||
.mockResolvedValueOnce({
|
const to = ref(new Map());
|
||||||
data: [
|
from.value.set(1, { notificationFk: 1 });
|
||||||
{
|
to.value.set(2, { notificationFk: 2 });
|
||||||
id: 1,
|
|
||||||
name: 'Name 1',
|
|
||||||
description: 'Description 1',
|
|
||||||
notificationFk: 1,
|
|
||||||
active: true
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
await vm.fetch();
|
|
||||||
|
|
||||||
expect(axios.get).toHaveBeenCalledWith(`NotificationSubscriptions/${entityId}/getList`);
|
await vm.swapEntry(from.value, to.value, 1);
|
||||||
expect(vm.notifications).toEqual([
|
expect(to.value.size).toBe(2);
|
||||||
{
|
expect(from.value.size).toBe(0);
|
||||||
id: 1,
|
|
||||||
notificationFk: 1,
|
|
||||||
name: 'Name 1',
|
|
||||||
description: 'Description 1',
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('disableNotification()', () => {
|
|
||||||
it('should disable the notification', async () => {
|
|
||||||
vi.spyOn(axios, 'delete').mockResolvedValue({ data: { count: 1 } });
|
|
||||||
vi.spyOn(vm.quasar, 'notify');
|
|
||||||
const subscriptionId = 1;
|
|
||||||
vm.notifications = [{ id: 1, active: true }];
|
|
||||||
|
|
||||||
await vm.disableNotification(vm.notifications[0]);
|
|
||||||
|
|
||||||
expect(axios.delete).toHaveBeenCalledWith(
|
|
||||||
`NotificationSubscriptions/${subscriptionId}`
|
|
||||||
);
|
|
||||||
expect(vm.notifications[0].id).toBeNull();
|
|
||||||
expect(vm.notifications[0].id).toBeFalsy();
|
|
||||||
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({ type: 'positive' })
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('toggleNotification()', () => {
|
|
||||||
it('should activate the notification', async () => {
|
|
||||||
vi.spyOn(axios, 'post').mockResolvedValue({
|
|
||||||
data: { id: 1, notificationFk: 1 },
|
|
||||||
});
|
|
||||||
vm.notifications = [{ id: null, active: true, notificationFk: 1 }];
|
|
||||||
|
|
||||||
await vm.toggleNotification(vm.notifications[0]);
|
|
||||||
|
|
||||||
expect(axios.post).toHaveBeenCalledWith('NotificationSubscriptions', {
|
|
||||||
notificationFk: 1,
|
|
||||||
userFk: entityId,
|
|
||||||
});
|
|
||||||
expect(vm.notifications[0].id).toBe(1);
|
|
||||||
expect(vm.notifications[0].active).toBeTruthy();
|
|
||||||
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({ type: 'positive' })
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should disable the notification', async () => {
|
|
||||||
vi.spyOn(vm, 'disableNotification');
|
|
||||||
vm.notifications = [{ id: 1, active: false, notificationFk: 1 }];
|
|
||||||
|
|
||||||
await vm.toggleNotification(vm.notifications[0]);
|
|
||||||
|
|
||||||
expect(vm.notifications[0].id).toBe(null);
|
|
||||||
expect(vm.notifications[0].active).toBeFalsy();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -63,6 +63,7 @@ class FormDataMock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
global.FormData = FormDataMock;
|
global.FormData = FormDataMock;
|
||||||
|
global.URL = class URL {};
|
||||||
|
|
||||||
export function createWrapper(component, options) {
|
export function createWrapper(component, options) {
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
|
|