Compare commits
184 Commits
dev
...
bugfix/fix
Author | SHA1 | Date |
---|---|---|
William Buezas | e5efdff1d8 | |
William Buezas | 15c5dc761f | |
William Buezas | 514af4f11e | |
Javier Segarra | 72da0cb5a1 | |
Javier Segarra | baa1422025 | |
William Buezas | c950bf5ef1 | |
William Buezas | fdff59a74d | |
William Buezas | 68f32c36e7 | |
William Buezas | 3e93f14196 | |
William Buezas | 5cab14f371 | |
William Buezas | 49244a411c | |
William Buezas | 2eb0963829 | |
William Buezas | 477c42b964 | |
William Buezas | baaff90aef | |
William Buezas | 4111dd8669 | |
William Buezas | ce7e04379a | |
William Buezas | 6927b86570 | |
William Buezas | 577a6281b8 | |
William Buezas | 4358ef70c0 | |
William Buezas | d8ec175328 | |
William Buezas | 0f5014088d | |
William Buezas | 202c22b8a9 | |
William Buezas | 58b7325605 | |
William Buezas | e6f0320cf5 | |
William Buezas | fac528b42f | |
William Buezas | 101ac917a2 | |
Javier Segarra | 0529505cc2 | |
Javier Segarra | 622ce6ae28 | |
William Buezas | 8664f40e06 | |
William Buezas | e39cab82f6 | |
William Buezas | 72cd9e6cd6 | |
William Buezas | ff0d2db8fe | |
William Buezas | bbaa6d87ff | |
William Buezas | c3e3b4d7b6 | |
Javier Segarra | d824df5eb0 | |
Javier Segarra | f4fea5e904 | |
William Buezas | d09f90d47c | |
William Buezas | bc97e000b8 | |
William Buezas | cdf534e70b | |
William Buezas | f8e67f0d98 | |
William Buezas | 2b41bc31c1 | |
William Buezas | cc44041e31 | |
William Buezas | 85eee7bb7a | |
William Buezas | 2a43ab023a | |
William Buezas | be868b60ab | |
William Buezas | f56d4cca14 | |
William Buezas | 67847e798b | |
William Buezas | 79ddbe4916 | |
William Buezas | 8ffe9a6a96 | |
William Buezas | 21b6e96148 | |
William Buezas | f1a8fd2710 | |
Javier Segarra | 59e8285bd2 | |
William Buezas | 2176c6e259 | |
William Buezas | 533c7560dc | |
William Buezas | c4a64bff91 | |
William Buezas | 3c71c71df1 | |
William Buezas | e76e4e4f8c | |
William Buezas | f95a8c9daa | |
William Buezas | 2758dcf2c8 | |
Javier Segarra | facb565d26 | |
Javier Segarra | a7edc9b87a | |
William Buezas | 93e739f35c | |
William Buezas | 65e0d752d6 | |
William Buezas | 418911b903 | |
William Buezas | decf955224 | |
Javier Segarra | d736ea716b | |
William Buezas | a9427ddab9 | |
Javier Segarra | 057bfae517 | |
Javier Segarra | 929623949b | |
Javier Segarra | 38a88bb0cc | |
William Buezas | 265ff5c47f | |
William Buezas | c41d1430f7 | |
William Buezas | 2bff9304e8 | |
William Buezas | c53658e6e0 | |
William Buezas | e94f8c6b23 | |
William Buezas | c52fe7a870 | |
William Buezas | bfbe3621d6 | |
Javier Segarra | 05568280f3 | |
William Buezas | 6bfa5b9715 | |
William Buezas | 6631be401b | |
William Buezas | ba2ded5c48 | |
William Buezas | 723a977ecd | |
William Buezas | a1d67ebc6f | |
William Buezas | 7026e3416f | |
William Buezas | 975495113d | |
William Buezas | 8e0f09cc0f | |
William Buezas | 95e23c05fa | |
William Buezas | 6423ecfb05 | |
William Buezas | 3a21292030 | |
William Buezas | f2bd3c2fa6 | |
William Buezas | 020e0afc96 | |
William Buezas | 766417bb73 | |
William Buezas | c77c2e6648 | |
William Buezas | 6fe518601f | |
William Buezas | cb2c9871cc | |
Javier Segarra | a832a1889a | |
William Buezas | 7c4123ca0b | |
William Buezas | facbe9b990 | |
William Buezas | 57880705d0 | |
Javier Segarra | ed29a1939c | |
Javier Segarra | 5456db8add | |
Javier Segarra | 053b9f8457 | |
Javier Segarra | 8866331926 | |
William Buezas | 33ef1da2a9 | |
Javier Segarra | 2e2c83dcde | |
William Buezas | 73eb3dcbee | |
William Buezas | 05d735702e | |
Javier Segarra | 462a8a3cf8 | |
Javier Segarra | f821b8689a | |
Javier Segarra | e47edb9827 | |
William Buezas | 25f4f822b4 | |
William Buezas | ef35914f34 | |
William Buezas | b19bf710e8 | |
William Buezas | 17a519e2ee | |
William Buezas | 95a2bfb69c | |
William Buezas | 6e41548fdf | |
William Buezas | c86c1cc0c0 | |
William Buezas | 87c151c057 | |
William Buezas | 13af1d03a3 | |
William Buezas | 14bef2383f | |
Javier Segarra | 5053a908f7 | |
William Buezas | 7e0c591026 | |
William Buezas | 594b17b4ab | |
William Buezas | 887ee8aea4 | |
William Buezas | 44627dbc8a | |
William Buezas | f2c8b90324 | |
William Buezas | e0f55f8ca3 | |
William Buezas | f36eb1bd88 | |
William Buezas | b728ecaf29 | |
William Buezas | 7c96106faa | |
William Buezas | a0fc1cfc07 | |
William Buezas | b66c47955c | |
William Buezas | ec14ca334a | |
William Buezas | 7837925be9 | |
William Buezas | 2fb892c71a | |
William Buezas | 2a1cd59492 | |
William Buezas | 76b99ed293 | |
William Buezas | 67c6f84de3 | |
William Buezas | 745e9a569c | |
William Buezas | ad2d494481 | |
Javier Segarra | 24687e57e6 | |
Javier Segarra | c20f48b2bf | |
Javier Segarra | 6bad41db20 | |
Javier Segarra | eb0328753a | |
Javier Segarra | e067f5f7bd | |
Javier Segarra | 34a0d93ece | |
William Buezas | 4256f45373 | |
William Buezas | 93cc0d4286 | |
William Buezas | ef36566442 | |
William Buezas | 06cd9b01d3 | |
William Buezas | 7f831ae3a5 | |
William Buezas | 382378e867 | |
William Buezas | 401487dfd3 | |
William Buezas | aa4ccf65f5 | |
Javier Segarra | fb267b910b | |
Javier Segarra | 24a9c130d1 | |
Javier Segarra | f59b37c722 | |
Javier Segarra | 160552ff2f | |
William Buezas | 83e3e034a8 | |
William Buezas | 61062c1418 | |
William Buezas | 2cbbaf619c | |
William Buezas | ec0d783672 | |
William Buezas | 07c5f64265 | |
William Buezas | dcbc154caa | |
Javier Segarra | 0d3da684b4 | |
William Buezas | 8d2f041c46 | |
William Buezas | 28b2dd386f | |
Javier Segarra | d589b89a62 | |
Javier Segarra | 04660bd05e | |
Javier Segarra | 1d6ec00c78 | |
Javier Segarra | ce557dc5b9 | |
Javier Segarra | 4387a868bc | |
William Buezas | 003f42dd03 | |
William Buezas | 9dfeb2f7ef | |
William Buezas | 8bea750244 | |
William Buezas | bf2094163d | |
William Buezas | e0cc4e40ba | |
William Buezas | 47c6fe02ec | |
Juan Ferrer | 6458d8db5e | |
Juan Ferrer | 0234e14c6b | |
Juan Ferrer | 7e26aa773c | |
Juan Ferrer | 0d0be4ee5f | |
Juan Ferrer | 042b8b0309 | |
Juan Ferrer | b7658b76cf |
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
|
@ -0,0 +1,8 @@
|
|||
/dist
|
||||
/src-bex/www
|
||||
/src-capacitor
|
||||
/src-cordova
|
||||
/.quasar
|
||||
/node_modules
|
||||
.eslintrc.js
|
||||
babel.config.js
|
|
@ -0,0 +1,85 @@
|
|||
module.exports = {
|
||||
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
|
||||
// This option interrupts the configuration hierarchy at this file
|
||||
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
|
||||
root: true,
|
||||
|
||||
parserOptions: {
|
||||
parser: '@babel/eslint-parser',
|
||||
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
|
||||
sourceType: 'module' // Allows for the use of imports
|
||||
},
|
||||
|
||||
env: {
|
||||
browser: true,
|
||||
'vue/setup-compiler-macros': true
|
||||
},
|
||||
|
||||
extends: ['standard'],
|
||||
plugins: ['vue', 'prettier'],
|
||||
globals: {
|
||||
ga: 'readonly', // Google Analytics
|
||||
cordova: 'readonly',
|
||||
__statics: 'readonly',
|
||||
__QUASAR_SSR__: 'readonly',
|
||||
__QUASAR_SSR_SERVER__: 'readonly',
|
||||
__QUASAR_SSR_CLIENT__: 'readonly',
|
||||
__QUASAR_SSR_PWA__: 'readonly',
|
||||
process: 'readonly',
|
||||
Capacitor: 'readonly',
|
||||
chrome: 'readonly'
|
||||
},
|
||||
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
// allow async-await
|
||||
'generator-star-spacing': 'off',
|
||||
// allow paren-less arrow functions
|
||||
'arrow-parens': 'off',
|
||||
'one-var': 'off',
|
||||
'no-void': 'off',
|
||||
'multiline-ternary': 'off',
|
||||
|
||||
'import/first': 'off',
|
||||
'import/named': 'error',
|
||||
'import/namespace': 'error',
|
||||
'import/default': 'error',
|
||||
'import/export': 'error',
|
||||
'import/extensions': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
|
||||
'prefer-promise-reject-errors': 'off',
|
||||
semi: 'off',
|
||||
// allow debugger during development only
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['src/**/*.{js,vue,scss}', 'quasar.config.js'], // Aplica ESLint solo a archivos .js, .vue y .scss dentro de src (Proyecto de quasar)
|
||||
extends: [
|
||||
// Base ESLint recommended rules
|
||||
'eslint:recommended',
|
||||
// Uncomment any of the lines below to choose desired strictness,
|
||||
// but leave only one uncommented!
|
||||
// See https://eslint.vuejs.org/rules/#available-rules
|
||||
// 'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
|
||||
'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
|
||||
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
||||
// https://github.com/prettier/eslint-config-prettier#installation
|
||||
// usage with Prettier, provided by 'eslint-config-prettier'.
|
||||
'prettier'
|
||||
],
|
||||
rules: {
|
||||
semi: 'off',
|
||||
'space-before-function-paren': 'off',
|
||||
'prefer-promise-reject-errors': 'off',
|
||||
'vue/no-multiple-template-root': 'off'
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: '2021'
|
||||
},
|
||||
plugins: ['vue']
|
||||
}
|
||||
]
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
extends: eslint:recommended
|
||||
parserOptions:
|
||||
ecmaVersion: 2017
|
||||
sourceType: module
|
||||
rules:
|
||||
no-undef: 0
|
||||
no-redeclare: 0
|
||||
no-mixed-spaces-and-tabs: 0
|
||||
no-console: 0
|
||||
no-cond-assign: 0
|
||||
no-unexpected-multiline: 0
|
||||
brace-style: [error, 1tbs]
|
||||
space-before-function-paren: [error, never]
|
||||
padded-blocks: [error, never]
|
||||
func-call-spacing: [error, never]
|
|
@ -1,4 +1,36 @@
|
|||
node_modules
|
||||
build/
|
||||
config.my.php
|
||||
.vscode/
|
||||
|
||||
.DS_Store
|
||||
.thumbs.db
|
||||
node_modules
|
||||
|
||||
# Quasar core related directories
|
||||
.quasar
|
||||
/dist
|
||||
|
||||
# Cordova related directories and files
|
||||
/src-cordova/node_modules
|
||||
/src-cordova/platforms
|
||||
/src-cordova/plugins
|
||||
/src-cordova/www
|
||||
|
||||
# Capacitor related directories and files
|
||||
/src-capacitor/www
|
||||
/src-capacitor/node_modules
|
||||
|
||||
# BEX related directories and files
|
||||
/src-bex/www
|
||||
/src-bex/js/core
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# pnpm-related options
|
||||
shamefully-hoist=true
|
||||
strict-peer-dependencies=false
|
|
@ -0,0 +1,9 @@
|
|||
/* eslint-disable */
|
||||
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// to edit target browsers: use "browserslist" field in package.json
|
||||
require('autoprefixer')
|
||||
]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
module.exports = {
|
||||
printWidth: 80,
|
||||
tabWidth: 4,
|
||||
useTabs: false,
|
||||
singleQuote: true,
|
||||
bracketSpacing: true,
|
||||
arrowParens: 'avoid',
|
||||
trailingComma: 'none'
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"editorconfig.editorconfig",
|
||||
"vue.volar",
|
||||
"wayou.vscode-todo-highlight"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"octref.vetur",
|
||||
"hookyqr.beautify",
|
||||
"dbaeumer.jshint",
|
||||
"ms-vscode.vscode-typescript-tslint-plugin"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Listen for XDebug",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"port": 9000
|
||||
},
|
||||
{
|
||||
"name": "Launch currently open script",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"cwd": "${fileDirname}",
|
||||
"port": 9000
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"editor.bracketPairColorization.enabled": true,
|
||||
"editor.guides.bracketPairs": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
|
||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"],
|
||||
"cSpell.words": ["axios", "composables"]
|
||||
}
|
|
@ -1,34 +1,25 @@
|
|||
#!/usr/bin/env groovy
|
||||
|
||||
def BRANCH_ENV = [
|
||||
test: 'test',
|
||||
master: 'production'
|
||||
]
|
||||
def remote = [:]
|
||||
|
||||
node {
|
||||
stage('Setup') {
|
||||
env.NODE_ENV = BRANCH_ENV[env.BRANCH_NAME] ?: 'dev'
|
||||
|
||||
echo "NODE_NAME: ${env.NODE_NAME}"
|
||||
echo "WORKSPACE: ${env.WORKSPACE}"
|
||||
}
|
||||
}
|
||||
pipeline {
|
||||
agent any
|
||||
environment {
|
||||
PROJECT_NAME = 'hedera-web'
|
||||
STACK_NAME = "${env.PROJECT_NAME}-${env.BRANCH_NAME}"
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
script {
|
||||
def packageJson = readJSON file: 'package.json'
|
||||
env.VERSION = packageJson.version
|
||||
|
||||
switch (env.BRANCH_NAME) {
|
||||
case 'master':
|
||||
env.NODE_ENV = 'production'
|
||||
env.MAIN_REPLICAS = 3
|
||||
env.CRON_REPLICAS = 1
|
||||
break
|
||||
case 'test':
|
||||
env.NODE_ENV = 'test'
|
||||
env.MAIN_REPLICAS = 1
|
||||
env.CRON_REPLICAS = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
setEnv()
|
||||
}
|
||||
}
|
||||
stage('Debuild') {
|
||||
when {
|
||||
anyOf {
|
||||
|
@ -38,31 +29,28 @@ pipeline {
|
|||
}
|
||||
agent {
|
||||
docker {
|
||||
image 'registry.verdnatura.es/debuild:2.21.3-vn2'
|
||||
image 'registry.verdnatura.es/verdnatura/debuild:2.23.4-vn7'
|
||||
registryUrl 'https://registry.verdnatura.es/'
|
||||
registryCredentialsId 'docker-registry'
|
||||
args '-v /mnt/appdata/reprepro:/reprepro'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'debuild -us -uc -b'
|
||||
sh 'vn-includedeb stretch'
|
||||
}
|
||||
}
|
||||
stage('Container') {
|
||||
when {
|
||||
anyOf {
|
||||
branch 'master'
|
||||
branch 'test'
|
||||
sh 'mkdir -p debuild'
|
||||
sh 'mv ../hedera-web_* debuild'
|
||||
|
||||
script {
|
||||
def files = findFiles(glob: 'debuild/*.changes')
|
||||
files.each { file -> env.CHANGES_FILE = file.name }
|
||||
}
|
||||
|
||||
configFileProvider([
|
||||
configFile(fileId: "dput.cf", variable: 'DPUT_CONFIG')
|
||||
]) {
|
||||
sshagent(credentials: ['jenkins-agent']) {
|
||||
sh 'dput --config "$DPUT_CONFIG" verdnatura "debuild/$CHANGES_FILE"'
|
||||
}
|
||||
}
|
||||
}
|
||||
environment {
|
||||
CREDS = credentials('docker-registry')
|
||||
}
|
||||
steps {
|
||||
sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY'
|
||||
sh 'docker-compose build --build-arg BUILD_ID=$BUILD_ID --parallel'
|
||||
sh 'docker-compose push'
|
||||
}
|
||||
}
|
||||
stage('Deploy') {
|
||||
|
@ -73,15 +61,41 @@ pipeline {
|
|||
}
|
||||
}
|
||||
environment {
|
||||
DOCKER_HOST = "${env.SWARM_HOST}"
|
||||
CREDS = credentials('docker-registry')
|
||||
IMAGE = "$REGISTRY/verdnatura/hedera-web"
|
||||
}
|
||||
steps {
|
||||
sh "docker stack deploy --with-registry-auth --compose-file docker-compose.yml ${env.STACK_NAME}"
|
||||
script {
|
||||
def packageJson = readJSON file: 'package.json'
|
||||
env.VERSION = "${packageJson.version}"
|
||||
env.TAG = "${packageJson.version}-build${env.BUILD_ID}"
|
||||
}
|
||||
|
||||
sh 'docker-compose build --build-arg BUILD_ID=$BUILD_ID --parallel'
|
||||
sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY'
|
||||
sh 'docker push $IMAGE:$TAG'
|
||||
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'master') {
|
||||
sh 'docker tag $IMAGE:$TAG $IMAGE:latest'
|
||||
sh 'docker push $IMAGE:latest'
|
||||
}
|
||||
}
|
||||
|
||||
withKubeConfig([
|
||||
serverUrl: "$KUBERNETES_API",
|
||||
credentialsId: 'kubernetes',
|
||||
namespace: 'salix'
|
||||
]) {
|
||||
sh 'kubectl set image deployment/hedera-web-$BRANCH_NAME hedera-web-$BRANCH_NAME=$IMAGE:$TAG'
|
||||
sh 'kubectl set image deployment/hedera-web-cron-$BRANCH_NAME hedera-web-cron-$BRANCH_NAME=$IMAGE:$TAG'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
unsuccessful {
|
||||
setEnv()
|
||||
sendEmail()
|
||||
}
|
||||
}
|
||||
|
|
18
README.md
|
@ -5,17 +5,26 @@ Hedera is the main web page for Verdnatura.
|
|||
## Getting Started
|
||||
|
||||
Required dependencies.
|
||||
* PHP >= 7.0
|
||||
* Node.js >= 8.0
|
||||
|
||||
- PHP >= 7.0
|
||||
- Node.js >= 8.0
|
||||
|
||||
Launch application for development.
|
||||
|
||||
```
|
||||
$ npm run dev
|
||||
$ npm run dev
|
||||
```
|
||||
|
||||
Launch project backend.
|
||||
|
||||
```
|
||||
$ php -S 127.0.0.1:3002 -t . index.php
|
||||
|
||||
Run server side method from command line.
|
||||
```
|
||||
$ php hedera-web.php -m method_path
|
||||
|
||||
$ php hedera-web.php -m method_path
|
||||
|
||||
```
|
||||
|
||||
## Built with
|
||||
|
@ -23,3 +32,4 @@ $ php hedera-web.php -m method_path
|
|||
* [Webpack](https://webpack.js.org/)
|
||||
* [MooTools](https://mootools.net/)
|
||||
* [TinyMCE](https://www.tinymce.com/)
|
||||
```
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/* eslint-disable */
|
||||
|
||||
module.exports = api => {
|
||||
return {
|
||||
presets: [
|
||||
[
|
||||
'@quasar/babel-preset-app',
|
||||
api.caller(caller => caller && caller.target === 'node')
|
||||
? { targets: { node: 'current' } }
|
||||
: {}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
|
@ -51,4 +51,4 @@
|
|||
}
|
||||
.new-text li {
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,123 +1,130 @@
|
|||
|
||||
var Result = require('./result');
|
||||
const Result = require('./result');
|
||||
|
||||
/**
|
||||
* This class stores the database results.
|
||||
*/
|
||||
module.exports = new Class({
|
||||
results: null
|
||||
,error: null
|
||||
results: null,
|
||||
error: null,
|
||||
|
||||
/**
|
||||
* Initilizes the resultset object.
|
||||
*/
|
||||
,initialize(results, error) {
|
||||
this.results = results;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the query error.
|
||||
*
|
||||
* @return {Db.Err} the error or null if no errors hapened
|
||||
*/
|
||||
,getError() {
|
||||
return this.error;
|
||||
}
|
||||
|
||||
,fetch() {
|
||||
if (this.error)
|
||||
throw this.error;
|
||||
|
||||
if (this.results !== null
|
||||
&& this.results.length > 0)
|
||||
return this.results.shift();
|
||||
/**
|
||||
* Initilizes the resultset object.
|
||||
*/
|
||||
initialize(results, error) {
|
||||
this.results = results;
|
||||
this.error = error;
|
||||
},
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetchs the next result from the resultset.
|
||||
*
|
||||
* @return {Db.Result} the result or %null if error or there are no more results
|
||||
*/
|
||||
,fetchResult() {
|
||||
var result = this.fetch();
|
||||
|
||||
if (result !== null) {
|
||||
if (result.data instanceof Array)
|
||||
return new Result(result);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Gets the query error.
|
||||
*
|
||||
* @return {Db.Err} the error or null if no errors hapened
|
||||
*/
|
||||
getError() {
|
||||
return this.error;
|
||||
},
|
||||
|
||||
return null;
|
||||
}
|
||||
fetch() {
|
||||
if (this.error) {
|
||||
throw this.error;
|
||||
}
|
||||
console.log('this.results', this.results);
|
||||
if (this.results !== null && this.results.length > 0) {
|
||||
return this.results.shift();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetchs the first row object from the next resultset.
|
||||
*
|
||||
* @return {Array} the row if success, %null otherwise
|
||||
*/
|
||||
,fetchObject() {
|
||||
var result = this.fetch();
|
||||
return null;
|
||||
},
|
||||
|
||||
if (result !== null
|
||||
&& result.data instanceof Array
|
||||
&& result.data.length > 0)
|
||||
return result.data[0];
|
||||
/**
|
||||
* Fetchs the next result from the resultset.
|
||||
*
|
||||
* @return {Db.Result} the result or %null if error or there are no more results
|
||||
*/
|
||||
fetchResult() {
|
||||
const result = this.fetch();
|
||||
console.log('test result', result);
|
||||
if (result !== null) {
|
||||
if (result.data instanceof Array) {
|
||||
return new Result(result);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetchs data from the next resultset.
|
||||
*
|
||||
* @return {Array} the data
|
||||
*/
|
||||
,fetchData() {
|
||||
var result = this.fetch();
|
||||
/**
|
||||
* Fetchs the first row object from the next resultset.
|
||||
*
|
||||
* @return {Array} the row if success, %null otherwise
|
||||
*/
|
||||
fetchObject() {
|
||||
const result = this.fetch();
|
||||
|
||||
if (result !== null
|
||||
&& result.data instanceof Array)
|
||||
return result.data;
|
||||
if (
|
||||
result !== null &&
|
||||
result.data instanceof Array &&
|
||||
result.data.length > 0
|
||||
) {
|
||||
return result.data[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetchs the first row and column value from the next resultset.
|
||||
*
|
||||
* @return {Object} the value if success, %null otherwise
|
||||
*/
|
||||
,fetchValue() {
|
||||
var row = this.fetchRow();
|
||||
/**
|
||||
* Fetchs data from the next resultset.
|
||||
*
|
||||
* @return {Array} the data
|
||||
*/
|
||||
fetchData() {
|
||||
const result = this.fetch();
|
||||
|
||||
if (row instanceof Array && row.length > 0)
|
||||
return row[0];
|
||||
if (result !== null && result.data instanceof Array) {
|
||||
return result.data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetchs the first row from the next resultset.
|
||||
*
|
||||
* @return {Array} the row if success, %null otherwise
|
||||
*/
|
||||
,fetchRow() {
|
||||
var result = this.fetch();
|
||||
/**
|
||||
* Fetchs the first row and column value from the next resultset.
|
||||
*
|
||||
* @return {Object} the value if success, %null otherwise
|
||||
*/
|
||||
fetchValue() {
|
||||
const row = this.fetchRow();
|
||||
|
||||
if (result !== null
|
||||
&& result.data instanceof Array
|
||||
&& result.data.length > 0) {
|
||||
var object = result.data[0];
|
||||
var row = new Array(result.columns.length);
|
||||
for(var i = 0; i < row.length; i++)
|
||||
row[i] = object[result.columns[i].name];
|
||||
return row;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
if (row instanceof Array && row.length > 0) {
|
||||
return row[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetchs the first row from the next resultset.
|
||||
*
|
||||
* @return {Array} the row if success, %null otherwise
|
||||
*/
|
||||
fetchRow() {
|
||||
const result = this.fetch();
|
||||
|
||||
if (
|
||||
result !== null &&
|
||||
result.data instanceof Array &&
|
||||
result.data.length > 0
|
||||
) {
|
||||
const object = result.data[0];
|
||||
const row = new Array(result.columns.length);
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
row[i] = object[result.columns[i].name];
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</div>
|
||||
<form id="form">
|
||||
<div class="form-group">
|
||||
<input
|
||||
<input
|
||||
placeholder="_User"
|
||||
type="text"
|
||||
id="user"
|
||||
|
@ -45,7 +45,7 @@
|
|||
</form>
|
||||
<div class="footer">
|
||||
<p>
|
||||
<t>Yet you are not a customer?</t>
|
||||
<t>Yet you are not a customer?</t>
|
||||
<a href="//verdnatura.es/register/" target="_blank"><t>Sign up</t></a>
|
||||
</p>
|
||||
<p class="contact">
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"src/*": [
|
||||
"src/*"
|
||||
],
|
||||
"app/*": [
|
||||
"*"
|
||||
],
|
||||
"components/*": [
|
||||
"src/components/*"
|
||||
],
|
||||
"layouts/*": [
|
||||
"src/layouts/*"
|
||||
],
|
||||
"pages/*": [
|
||||
"src/pages/*"
|
||||
],
|
||||
"assets/*": [
|
||||
"src/assets/*"
|
||||
],
|
||||
"boot/*": [
|
||||
"src/boot/*"
|
||||
],
|
||||
"stores/*": [
|
||||
"src/stores/*"
|
||||
],
|
||||
"vue$": [
|
||||
"node_modules/vue/dist/vue.runtime.esm-bundler.js"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
".quasar",
|
||||
"node_modules"
|
||||
]
|
||||
}
|
135
package.json
|
@ -1,49 +1,90 @@
|
|||
{
|
||||
"name": "hedera-web",
|
||||
"version": "22.48.2",
|
||||
"description": "Verdnatura web page",
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.verdnatura.es/hedera-web"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"archiver": "^5.3.1",
|
||||
"assets-webpack-plugin": "^7.1.1",
|
||||
"babel-loader": "^9.1.0",
|
||||
"bundle-loader": "^0.5.6",
|
||||
"css-loader": "^6.7.2",
|
||||
"eslint": "^8.15.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"glob": "^8.0.3",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"json-loader": "^0.5.7",
|
||||
"mini-css-extract-plugin": "^2.7.0",
|
||||
"node-sass": "^7.0.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass-loader": "^12.6.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-dev-server": "^4.11.1",
|
||||
"webpack-merge": "^5.8.0",
|
||||
"yaml-loader": "^0.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"js-yaml": "^3.12.1",
|
||||
"mootools": "^1.5.2",
|
||||
"promise-polyfill": "^8.2.3",
|
||||
"require-yaml": "0.0.1",
|
||||
"tinymce": "^6.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"front": "webpack serve --open",
|
||||
"back": "cd ../salix && gulp backOnly",
|
||||
"db": "cd ../vn-database && myvc run",
|
||||
"build": "rm -rf build/ ; webpack",
|
||||
"clean": "rm -rf build/"
|
||||
}
|
||||
"name": "hedera-web",
|
||||
"version": "22.48.2",
|
||||
"description": "Verdnatura web page",
|
||||
"license": "GPL-3.0",
|
||||
"productName": "Salix",
|
||||
"author": "Verdnatura",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.verdnatura.es/hedera-web"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.13.14",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@intlify/vue-i18n-loader": "^4.2.0",
|
||||
"@quasar/app-webpack": "^3.0.0",
|
||||
"archiver": "^5.3.1",
|
||||
"assets-webpack-plugin": "^7.1.1",
|
||||
"babel-loader": "^9.1.0",
|
||||
"bundle-loader": "^0.5.6",
|
||||
"css-loader": "^5.2.7",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-plugin-import": "^2.19.1",
|
||||
"eslint-plugin-n": "^15.0.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint-plugin-vue": "^9.27.0",
|
||||
"eslint-webpack-plugin": "^3.1.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"glob": "^8.0.3",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"json-loader": "^0.5.7",
|
||||
"mini-css-extract-plugin": "^2.7.0",
|
||||
"node-sass": "^7.0.1",
|
||||
"postcss": "^8.4.39",
|
||||
"postcss-import": "^13.0.0",
|
||||
"postcss-loader": "^4.3.0",
|
||||
"postcss-url": "^10.1.3",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass-loader": "^12.6.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-dev-server": "^4.11.1",
|
||||
"webpack-merge": "^5.8.0",
|
||||
"yaml-loader": "^0.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.0.0",
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.6.5",
|
||||
"js-yaml": "^3.12.1",
|
||||
"mootools": "^1.5.2",
|
||||
"pinia": "^2.0.11",
|
||||
"promise-polyfill": "^8.2.3",
|
||||
"quasar": "^2.6.0",
|
||||
"require-yaml": "0.0.1",
|
||||
"tinymce": "^6.3.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"front": "webpack serve --open",
|
||||
"back": "cd ../vn-database && myvc start && cd ../salix && gulp backOnly",
|
||||
"build": "rm -rf build/ ; webpack",
|
||||
"clean": "rm -rf build/",
|
||||
"lint": "eslint --ext .js,.vue ./"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 10 Chrome versions",
|
||||
"last 10 Firefox versions",
|
||||
"last 4 Edge versions",
|
||||
"last 7 Safari versions",
|
||||
"last 8 Android versions",
|
||||
"last 8 ChromeAndroid versions",
|
||||
"last 8 FirefoxAndroid versions",
|
||||
"last 10 iOS versions",
|
||||
"last 5 Opera versions"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.22.1",
|
||||
"npm": ">= 6.13.4",
|
||||
"yarn": ">= 1.21.1"
|
||||
}
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 552 B |
After Width: | Height: | Size: 974 B |
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 40 40"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="accessory.svg"
|
||||
width="40"
|
||||
height="40"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs9" /><sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.595"
|
||||
inkscape:cx="100"
|
||||
inkscape:cy="99.860918"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#1E1E1C;}
|
||||
.st1{fill:none;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
.st2{fill:#FFFFFF;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<path
|
||||
class="st0"
|
||||
d="M 37.313228,1.0829325 H 2.714188 C 1.233722,1.0829325 0,2.2892385 0,3.7971205 v 5.099383 c 0,1.4804655 1.206306,2.7141875 2.714188,2.7141875 h 2.083619 v 0.08225 L 7.978067,33.8451 c 0.411241,2.933516 2.63194,5.071968 5.236464,5.071968 h 13.543522 c 2.63194,0 4.825223,-2.138452 5.236464,-5.071968 l 3.207676,-22.234407 h 2.083619 C 38.766278,11.610693 40,10.404387 40,8.8965055 v -5.126801 c 0,-1.480466 -1.206306,-2.686772 -2.686772,-2.686772 z m -4.386566,10.6374225 -3.152844,21.79575 c -0.274161,1.809459 -1.535298,3.125429 -3.015765,3.125429 H 13.241947 c -1.480467,0 -2.76902,-1.31597 -3.015765,-3.125429 L 7.073338,11.583275 h 25.908156 z m 4.825223,-2.8238515 c 0,0.246744 -0.191912,0.466073 -0.466073,0.466073 H 2.714188 c -0.246745,0 -0.466073,-0.191913 -0.466073,-0.466073 v -5.126799 c 0,-0.246745 0.191912,-0.466073 0.466073,-0.466073 h 34.59904 c 0.246745,0 0.466073,0.191912 0.466073,0.466073 v 5.126799 z"
|
||||
id="path4"
|
||||
style="stroke-width:0.27416;fill:#1a1a1a;fill-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 40 40"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="artificial.svg"
|
||||
width="40"
|
||||
height="40"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs13" /><sodipodi:namedview
|
||||
id="namedview11"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.595"
|
||||
inkscape:cx="100"
|
||||
inkscape:cy="99.860918"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#1E1E1C;}
|
||||
.st1{fill:none;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
.st2{fill:#FFFFFF;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<g
|
||||
id="g8"
|
||||
transform="matrix(0.26820887,0,0,0.26820887,-6.5074179,-6.820887)"
|
||||
style="fill:#1a1a1a;fill-opacity:1">
|
||||
<path
|
||||
class="st0"
|
||||
d="m 113.7,83.3 c -4.1,0 -8.2,0 -12.4,0 -5.2,0 -10.4,0 -15.6,0 h -2 c -1.5,0 -1.8,0.2 -1.8,1.8 v 30.2 c 0,0.6 0,0.9 0.3,1.2 0.3,0.3 0.6,0.3 1.3,0.3 h 30.2 c 1.5,0 1.7,-0.3 1.7,-1.7 0,-10 0,-20.1 0,-30.1 0.1,-1.5 -0.2,-1.7 -1.7,-1.7 z m -6.4,17.5 v 7.6 h -0.6 c -1.3,0 -2.6,0 -3.9,0 h -1.7 -4.5 c -1.9,0 -3.9,0 -5.8,0 H 90.4 L 90.3,108 v 0 c 0,-5.5 0,-10.8 0,-16.2 v -0.4 h 0.5 c 6.4,0 11.4,0 16,0 h 0.3 l 0.2,0.3 c 0,0 0,0.1 0,0.2 0,3.1 0,6 0,8.9 z"
|
||||
id="path4"
|
||||
style="fill:#1a1a1a;fill-opacity:1" />
|
||||
<path
|
||||
class="st0"
|
||||
d="m 145.9,158.2 25.8,-44.7 c 0.3,-0.5 0.6,-1 0.9,-1.6 l 0.8,-1.4 L 155.2,100 173.4,89.5 173,88.7 c -0.1,-0.3 -0.3,-0.5 -0.4,-0.8 L 146.3,42.1 c -0.4,-0.8 -0.8,-1.1 -1.1,-1.2 -0.3,-0.1 -0.8,0.1 -1.5,0.5 l -4.6,2.6 c -3.8,2.2 -7.5,4.3 -11.3,6.5 L 127.1,51 V 31.7 c 0,-0.7 0,-1.1 -0.3,-1.4 C 126.5,30 126.2,30 125.4,30 H 72.5 c -0.1,0 -0.3,0 -0.5,0 -0.5,0 -0.8,0.1 -1.1,0.3 -0.2,0.2 -0.3,0.6 -0.3,1 0,0.2 0,0.4 0,0.6 v 0.4 3.6 c 0,4.8 0,9.6 0,14.4 V 51 L 52.9,40.8 52.4,40.7 52.1,41.1 c -0.1,0.1 -0.2,0.2 -0.3,0.3 L 25.1,87.5 c -1,1.7 -0.9,2 0.8,3 L 42.3,100 32,105.8 c -2.3,1.3 -4.5,2.6 -6.8,3.9 -0.4,0.2 -0.8,0.5 -0.9,1 -0.1,0.3 0,0.7 0.3,1.1 0.1,0.2 0.2,0.3 0.3,0.5 l 25.9,45 c 0.3,0.6 0.7,1.2 1.2,1.8 l 0.3,0.3 18.1,-10.4 v 2.5 c 0,1.6 0,3.1 0,4.6 l -0.1,12.1 c 0,1.5 0.2,1.7 1.6,1.7 h 2.5 c 1.6,0 3.1,0.1 4.7,0.1 h 45.1 c 2.4,0 2.6,-0.2 2.6,-2.7 v -17.4 -0.8 l 0.6,0.3 c 2.8,1.6 5.6,3.2 8.3,4.8 l 1.9,1.1 c 2.1,1.2 4.2,2.4 6.3,3.6 0.2,0.1 0.7,0.4 1.1,0.2 0.4,0 0.6,-0.2 0.9,-0.9 z m -3.8,-10 -0.3,-0.2 -22.7,-13.1 -0.2,0.5 c -0.3,0.6 -0.3,1.1 -0.2,1.6 v 0.3 c 0,6.4 0,12.8 0,19.3 v 4.1 c 0,0.2 0,0.4 0,0.7 v 0.4 h -40 v -0.7 c 0,-1.8 0,-3.6 0,-5.3 l 0.1,-20.9 -0.7,0.3 c -1.9,0.8 -3.5,1.8 -5.2,2.8 -0.9,0.5 -1.8,1 -2.6,1.5 -1.6,0.9 -3.2,1.8 -5,2.9 l -10,5.8 -19.9,-34.6 14.6,-8.4 9,-5.2 -6.5,-3.7 -17.2,-10 0.3,-0.6 19.2,-33.1 c 0.1,-0.2 0.2,-0.3 0.3,-0.5 l 0.2,-0.3 0.5,0.2 c 1.3,0.8 2.7,1.6 4,2.4 l 17.4,10.1 c 0.3,0.2 0.7,0.4 1.2,0.1 l 0.3,-0.2 c 0.2,-0.3 0.2,-0.7 0.2,-1.2 0,-0.9 0,-1.7 0,-2.5 0,-0.8 0,-1.5 0,-2.3 L 79,38.2 h 0.6 c 2.1,0 4.2,0 6.2,0.1 h 0.3 c 2.3,0.1 4.2,0.1 5.9,0.1 5.8,0 11.4,0 18.1,0 h 7.9 0.1 c 0.1,0 0.2,0 0.3,0 h 0.3 v 27.1 l 23.6,-13.5 19.9,34.6 -23.5,13.5 23.5,13.6 -0.2,0.3 z"
|
||||
id="path6"
|
||||
style="fill:#1a1a1a;fill-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 40 40"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="flower.svg"
|
||||
width="40"
|
||||
height="40"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs13"><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient981"><stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop977" /><stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop979" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient981"
|
||||
id="linearGradient983"
|
||||
x1="34.739397"
|
||||
y1="99.599534"
|
||||
x2="165.73375"
|
||||
y2="99.599534"
|
||||
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
|
||||
id="namedview11"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4.365"
|
||||
inkscape:cx="102.29095"
|
||||
inkscape:cy="63.459336"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#1E1E1C;}
|
||||
.st1{fill:none;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
.st2{fill:#FFFFFF;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<g
|
||||
id="g8"
|
||||
transform="matrix(0.28530481,0,0,0.28530481,-8.5979763,-8.4162261)"
|
||||
style="stroke:none;stroke-opacity:1;fill:#1a1a1a;fill-opacity:1">
|
||||
<path
|
||||
class="st0"
|
||||
d="m 100.1,76.9 h -0.5 c -6.1,0 -11.8,2.3 -16.1,6.5 -4.4,4.3 -6.8,10.1 -6.8,16.3 0,6.2 2.4,12 6.7,16.2 4.2,4.2 10,6.5 15.9,6.5 h 0.3 0.3 c 5.9,0 11.6,-2.4 15.9,-6.5 4.3,-4.2 6.7,-10 6.7,-16.2 0,-12.5 -9.9,-22.5 -22.4,-22.8 z M 89.2,89.1 c 2.7,-2.7 6.4,-4.2 10,-4.2 h 0.4 c 3.8,-0.1 7.6,1.4 10.5,4.2 2.9,2.8 4.5,6.6 4.5,10.8 0,8.2 -6.8,14.9 -15,14.9 h -0.1 c -8,0 -14.8,-6.8 -14.8,-14.9 -0.1,-4.1 1.5,-8 4.5,-10.8 z"
|
||||
id="path4"
|
||||
style="stroke:none;stroke-opacity:1;fill:#1a1a1a;fill-opacity:1" />
|
||||
<path
|
||||
class="st0"
|
||||
d="m 157.2,102 -0.2,-0.2 0.2,-0.2 c 0.1,-0.1 0.2,-0.2 0.3,-0.3 6.1,-6.5 8.9,-14.4 8.1,-23.2 -0.7,-7.9 -4.1,-14.5 -10,-19.7 -5.1,-4.4 -10.9,-6.8 -17.3,-7.3 v 0 h -0.4 c -1.7,-0.1 -3.4,0 -5.2,0.1 -0.8,0.1 -1.5,0.1 -2.2,0.2 h -0.1 c -0.1,-0.1 -0.2,-0.4 -0.4,-1 -1.7,-5.4 -4.7,-10 -8.9,-13.6 -6.8,-5.8 -14.8,-8.2 -23.6,-7 -6.5,0.8 -12.3,3.7 -17.1,8.5 -3,3 -5.3,6.6 -6.8,10.7 L 73.4,49.5 73,49.3 C 68.6,48.2 64.2,48.2 59.9,49 53.3,50.4 47.8,53.5 43.6,58.4 37.1,66.1 34.8,75 36.9,85 c 1,4.6 3,8.7 6.1,12.4 L 43.3,97.8 43,98 c -2.7,2.8 -4.8,6 -6.2,9.6 v 0 c 0,0.1 -4.5,9.7 -0.2,21.5 0,0.1 0,0.1 0.1,0.2 0,0.1 0.1,0.2 0.1,0.4 0.4,1 0.8,2 1.3,2.9 0.4,0.9 0.8,1.5 1.1,2 3.3,5.4 8.2,9.4 14.4,11.8 5,2 10.4,2.5 16,1.6 l 0.3,-0.1 0.2,0.5 c 0.9,3.2 2.3,6.1 4.1,8.6 4.4,6.2 10.2,10.2 17.4,12 0.8,0.2 3.6,0.7 7.3,0.7 2.3,0 4.9,-0.2 7.6,-0.9 l 0.3,-0.1 c 0.3,-0.1 0.5,-0.1 0.7,-0.2 0.1,0 0.2,-0.1 0.4,-0.1 l 0.2,-0.1 c 4.2,-1.4 8.1,-3.7 11.5,-7 3.1,-3 5.5,-6.6 7,-10.7 0.1,-0.2 0.1,-0.3 0.2,-0.5 l 0.1,-0.2 h 0.2 c 0.1,0 0.3,0.1 0.5,0.1 3,0.7 5.8,0.9 8.6,0.8 4.3,-0.2 8.4,-1.3 12.1,-3.3 6,-3.2 10.5,-7.8 13.3,-13.9 2.9,-6.4 3.5,-13.1 1.8,-20.1 -1.3,-4.1 -3.3,-8.1 -6.2,-11.5 z M 149,98.3 c -0.8,0.6 -1.6,1.2 -2.3,1.7 l -1.7,1.2 0.4,0.5 c 0.7,0.9 1.6,1.6 2.3,2.2 0.4,0.3 0.8,0.6 1.1,1 4.6,4.2 7,9.2 7.4,15.3 0.3,5.9 -1.5,11.1 -5.3,15.4 -4.8,5.4 -10.8,7.9 -18,7.5 -2.6,-0.2 -5.2,-0.9 -8.1,-2.2 -0.6,-0.2 -1.1,-0.5 -1.7,-0.7 l -1.9,-0.8 -1.1,5.3 c -1.9,9.3 -9.8,16.4 -19.4,17.3 -0.7,0.1 -1.4,0.1 -2.1,0.1 -5,0 -9.9,-1.8 -13.9,-5.1 -3.9,-3.3 -6.6,-7.9 -7.5,-12.9 -0.2,-1.2 -0.4,-2.5 -0.5,-3.7 l -0.1,-0.9 c 0,-0.4 -0.1,-1 -0.5,-1.5 l -0.3,-0.3 -1.8,0.6 c -0.9,0.3 -1.8,0.6 -2.7,0.9 -8.9,3.1 -19,-0.1 -24.6,-7.8 -2.6,-3.6 -4,-7.6 -4.1,-11.8 -0.3,-7.8 2.9,-14.2 9.4,-19 L 55.4,98 55,97.7 c -0.7,-0.7 -1.5,-1.4 -2.2,-2 -0.9,-0.7 -1.7,-1.5 -2.5,-2.3 -4,-4.2 -6.1,-9.1 -6.2,-14.8 -0.1,-5.4 1.6,-10.2 5,-14.3 3.5,-4.2 8,-6.8 13.4,-7.7 4.2,-0.7 8.4,-0.1 12.6,1.8 0.8,0.4 1.6,0.7 2.6,1.1 0.6,0.2 0.9,0.4 1.3,0.2 0.4,-0.2 0.5,-0.6 0.6,-1.2 0.1,-0.6 0.2,-1.2 0.3,-1.8 v -0.1 c 0.2,-1.2 0.4,-2.3 0.8,-3.4 2.8,-8.5 8.6,-13.7 17.2,-15.3 7.4,-1.4 13.9,0.7 19.5,6.3 3.5,3.5 5.6,8 6.1,13.2 0.1,0.8 0.2,1.6 0.3,2.4 l 0.2,2 0.6,-0.1 c 1,-0.2 1.9,-0.5 2.7,-0.8 l 0.1,-0.1 c 0.3,-0.1 0.5,-0.2 0.8,-0.3 8,-2.8 15.3,-1.5 21.8,3.8 4.6,3.8 7.2,8.8 7.7,14.8 0.6,7.7 -2.4,14.2 -8.7,19.2 z"
|
||||
id="path6"
|
||||
style="stroke:none;stroke-opacity:1;fill:#1a1a1a;fill-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.9 KiB |
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 40 40"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="fruit.svg"
|
||||
width="40"
|
||||
height="40"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs9" /><sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.595"
|
||||
inkscape:cx="100"
|
||||
inkscape:cy="99.860918"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#1E1E1C;}
|
||||
.st1{fill:none;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
.st2{fill:#FFFFFF;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<path
|
||||
class="st0"
|
||||
d="m 4.6155643,32.24963 c 1.033877,0 2.041908,0.232623 2.998243,0.67202 0.18093,0.07754 0.387705,0.180928 0.568633,0.284316 l 0.103387,0.05169 c 1.9902137,0.982183 4.2388957,1.473275 6.4875777,1.473275 1.68005,0 3.3601,-0.284317 4.936762,-0.852949 h 0.02585 l 0.05169,0.02585 c 1.60251,0.568632 3.282559,0.852948 4.988456,0.852948 0.05169,0 0.103387,0 0.155081,0 h 0.103388 c 0.02585,0 0.02585,0 0.05169,0 0.02585,0 0.05169,0 0.07754,0 h 0.02585 c 3.980425,-0.103388 7.676536,-1.75759 10.468003,-4.600753 2.791467,-2.843161 4.316438,-6.616811 4.342284,-10.597237 0,-0.284317 -0.103387,-0.542786 -0.310162,-0.749561 -0.206776,-0.206776 -0.465245,-0.310163 -0.723717,-0.310163 h -9.434126 l -0.02585,-0.103388 c -0.28431,-2.50715 -1.188952,-4.910914 -2.662226,-6.952821 -0.232622,-0.336009 -0.516938,-0.697866 -0.852948,-1.059723 l -0.05169,-0.05169 0.05169,-0.07754 c 1.550815,-1.8351313 1.628356,-3.1274773 1.628356,-3.3859473 0,-0.542785 -0.413551,-0.982183 -0.982183,-1.008029 0,0 0,0 0,0 -0.516938,0 -1.00803,0.439397 -1.059723,0.956336 0,0 -0.07754,0.74956 -1.03388,1.938519 l -0.02585,0.02585 -0.07754,0.02585 -0.05169,-0.02585 C 21.493607,6.2993277 17.797496,5.0586757 13.997999,5.2654507 10.04342,5.4722267 6.4248503,7.2298167 3.7884643,10.176366 c -4.03211997,4.497365 -4.936762,10.933249 -2.274529,16.361101 l 0.05169,0.103388 c 0.103387,0.180931 0.206775,0.361857 0.310163,0.594479 0.568632,1.240652 0.775407,2.610541 0.594479,3.954581 -0.05169,0.310163 0.05169,0.646172 0.284316,0.878795 0.232622,0.232622 0.568632,0.33601 0.878795,0.284316 0.310163,-0.07754 0.646173,-0.103388 0.982183,-0.103388 z m 33.2391427,-11.682808 0.07754,0.07754 v 0.05169 c -0.232622,2.636385 -1.240652,5.117691 -2.920704,7.185445 l -0.02585,0.02585 h -0.103388 l -0.05169,-0.02585 -5.221077,-5.22108 c -0.387704,-0.387704 -1.033876,-0.439397 -1.42158,-0.103387 -0.232622,0.180928 -0.361859,0.465244 -0.361859,0.74956 0,0.284316 0.10339,0.568635 0.310162,0.77541 l 5.324467,5.324465 v 0.05169 0.05169 l -0.02585,0.02585 c -2.016059,1.757593 -4.497363,2.843163 -7.133751,3.127479 h -0.05169 l -0.07754,-0.05169 v -0.05169 -8.271015 c 0,-0.568632 -0.413551,-1.033877 -0.930489,-1.08557 -0.284316,-0.02585 -0.568632,0.07754 -0.775408,0.258469 -0.206775,0.206775 -0.33601,0.465244 -0.33601,0.74956 v 8.426097 l -0.07754,0.07754 h -0.05169 c -1.266499,-0.07754 -2.481304,-0.33601 -3.670265,-0.749561 -0.129232,-0.07754 -0.232622,-0.10339 -0.336009,-0.129237 -1.240652,-0.491089 -2.377916,-1.163111 -3.411793,-1.990212 l -0.02585,-0.02585 v -0.103382 l 0.02585,-0.05169 5.557089,-5.557086 c 0.387704,-0.387704 0.439398,-1.033879 0.103385,-1.421583 -0.180926,-0.232622 -0.465242,-0.361857 -0.749558,-0.361857 -0.284316,-0.02585 -0.568635,0.103388 -0.77541,0.310163 l -5.660475,5.660477 h -0.05169 -0.05169 l -0.02585,-0.02585 c -1.860989,-2.119449 -2.972406,-4.729988 -3.230875,-7.547301 v -0.05169 l 0.05169,-0.07754 h 0.05169 26.053699 z m -34.5056417,5.014302 -0.02585,-0.02585 C 1.0745333,20.90283 1.8499413,15.397433 5.3134283,11.546242 7.5879573,9.0132437 10.689589,7.5141217 14.101382,7.3331937 c 3.385947,-0.180928 6.668506,0.956336 9.201504,3.2308653 0.129234,0.103388 0.232622,0.206776 0.361857,0.33601 l 0.180931,0.180929 c 0.155079,0.155081 0.310163,0.310163 0.465242,0.491091 l 0.02585,0.02585 c 0.310163,0.33601 0.594479,0.723714 0.878795,1.085571 1.214805,1.68005 1.990212,3.670263 2.248681,5.738017 v 0.05169 l -0.05169,0.07754 h -0.05169 -16.774653 c -0.284316,0 -0.542785,0.103388 -0.7237137,0.310163 -0.206775,0.206776 -0.310163,0.465245 -0.310163,0.749562 0,5.221077 2.6363857,10.002759 7.0562097,12.845919 l 0.05169,0.02585 -0.02585,0.155081 -0.07754,0.02585 c -0.620326,0.07754 -1.214806,0.129231 -1.835132,0.129231 -1.912672,0 -3.77365,-0.413548 -5.5053937,-1.266498 h -0.02585 c -0.232619,-0.15508 -0.516934,-0.284314 -0.749557,-0.387702 -1.188958,-0.568632 -2.455457,-0.852948 -3.747804,-0.852948 h -0.103388 v -0.103388 c 0,-1.292348 -0.310163,-2.558847 -0.827101,-3.721958 -0.129235,-0.361857 -0.258469,-0.620326 -0.413551,-0.878795 z"
|
||||
id="path4"
|
||||
style="stroke-width:0.258469;fill:#1a1a1a;fill-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 5.3 KiB |
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 40 40"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="greenery.svg"
|
||||
width="40"
|
||||
height="40"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs9" /><sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.595"
|
||||
inkscape:cx="100"
|
||||
inkscape:cy="99.860918"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#1E1E1C;}
|
||||
.st1{fill:none;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
.st2{fill:#FFFFFF;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<path
|
||||
class="st0"
|
||||
d="M 34.045254,21.098493 C 33.702189,16.753004 30.814725,12.321749 25.46863,7.9190818 21.15173,4.3740778 16.405999,1.8582678 14.54773,0.94342876 l -0.02859,-0.05718 h -0.02859 l -0.02859,-0.02859 c -0.428827,-0.228707 -0.714715,-0.343062 -0.800481,-0.400239 L 12.575107,-2.4007925e-7 12.031921,1.0577838 C 5.4279214,14.065662 4.1700164,23.642891 8.2867954,29.503584 c 3.6307706,5.174562 10.2919476,5.946458 13.9227186,5.946458 0.571774,0 1.114961,-0.02859 1.600969,-0.05718 h 0.05718 l 0.02859,0.05718 c 0.886252,1.572381 1.743914,2.944641 2.601576,4.088191 0.200121,0.257299 0.486009,0.428831 0.829074,0.45742 0.343065,0.02859 0.68613,-0.08577 0.943428,-0.285888 l 0.02859,-0.02859 c 0.486009,-0.428832 0.543186,-1.14355 0.142944,-1.658148 -0.743308,-1.000606 -1.543792,-2.201333 -2.315689,-3.602181 l -0.05718,-0.08577 0.08577,-0.05718 c 5.631983,-4.031013 8.262147,-8.462268 7.890494,-13.179411 z M 22.466812,32.99141 c -0.05718,0 -0.114355,0 -0.200121,0 -7.433073,0 -10.69219,-3.058995 -11.950095,-4.888675 -3.1733496,-4.54561 -2.3156876,-12.493281 2.51581,-22.9567612 l 0.114355,-0.285888 0.08577,0.285888 c 1.686736,6.4610562 5.145974,18.6684502 9.520052,27.6739042 l 0.08577,0.142944 z m 2.601576,-0.943428 -0.114355,0.08577 -0.05718,-0.114355 C 20.608544,23.271238 17.092128,10.949489 15.376804,4.4026668 l -0.05718,-0.22871 0.22871,0.114355 c 2.630165,1.458026 6.461056,3.802303 9.720173,6.7469432 3.945247,3.516416 6.060814,6.975654 6.318113,10.291948 0.285887,3.659359 -1.915446,7.29013 -6.518234,10.720779 z"
|
||||
id="path4"
|
||||
style="stroke-width:0.285887;fill:#1a1a1a;fill-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 40 40"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="handmade.svg"
|
||||
width="40"
|
||||
height="40"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs13" /><sodipodi:namedview
|
||||
id="namedview11"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.595"
|
||||
inkscape:cx="100"
|
||||
inkscape:cy="99.860918"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#1E1E1C;}
|
||||
.st1{fill:none;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
.st2{fill:#FFFFFF;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<g
|
||||
id="g8"
|
||||
transform="matrix(0.28579156,0,0,0.28579156,-8.7130268,-8.5559862)"
|
||||
style="fill:#1a1a1a;fill-opacity:1">
|
||||
<path
|
||||
class="st0"
|
||||
d="m 165,128.6 c 1.6,-6.6 2,-12.7 1.5,-18.7 -0.6,-5.6 -2,-10.4 -4.5,-14.8 -4.2,-7.5 -10.6,-12.8 -19,-15.5 -1,-0.3 -2,-0.6 -3.1,-0.8 l -1.8,-0.4 1.9,-0.3 c 1.4,-0.2 2.8,-0.5 4.1,-0.7 l 0.7,-0.1 c 4.4,-0.8 8.9,-1.6 13.3,-2.9 3.3,-1 6.2,-2.7 8.8,-4.5 l 0.8,-0.6 -0.7,-0.7 c -0.6,-0.6 -1.3,-1.3 -1.9,-1.9 -2,-2 -4,-4 -6.2,-5.8 -7.8,-6.3 -15,-9.6 -22.9,-10.6 -5.7,-0.7 -11.2,-0.1 -16.2,1.9 -1.6,0.6 -3.1,1.4 -4.2,2 l -0.3,0.2 -0.2,-0.3 c -1.2,-2.2 -2.8,-4.2 -4.9,-6 -4.1,-3.4 -9,-5 -14.7,-4.6 H 95.1 L 95,43.1 C 94,40.3 92.4,37.8 90.1,35.6 83.4,29.1 73.3,28.1 65.4,33.1 62.1,35.2 59.6,38 58,41.6 L 57.8,42.1 57.3,42 c -1.9,-0.3 -3.8,-0.3 -5.9,0 -4.9,0.6 -9.5,3.2 -12.8,7.2 -3.5,4.3 -5,9.7 -4.3,15.3 0.4,3.4 1.6,6.4 3.5,9.1 0,0.1 0.1,0.1 0.1,0.1 l 0.1,0.2 -0.2,0.3 c -3.4,4.2 -4.9,8.9 -4.5,14.2 0.5,6.8 3.7,12.2 9.6,15.9 3.5,2.3 7.7,3.2 12.3,2.8 l 0.3,-0.1 0.1,0.3 c 1.2,3.4 3.2,6.2 5.8,8.4 3.4,2.9 7.2,4.4 11.3,4.7 0.3,0 0.4,0.1 0.7,0.5 l 6.5,8.8 c 2.5,3.3 5,6.8 7.5,10.2 0.5,0.7 0.5,1.1 0.3,1.6 -0.4,0.9 -0.6,1.9 -0.9,2.7 v 0.1 c -0.1,0.4 -0.2,0.8 -0.3,1.2 l -2,6.5 c -1.3,4.1 -2.5,8.2 -3.8,12.2 -0.5,1.6 -0.3,3 0.4,4.1 0.8,1.1 2.1,1.6 3.8,1.6 H 109 c 1,0 2,0 3,0 2.7,0 5.4,0 8.1,0 1.2,0 2.2,-0.3 3,-1 1.7,-1.5 1.5,-3.4 1,-4.9 l -1.8,-5.9 c -1.7,-5.6 -3.4,-11.3 -5.1,-17.1 -0.1,-0.5 -0.1,-0.8 0.2,-1.3 4.3,-6.8 8.5,-13.7 12.7,-20.4 l 5.2,-8.3 c 0.2,-0.3 0.4,-0.7 0.7,-1 l 0.3,-0.4 7.9,9.1 c 3.1,3.6 6.4,7.4 10,10.7 2.5,2.3 5.5,3.8 8.4,5.2 l 0.9,0.4 0.7,-2.9 c 0.2,-1.2 0.5,-2.3 0.8,-3.5 z M 113.9,75 c 2.4,-3.5 3.6,-7.4 3.5,-11.7 0,-0.5 0.1,-0.7 0.6,-1 5.2,-3.3 10.5,-4.6 16.5,-4 5.4,0.6 10.5,2.7 16,6.5 0.7,0.5 1.5,1 2.3,1.7 l 0.6,0.5 -0.7,0.2 c -1.5,0.4 -3,0.7 -4.3,0.9 -3.1,0.6 -6.1,1.1 -9.1,1.6 -4,0.7 -8.1,1.4 -12.2,2.3 -3.5,0.7 -8.1,1.8 -12.7,3.5 l -1.1,0.4 z m -39.6,37.4 c -2.9,0 -5.7,-1.1 -7.9,-3.3 -2.2,-2.1 -3.5,-4.8 -3.8,-8.2 -0.1,-0.7 -0.1,-1.3 -0.2,-2 L 62.2,97 H 61.3 C 60.1,97.1 59,97.6 58,98 l -0.4,0.2 c -3,1.2 -6.4,1.1 -9.3,-0.3 -3,-1.4 -5.4,-4.1 -6.4,-7.3 -1.5,-4.7 0.2,-10 4.2,-13 l 4,-3 -0.5,-0.7 C 49,73 48.2,72.4 47.4,71.8 l -0.1,-0.1 c -0.2,-0.2 -0.5,-0.4 -0.7,-0.5 -3,-2.5 -4.5,-5.7 -4.4,-9.3 0.1,-5.3 3,-9.4 8,-11.2 2.8,-1 5.6,-0.9 8.5,0.3 l 4.7,2 0.3,-0.9 c 0.4,-1.1 0.6,-2.2 0.7,-3.3 l 0.2,-1 c 0.8,-4.4 4.5,-9.3 10.9,-9.6 4.2,-0.2 7.7,1.4 10.3,5 1.4,1.9 2.1,4.2 2.3,7.1 0.1,0.9 0.1,1.8 0.4,2.7 L 88.7,53.8 91,53 c 0.8,-0.3 1.6,-0.6 2.4,-0.8 5.5,-2 11.6,0.4 14.3,5.5 2.2,4.1 2,8.3 -0.6,12.5 -1,1.6 -2.5,2.8 -4.2,3.9 l -0.5,0.4 c -1,0.7 -1.4,1.1 -1.4,1.7 0,0.6 0.4,1.1 1.3,1.8 1.6,1.4 3.2,2.8 4.3,4.7 1.9,3.1 2.1,7.1 0.6,10.7 -1.5,3.6 -4.4,6.1 -8.3,7 -2.3,0.6 -4.7,0.3 -7.1,-0.7 -0.7,-0.3 -1.4,-0.6 -2.1,-0.9 L 87.2,97.7 87,98.4 c -0.4,1 -0.5,1.9 -0.7,2.8 l -0.1,0.7 c -0.7,4 -2.7,7 -6.1,8.9 -1.9,1 -3.9,1.6 -5.8,1.6 z m 46,7.2 c -3.9,6.3 -7.4,11.9 -11,17.7 -0.8,1.3 -0.9,2.6 -0.4,4.3 l 4.5,15.1 c 0.5,1.5 0.9,3 1.3,4.5 l 0.1,0.5 h -0.5 c -2.1,0 -4.2,0 -6.3,0 h -2.7 c -4.7,0 -9.7,0 -14.8,0.2 H 90 l 0.1,-0.5 c 0.2,-0.7 0.4,-1.4 0.6,-2.1 0.1,-0.4 0.3,-0.9 0.4,-1.3 1.8,-6 3.4,-11 4.9,-15.7 0.7,-2.1 0.3,-4 -1,-5.8 -3.2,-4.3 -6.5,-8.7 -9.4,-12.7 l -3.5,-4.7 0.4,-0.2 c 3.4,-1.6 6,-3.8 8.1,-6.7 0.7,-1 1.3,-2.1 1.8,-3.1 l 0.2,-0.5 0.5,0.1 c 3,0.5 5.9,0.3 8.6,-0.5 6.9,-2.1 11.5,-6.6 13.7,-13.5 l 0.1,-0.5 0.5,0.1 c 3.1,0.5 5.7,2.3 8.3,4.2 1.4,1 2.6,2.2 3.9,3.3 0.5,0.5 1.1,1 1.7,1.4 l 0.3,0.2 z m 38.1,-3.2 c -0.1,1.6 -0.2,3.1 -0.4,4.6 l -0.1,0.8 -1.3,-1.4 c -2.8,-3 -5.5,-6.2 -8.1,-9.2 -2,-2.3 -4.2,-4.9 -6.4,-7.3 -3.5,-3.8 -7.3,-7.9 -11.7,-11.4 -2.4,-1.9 -4.7,-3.4 -7,-4.5 l -0.9,-0.4 0.9,-0.3 c 0.8,-0.2 1.6,-0.4 2.5,-0.6 6.4,-1.1 12.4,-0.4 17.7,2.2 7.1,3.5 11.7,9.3 13.7,17.5 1,3.2 1.3,6.5 1.1,10 z"
|
||||
id="path4"
|
||||
style="fill:#1a1a1a;fill-opacity:1" />
|
||||
<path
|
||||
class="st0"
|
||||
d="m 75.4,59.4 h -0.2 -0.1 c -8.7,0.1 -15.5,7.1 -15.5,15.9 0,4.3 1.6,8.3 4.6,11.3 2.9,2.9 6.9,4.6 11.2,4.6 8.8,0 15.8,-7 15.8,-15.8 0,-4.3 -1.6,-8.3 -4.6,-11.2 -3,-3.1 -7,-4.8 -11.2,-4.8 z m -0.1,23.5 c -2,0 -3.9,-0.8 -5.3,-2.3 -1.4,-1.5 -2.2,-3.4 -2.2,-5.4 0.1,-4.3 3.3,-7.6 7.6,-7.6 2,0 3.8,0.8 5.2,2.2 1.5,1.5 2.3,3.5 2.3,5.5 0,4.2 -3.4,7.6 -7.6,7.6 z"
|
||||
id="path6"
|
||||
style="fill:#1a1a1a;fill-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.5 KiB |
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 40 40"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="handmadeArtificial.svg"
|
||||
width="40"
|
||||
height="40"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs13" /><sodipodi:namedview
|
||||
id="namedview11"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.595"
|
||||
inkscape:cx="100"
|
||||
inkscape:cy="99.860918"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#1E1E1C;}
|
||||
.st1{fill:none;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
.st2{fill:#FFFFFF;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<g
|
||||
id="g8"
|
||||
transform="matrix(0.2875629,0,0,0.2875629,-8.75629,-8.7994244)"
|
||||
style="fill:#1a1a1a;fill-opacity:1">
|
||||
<path
|
||||
class="st0"
|
||||
d="m 92.8,87.4 c 0,-0.2 0,-0.3 0,-0.4 V 65.7 c 0,-0.5 -0.1,-0.8 -0.3,-0.9 -0.2,-0.2 -0.5,-0.3 -1,-0.3 -0.1,0 -0.3,0 -0.4,0 h -20 -0.3 c -0.2,0 -0.4,0 -0.5,0 -0.4,0 -0.7,0.2 -0.8,0.2 -0.2,0.2 -0.3,0.5 -0.3,0.8 0,0.2 0,0.3 0,0.5 V 88 H 92.7 Z M 85.3,72.3 c 0,0.9 0,1.8 0,2.7 v 2.3 c 0,0.9 0,1.8 0,2.8 v 0.4 h -0.4 c -2.6,0 -5.2,0 -7.9,0 h -0.4 v -0.4 c 0,-2.6 0,-5.2 0,-7.9 V 71.8 H 77 c 2.6,0 5.1,0 7.9,0 h 0.2 z"
|
||||
id="path4"
|
||||
style="fill:#1a1a1a;fill-opacity:1" />
|
||||
<path
|
||||
class="st0"
|
||||
d="m 167.4,121.4 c -0.1,-0.4 -0.2,-0.7 -0.4,-1.1 L 153.1,81.9 C 152.2,79.8 150.9,80 150.9,80 l -16.9,4 1.2,-0.9 9.6,-6.7 v 0 l 19.6,-13.8 -39.3,-9.8 v 0 c -0.6,-0.1 -1.2,-0.3 -1.7,-0.4 -1,-0.2 -1.4,0.2 -1.8,0.9 -0.1,0.1 -0.1,0.3 -0.2,0.4 v 0 l -0.4,0.7 -9,-15.8 c -0.4,-0.8 -0.7,-1.1 -1.1,-1.2 -0.4,-0.1 -0.8,0.1 -1.5,0.5 l -2.7,1.5 c -2,1.2 -4,2.3 -6,3.5 l -0.6,0.3 -0.1,-0.6 v -2.3 c 0,-1.4 0,-2.7 0,-4.1 0,-0.5 0,-1 0,-1.5 0,-1.2 0,-2.4 -0.1,-3.5 V 30.6 H 64.1 C 62.4,30.7 62,32 62,32.7 V 33 c 0,3.2 0,6.4 0,9.6 v 0.6 L 61.4,42.9 C 60.7,42.5 60,42.1 59.3,41.7 l -1.6,-0.9 c -1.8,-1 -3.7,-2.1 -5.5,-3.2 -0.3,-0.2 -0.7,-0.4 -1.1,-0.2 -0.4,0.1 -0.6,0.6 -0.7,0.8 0,0.1 -0.1,0.2 -0.2,0.4 l -18.3,31.3 0.4,0.2 c 1.7,1.1 3.5,2.1 5.3,3.1 0.2,0.1 0.4,0.2 0.5,0.3 v 0 l 5,2.5 -2.8,1.6 c 0,0 -0.1,0 -0.1,0.1 l -8.3,4.8 0.2,0.4 c 1.9,3.7 17.8,31.4 18.8,32.4 l 0.2,0.3 6.2,-3.5 4.7,-3 -0.2,6.8 v 0 c 0,1.6 0,3.1 0,4.6 0,0.3 0,0.8 0.3,1.2 0.3,0.3 0.8,0.3 1,0.3 0.5,0 1.1,0 1.7,0.1 h 0.1 c 0.6,0.1 1.2,0.1 1.7,0.1 0.7,0 1.4,0 2.1,0 0.9,0 1.8,0 2.7,0 0.1,0 0.1,0 0.2,0 0.7,0 1.2,0.3 1.8,0.9 4,5.1 8.4,10.6 13.4,16.7 0.5,0.6 0.6,1.1 0.4,1.8 -1.9,5.9 -3.9,11.7 -5.8,17.6 l -2.8,7.9 c -0.2,0.6 -0.1,1.2 0.2,1.8 0.4,0.5 0.9,0.8 1.6,0.8 h 44.2 l -0.2,-1 c -0.1,-0.2 -0.1,-0.4 -0.1,-0.6 l -1.8,-6.1 c -1.9,-6.6 -3.9,-13.4 -5.9,-20.3 -0.3,-1 -0.2,-1.7 0.4,-2.7 4.4,-7 8.8,-14.1 13.2,-21.1 l 3.8,-6.1 c 0.1,-0.1 0.2,-0.3 0.2,-0.4 l 0.3,0.1 0.1,-0.4 c 0.2,0 0.4,0.1 0.8,0.3 l 30.3,11.3 c 0.3,0.1 0.6,0.2 1,0.3 l 1.4,0.5 z M 129.3,70.5 c 0.5,-0.3 0.6,-0.8 0.4,-1.3 l -0.9,-1.5 c -0.7,-1.3 -1.5,-2.5 -2.2,-3.8 l -2.6,-4 16.5,4 v 0 l 0.7,0.2 5.8,1.9 -2.6,1.7 -4.6,3 c -4.6,3 -9.2,5.9 -13.8,8.9 -0.2,0.2 -0.4,0.2 -0.6,0.2 -0.2,0 -0.3,-0.1 -0.5,-0.2 -1.5,-0.9 -3,-1.8 -4.5,-2.7 l -1.1,-0.6 z m -59,43.9 h -0.9 v -0.5 -17.6 l -0.7,0.3 c -1.7,0.8 -3.3,1.8 -4.9,2.7 l -0.1,0.1 c -0.9,0.5 -1.8,1.1 -2.7,1.6 -1.8,1 -3.7,2.1 -5.3,3.1 l -2,1.2 -0.6,-1.1 c -0.6,-1 -1.2,-2 -1.8,-3 -3,-5.1 -5.9,-10.3 -8.8,-15.4 L 42.2,85.3 42.7,85 56,77.3 c 0.2,-0.1 0.4,-0.3 0.7,-0.4 L 57.8,76.2 43.7,68 C 42.9,67.5 42.6,66.5 43.1,65.7 L 53,48.6 c 0.5,-0.8 1.5,-1.1 2.3,-0.6 L 68,55.3 c 0.3,0.2 0.6,0.2 0.9,0 0.3,-0.2 0.5,-0.5 0.5,-0.8 v -0.8 c 0,-1.2 0.1,-2.2 0.1,-3.3 0,-2.4 0,-4.8 0,-7.2 V 38 H 70 c 7.3,0 14.7,0 22,0 h 0.5 v 16.5 c 0,0.3 0.2,0.7 0.5,0.8 0.3,0.1 0.7,0.2 1,0 L 106.8,48 c 0.8,-0.5 1.9,-0.2 2.3,0.6 l 6.8,11.9 c 1.2,2 2.3,4 3.5,6.1 l 0.3,0.6 -14.3,8.2 c -0.3,0.2 -0.4,0.5 -0.4,0.8 0,0.3 0.2,0.6 0.5,0.8 l 12.8,7.4 c 0.8,0.5 1.1,1.5 0.6,2.3 l -9.9,17.1 c -0.2,0.4 -0.6,0.7 -1,0.8 -0.4,0.1 -0.9,0.1 -1.3,-0.2 L 93.2,96.6 92.9,97 c -0.4,0.4 -0.4,0.9 -0.3,1.4 v 8.4 3.7 c 0,1.3 0,2.4 -0.2,3.6 l -0.1,0.3 h -0.5 c -0.1,0 -0.2,0 -0.3,0 -7,0 -14.1,0.1 -21.2,0 z m 55.8,-4.5 -9.5,15.2 c -2.7,4.2 -5.3,8.5 -8,12.7 -0.5,0.8 -0.6,1.5 -0.3,2.5 1.5,5.3 3.1,10.5 4.6,15.8 l 1.7,5.9 h -0.5 c 0,0 -0.1,0 -0.1,0 h -0.1 l -20.2,-0.1 c -0.7,0 -1.4,0 -2.1,0 h -3.1 l 1.3,-4.1 c 1.8,-5.7 3.8,-11.6 5.7,-17.3 0.4,-1.1 0.2,-1.8 -0.5,-2.7 -3.4,-4.2 -6.7,-8.4 -10.7,-13.3 l -1.9,-2.4 h 16.2 c 0.7,0 1,0 1.2,-0.3 0.3,-0.3 0.3,-0.6 0.3,-1.4 v -11 l 0.6,0.4 c 0.2,0.1 0.3,0.2 0.5,0.3 1.6,0.9 3.1,1.8 4.7,2.7 l 3.9,2.3 c 1.1,0.6 1.4,0.5 2,-0.5 l 5.5,-9.4 c 0.1,-0.1 0.1,-0.2 0.2,-0.3 l 0.2,-0.2 0.6,0.2 c 2.4,0.9 4.7,1.8 7.1,2.6 0.5,0.2 0.9,0.6 1,1 0.1,0.4 -0.1,0.9 -0.3,1.4 z m 21.6,-2.2 -26.3,-9.8 0.3,-0.6 c 0.5,-0.9 1,-1.8 1.5,-2.7 0.1,-0.2 0.3,-0.2 0.7,-0.3 l 16.3,-3.9 c 2,-0.5 3.9,-0.9 5.9,-1.4 l 1,-0.1 1,1 V 90 c 1.5,4.1 2.9,8.1 4.4,12.2 l 3.6,8.7 z"
|
||||
id="path6"
|
||||
style="fill:#1a1a1a;fill-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.4 KiB |
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 40 40"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="mortuary.svg"
|
||||
width="40"
|
||||
height="40"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs9" /><sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.595"
|
||||
inkscape:cx="100"
|
||||
inkscape:cy="99.860918"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#1E1E1C;}
|
||||
.st1{fill:none;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
.st2{fill:#FFFFFF;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<path
|
||||
class="st0"
|
||||
d="m 35.428571,16.4 c 0,-8.085714 -6.228572,-14.714286 -14.142857,-15.371429 V 0.314286 C 21.285715,0.142857 21.142858,0 20.971429,0 H 19.314286 C 19.142858,0 19,0.142857 19,0.314286 V 1 C 10.942858,1.514286 4.5714285,8.228571 4.5714285,16.4 c 0,5.6 3,10.514286 7.4857145,13.228571 L 9.3714285,37.4 c -0.171429,0.542857 0.08571,1.114286 0.628571,1.285714 l 0.2285725,0.08571 c 0.542856,0.171428 1.114286,-0.08571 1.285714,-0.628572 l 2.6,-7.485714 c 1.428572,0.6 2.999999,0.971428 4.628571,1.114286 v 7.2 C 18.742858,39.542857 19.2,40 19.771429,40 h 0.257143 C 20.6,40 21.057143,39.542857 21.057143,38.971429 V 31.8 c 1.657143,-0.114286 3.257143,-0.485714 4.714286,-1.085714 l 2.599999,7.514285 c 0.17143,0.542858 0.771429,0.8 1.285714,0.628572 l 0.228572,-0.08571 C 30.428571,38.6 30.685715,38 30.514286,37.485714 L 27.828572,29.714286 C 32.371428,27.028571 35.428571,22.057143 35.428571,16.4 Z m -2.285714,0 c 0,0.4 -0.02857,0.8 -0.05714,1.2 -1.6,-0.142857 -3.114285,-0.542857 -4.6,-1.085714 0,0 0,-0.02857 0,-0.02857 C 28.485715,11.8 24.685715,8 20,8 c -0.942857,0 -1.885715,0.142857 -2.771429,0.457143 -1.342857,-1.428572 -2.399999,-2.771429 -3.142856,-3.8 1.771428,-0.885714 3.771428,-1.4 5.914285,-1.4 7.257143,0 13.142857,5.885714 13.142857,13.142857 z M 19.085715,10.342857 c 0.285713,-0.05714 0.6,-0.05714 0.914285,-0.05714 3.085715,0 5.628571,2.257143 6.114285,5.2 -2.657142,-1.4 -5.057142,-3.257143 -7.02857,-5.142857 z M 12.114286,5.885714 C 13.228572,7.4 15.057143,9.685714 17.457143,12 c 4.857143,4.628571 10.085715,7.342857 15.199999,7.885714 -0.314285,1.142857 -0.771428,2.2 -1.371427,3.2 -11.2,-0.2 -19.742857,-11.971428 -21.6285725,-14.8 C 10.371429,7.371429 11.2,6.571429 12.114286,5.885714 Z m 1.914285,11.2 c 2.371428,2.285715 4.828572,4.114286 7.314286,5.485715 -0.428572,0.08571 -0.885714,0.142857 -1.342857,0.142857 -3.314285,0 -6,-2.6 -6.200001,-5.857143 0.08572,0.08571 0.142858,0.142857 0.228572,0.228571 z m 5.057144,12.428572 C 12.4,29.057143 7.0571425,23.485714 6.8571425,16.8 c -0.05714,-2.314286 0.457143,-4.485714 1.457143,-6.4 0.742857,1.057143 1.8857145,2.6 3.4000005,4.285714 -0.142857,0.6 -0.2,1.228572 -0.2,1.828572 C 11.514286,21.2 15.314285,25 20,25 c 1.485714,0 2.914285,-0.371429 4.171428,-1.085714 1.828571,0.714285 3.685715,1.2 5.514286,1.371428 -2.571429,2.828572 -6.4,4.514286 -10.599999,4.228572 z"
|
||||
id="path4"
|
||||
style="stroke-width:0.285714;fill:#1a1a1a;fill-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1,79 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="40mm"
|
||||
height="40mm"
|
||||
viewBox="0 0 40 40"
|
||||
version="1.1"
|
||||
id="svg1600"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="pets.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs1594" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.0684205"
|
||||
inkscape:cx="49.699837"
|
||||
inkscape:cy="46.277881"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:pagecheckerboard="0" />
|
||||
<metadata
|
||||
id="metadata1597">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g885"
|
||||
style="fill:#1a1a1a;fill-opacity:1">
|
||||
<path
|
||||
id="path8"
|
||||
style="fill:#1a1a1a;stroke-width:0.061654;fill-opacity:1"
|
||||
d="m 19.703707,14.502266 v 0.0062 c -3.970522,0.06782 -7.694429,1.566152 -10.5120202,4.211112 -2.8854103,2.706613 -4.4694945,6.301341 -4.4694945,10.117727 v 0.301792 h 0.00568 c 0.1294733,4.451422 3.6995645,8.002625 9.5875287,9.519317 1.886615,0.487066 3.964124,0.746207 6.017203,0.746207 h 0.0925 c 2.108569,-0.01233 4.204824,-0.283607 6.060615,-0.801502 5.622848,-1.559849 8.829214,-5.074293 8.792223,-9.649024 -0.03699,-3.890372 -1.646645,-7.52801 -4.538221,-10.25312 -2.873078,-2.706614 -6.689241,-4.19871 -10.739912,-4.19871 z m 0.191202,2.306319 h 0.0987 c 3.471124,0 6.726416,1.270111 9.167917,3.569807 2.435336,2.293533 3.791643,5.345174 3.816302,8.600509 0.02466,3.452627 -2.564741,6.159413 -7.114809,7.423319 -1.670828,0.462407 -3.551369,0.709001 -5.450314,0.715203 h -0.0739 c -1.868119,0 -3.748181,-0.234566 -5.455998,-0.672311 C 9.9258243,35.168873 7.0587142,32.450255 7.0093913,28.929807 v -0.08061 c 0,-3.175187 1.3317326,-6.177643 3.7424067,-8.446514 2.42917,-2.281201 5.653491,-3.557105 9.143111,-3.594095 z" />
|
||||
<path
|
||||
id="path26"
|
||||
style="fill:#1a1a1a;stroke-width:0.061654;fill-opacity:1"
|
||||
d="M 26.238191,0.59665452 C 25.201227,0.56851579 24.157001,1.0061208 23.235274,1.8627271 22.224144,2.8060343 21.46583,4.1686855 21.102071,5.7100374 20.738312,7.251389 20.799933,8.8113797 21.27467,10.09995 c 0.536391,1.455036 1.529238,2.422794 2.799312,2.724899 0.265113,0.06165 0.536424,0.0925 0.807703,0.0925 1.004962,1e-6 2.015954,-0.437841 2.916101,-1.264005 1.011129,-0.943308 1.769443,-2.3059584 2.133203,-3.8473105 C 30.294749,6.2646819 30.233125,4.7052079 29.758391,3.4166373 29.215834,1.9616013 28.223466,0.99332729 26.953392,0.69122239 26.71641,0.63457769 26.477491,0.60314808 26.238191,0.59665452 Z m -0.0863,2.29546728 c 0.09248,-3e-7 0.18495,0.01251 0.2651,0.031006 0.499396,0.1171429 0.912079,0.573587 1.177189,1.2826089 0.320601,0.875488 0.357614,1.9664889 0.0925,3.070097 -0.258947,1.103608 -0.782698,2.0593724 -1.460893,2.6944092 -0.554886,0.5117281 -1.134612,0.7336421 -1.634009,0.6164991 -0.493233,-0.117142 -0.906394,-0.57307 -1.171504,-1.2820916 -0.3206,-0.875488 -0.357614,-1.9670056 -0.0925,-3.0706136 0.258947,-1.1036078 0.783213,-2.0593725 1.461409,-2.6944092 0.456238,-0.425413 0.931128,-0.6475055 1.362708,-0.6475058 z" />
|
||||
<path
|
||||
id="path32"
|
||||
style="fill:#1a1a1a;stroke-width:0.061654;fill-opacity:1"
|
||||
d="m 36.206575,6.0790071 c -0.733356,0.010554 -1.501195,0.2351881 -2.257226,0.6686931 -1.196092,0.6905254 -2.23805,1.8499745 -2.940905,3.2680178 -0.709025,1.42421 -0.998387,2.95298 -0.819589,4.3217 0.197294,1.535186 0.949338,2.700355 2.1146,3.279903 0.493231,0.240451 1.02331,0.363802 1.578197,0.363802 0.746014,0 1.535129,-0.221683 2.305802,-0.665593 1.196091,-0.690526 2.238049,-1.849974 2.940907,-3.268017 0.70902,-1.424209 0.998902,-2.952979 0.820105,-4.3217002 C 39.751174,8.190627 38.999128,7.0254581 37.833866,6.4459098 37.32676,6.1923575 36.776964,6.0707992 36.206575,6.0790071 Z m 0.04857,2.2965007 c 0.203459,0 0.394742,0.043388 0.561208,0.1297078 0.456239,0.22812 0.758661,0.7645595 0.85731,1.5229044 0.117145,0.924811 -0.09285,1.99131 -0.598413,3.008602 -0.50556,1.017292 -1.226796,1.837195 -2.028298,2.299601 -0.659699,0.37609 -1.275956,0.462214 -1.732196,0.234094 -0.456239,-0.22812 -0.758671,-0.770725 -0.857311,-1.522904 -0.117144,-0.924811 0.09233,-1.99131 0.597895,-3.008602 0.505563,-1.017292 1.227315,-1.8371952 2.028817,-2.2996011 0.419248,-0.240451 0.819563,-0.3638021 1.170988,-0.3638021 z" />
|
||||
<path
|
||||
id="path26-6"
|
||||
style="fill:#1a1a1a;stroke-width:0.061654;fill-opacity:1"
|
||||
d="m 13.762467,0.59665452 c -0.239299,0.006494 -0.478218,0.0379232 -0.715202,0.0945679 -1.270074,0.3021049 -2.262958,1.27037888 -2.805513,2.72541488 -0.4747359,1.2885703 -0.5363581,2.8480446 -0.172599,4.3893962 0.363761,1.5413521 1.122075,2.9040025 2.133203,3.8473105 0.900148,0.826164 1.911656,1.264006 2.916618,1.264005 0.271279,0 0.542072,-0.03085 0.807185,-0.0925 1.270075,-0.302105 2.262922,-1.269863 2.799313,-2.724899 C 19.200209,8.8113797 19.26183,7.2513893 18.898071,5.7100374 18.534312,4.1686855 17.775998,2.8060343 16.764868,1.8627271 15.84314,1.0061207 14.799432,0.56851579 13.762467,0.59665452 Z m 0.0863,2.29546728 c 0.43158,0 0.905953,0.2220925 1.362191,0.6475055 0.678196,0.6350367 1.202462,1.5908014 1.461409,2.6944092 0.265113,1.103608 0.228099,2.1951256 -0.0925,3.0706136 C 16.314756,10.013672 15.901595,10.4696 15.408362,10.586742 14.908965,10.703885 14.329756,10.481971 13.77487,9.9702426 13.096676,9.3352058 12.572407,8.3794414 12.31346,7.2758334 c -0.265113,-1.1036081 -0.228099,-2.194609 0.0925,-3.070097 0.26511,-0.7090219 0.678309,-1.165466 1.177706,-1.2826089 0.08015,-0.018497 0.17262,-0.031006 0.2651,-0.031006 z" />
|
||||
<path
|
||||
id="path32-9"
|
||||
style="fill:#1a1a1a;stroke-width:0.061654;fill-opacity:1"
|
||||
d="M 3.7935669,6.0790071 C 3.2231793,6.0707997 2.6733809,6.1923575 2.166276,6.4459098 1.001015,7.0254579 0.24896743,8.190627 0.05167643,9.7258128 c -0.178799,1.3687212 0.11056822,2.8974912 0.81958822,4.3217002 0.70285695,1.418043 1.74481575,2.577491 2.94090565,3.268017 0.7706752,0.44391 1.5603043,0.665593 2.3063192,0.665593 0.554886,0 1.0849652,-0.123351 1.5781983,-0.363802 1.165261,-0.579548 1.9173055,-1.744717 2.1145994,-3.279903 0.1787962,-1.36872 -0.1110818,-2.89749 -0.8201048,-4.3217 C 8.2883263,8.597675 7.2463667,7.4382256 6.0502767,6.7477002 5.2942444,6.3141952 4.5269224,6.0895602 3.7935669,6.0790071 Z m -0.049093,2.2965007 c 0.351427,0 0.7522567,0.1233511 1.1715046,0.3638021 0.801502,0.4624059 1.5227369,1.2823091 2.0283001,2.2996011 0.5055639,1.017292 0.7155568,2.083791 0.5984128,3.008602 -0.09864,0.752179 -0.4010707,1.294784 -0.8573119,1.522904 C 6.229142,15.798537 5.6128851,15.712413 4.9531859,15.336323 4.1516839,14.873917 3.4299313,14.054014 2.9243692,13.036722 2.4188063,12.01943 2.20933,10.952931 2.326473,10.02812 2.4251229,9.2697751 2.727544,8.7333356 3.1837849,8.5052156 3.3502499,8.4188955 3.5410153,8.3755078 3.7444739,8.3755078 Z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 9.1 KiB |
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 40 40"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="preserved.svg"
|
||||
width="40"
|
||||
height="40"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs9" /><sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.595"
|
||||
inkscape:cx="100"
|
||||
inkscape:cy="99.860918"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#1E1E1C;}
|
||||
.st1{fill:none;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
.st2{fill:#FFFFFF;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<path
|
||||
class="st0"
|
||||
d="m 19.999996,39.999996 c 0.632411,0 1.121092,-0.517427 1.121092,-1.121092 v -8.39382 h 0.114984 c 2.874596,-0.08624 5.116781,-0.977363 6.669062,-2.644628 2.673374,-2.817104 2.443407,-6.927776 2.357169,-7.732663 v 0 c -0.05749,-0.603665 -0.546173,-1.092346 -1.149839,-1.149838 -0.287459,-0.02875 -1.322314,-0.114984 -2.644628,0.08624 l -0.488681,0.08624 0.431189,-0.258714 c 0.546174,-0.344951 1.034855,-0.747395 1.466044,-1.20733 2.673374,-2.817104 2.443407,-6.927776 2.357169,-7.732663 C 30.176067,9.328063 29.687384,8.839382 29.083719,8.78189 28.537545,8.7244 26.58282,8.609414 24.484365,9.356809 L 24.31189,9.414299 24.34064,9.213077 C 24.455623,8.609412 24.513115,7.977001 24.513115,7.373336 24.398131,3.492632 21.408552,0.790512 20.833633,0.301831 c -0.488682,-0.402444 -1.149839,-0.402444 -1.63852,0 -0.603665,0.459935 -3.593245,3.162055 -3.708228,7.042759 -0.02875,0.632411 0.02875,1.236076 0.172475,1.868487 l 0.02875,0.201222 -0.172475,-0.05749 C 13.41718,8.609414 11.433709,8.724398 10.887535,8.78189 10.28387,8.83938 9.7951894,9.328063 9.7376974,9.931728 c -0.08624,0.804887 -0.316205,4.915559 2.3571686,7.732663 0.431189,0.431189 0.891124,0.833633 1.437297,1.178584 l 0.402444,0.258714 -0.488681,-0.08624 c -0.632411,-0.08624 -1.178585,-0.114984 -1.609774,-0.114984 -0.431189,0 -0.747395,0.02875 -0.919871,0.02875 -0.603665,0.05749 -1.0923456,0.546173 -1.1498376,1.149838 -0.08624,0.804887 -0.316206,4.886813 2.3571686,7.732663 1.552281,1.63852 3.794466,2.529644 6.640316,2.644628 h 0.114984 v 8.39382 c 0,0.632411 0.488681,1.149838 1.121092,1.149838 z M 27.93388,21.171394 h 0.114984 v 0.114984 c -0.02875,1.20733 -0.28746,3.420769 -1.782249,5.001796 -1.121093,1.20733 -2.788358,1.839742 -4.973051,1.954725 H 21.17858 v -0.114983 c 0.08624,-2.299677 0.776141,-4.053181 2.012217,-5.203019 1.523536,-1.49479 3.621991,-1.753503 4.743083,-1.753503 z M 27.87639,10.966579 h 0.114984 v 0.114984 c -0.02875,1.20733 -0.287459,3.420769 -1.782249,5.001796 -1.121093,1.178585 -2.788358,1.839742 -4.973051,1.925979 H 21.12109 v -0.114983 c 0.08624,-2.299677 0.776141,-4.05318 2.012217,-5.203019 1.552282,-1.437298 3.650737,-1.696011 4.743083,-1.724757 z M 17.757811,7.40208 c 0.05749,-2.155947 1.379806,-3.880704 2.184693,-4.714337 l 0.08624,-0.08624 0.08624,0.08624 c 0.776141,0.833633 2.098455,2.55839 2.184693,4.714337 0.05749,1.696012 -0.689903,3.392023 -2.155947,5.030543 l -0.08624,0.08624 -0.08624,-0.08624 C 18.418968,10.794103 17.700319,9.098092 17.757811,7.40208 Z m -4.024434,8.681279 c -1.466044,-1.552281 -1.753503,-3.794466 -1.782249,-5.001796 v -0.114984 h 0.114984 c 1.35106,0.02875 3.277039,0.344951 4.743083,1.724757 v 0 c 1.236076,1.149839 1.897233,2.903342 2.012217,5.203019 v 0.114983 h -0.114984 c -2.184693,-0.08624 -3.851958,-0.747394 -4.973051,-1.925979 z m 5.001797,12.130794 c -2.184693,-0.08624 -3.851959,-0.747395 -4.973051,-1.925979 -1.466044,-1.552281 -1.753503,-3.794466 -1.782249,-5.001796 v -0.114984 h 0.114984 c 1.121092,0.02875 3.219547,0.287459 4.743083,1.724757 1.236076,1.149839 1.897233,2.903342 2.012217,5.203019 v 0.114983 z M 18.418968,21.228886 C 18.074017,20.91268 17.700319,20.625221 17.26913,20.337761 l -0.431189,-0.258714 0.488681,0.05749 c 0.833633,0.114984 1.638519,0.14373 2.500898,0.114984 0.02875,0 0.05749,0 0.08624,0 h 0.05749 0.05749 c 0.02875,0 0.05749,0 0.08624,0 0.201221,0 0.373697,0 0.574919,0 0.689903,0 1.379806,-0.05749 2.040963,-0.14373 l 0.517427,-0.08624 -0.431189,0.28746 c -0.43119,0.258714 -0.804887,0.574919 -1.149839,0.891125 -0.574919,0.546173 -1.092346,1.236076 -1.523535,2.012217 l -0.08624,0.172476 -0.08624,-0.172476 c -0.459931,-0.747391 -0.977359,-1.437294 -1.552278,-1.983467 z"
|
||||
id="path4"
|
||||
style="stroke-width:0.28746;fill-opacity:1;fill:#1a1a1a" />
|
||||
</svg>
|
After Width: | Height: | Size: 5.0 KiB |
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 40 40"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="treatments.svg"
|
||||
width="40"
|
||||
height="40"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs13" /><sodipodi:namedview
|
||||
id="namedview11"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.595"
|
||||
inkscape:cx="100"
|
||||
inkscape:cy="99.860918"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#1E1E1C;}
|
||||
.st1{fill:none;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
.st2{fill:#FFFFFF;stroke:#1E1E1C;stroke-width:8;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<g
|
||||
id="g8"
|
||||
transform="matrix(0.28715004,0,0,0.28715004,-9.3036616,-8.6719315)"
|
||||
style="fill:#1a1a1a;fill-opacity:1">
|
||||
<path
|
||||
class="st0"
|
||||
d="m 88.4,48.2 c -10.7,3.1 -16.5,10.9 -16.5,22 v 99.3 h 60.3 c 0,-0.1 0,-0.2 0,-0.3 0,-8.8 0,-17.6 0,-26.4 0,-24.2 0,-49.3 -0.1,-74.1 0,-7.7 -3.7,-13.9 -10.6,-18.1 v 0 c -1.3,-0.8 -2.8,-1.4 -4.4,-2 H 117 c -0.7,-0.3 -1.4,-0.6 -2.2,-0.9 l -0.2,-0.1 V 30.2 H 89.3 V 47.8 L 88.8,48 c -0.2,0.1 -0.3,0.2 -0.4,0.2 z m 35.7,113.4 H 79.9 V 73.2 h 44.2 z M 97.4,38.2 h 9.3 v 9.4 h -9.3 z m -4.3,17.4 h 3.4 c 4.8,0 9.7,-0.1 14.5,0 5.2,0.1 9.2,2.6 11.8,7.4 0.2,0.3 0.3,0.7 0.5,1 v 0.1 c 0.1,0.2 0,0.3 0,0.4 0,0 0,0.1 0,0.1 v 0.3 H 80.5 l 0.2,-0.5 c 0.7,-2.2 2,-4.1 3.9,-5.7 2.5,-2 5.4,-3.1 8.5,-3.1 z"
|
||||
id="path4"
|
||||
style="fill:#1a1a1a;fill-opacity:1" />
|
||||
<path
|
||||
class="st0"
|
||||
d="m 114.7,130.7 c 0.1,-3.1 0,-6.2 0,-9.3 v -1 c 0,-1.1 0,-2.2 0,-3.2 v -0.1 c 0,-1.1 0,-2.3 0,-3.4 v -0.2 c 0,-3 0,-6.1 0,-9.1 0,-1.4 -0.2,-2.6 -0.4,-3.6 -1.4,-5.5 -6.5,-9.5 -12.2,-9.5 -0.5,0 -1,0 -1.4,0.1 -6.5,0.8 -11.3,6.1 -11.4,12.5 -0.1,8.4 0,17.1 0,24.8 v 1.9 c 0,0.9 0.1,1.9 0.4,2.9 1.5,6.1 7.3,10.2 13.5,9.6 6.5,-0.7 11.4,-6 11.5,-12.4 z m -7.9,-9.7 c 0,3 0,6 0,9 0,3 -1.9,5.2 -4.7,5.2 -1.3,0 -2.4,-0.5 -3.3,-1.3 -1,-0.9 -1.5,-2.3 -1.5,-3.8 0,-8.5 0,-17.2 0,-25.7 0,-1.5 0.5,-2.9 1.5,-3.8 0.9,-0.8 2,-1.3 3.3,-1.3 2.8,0 4.7,2.2 4.7,5.2 0,3 0,6 0,9 v 3.8 z"
|
||||
id="path6"
|
||||
style="fill:#1a1a1a;fill-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 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 |
|
@ -0,0 +1,131 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
|
||||
<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="0.92.4 (5da689c313, 2019-01-14)"
|
||||
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" /><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="1013"
|
||||
id="namedview57"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.1818851"
|
||||
inkscape:cx="89.825797"
|
||||
inkscape:cy="26.457641"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="30"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" /><g
|
||||
id="Background" /><g
|
||||
id="Guides" /><g
|
||||
id="g864"><g
|
||||
style="fill:#ffffff"
|
||||
id="g9"><path
|
||||
style="fill-rule:evenodd;fill:#ffffff"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path11"
|
||||
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 h -7.254 z"
|
||||
clip-rule="evenodd" /></g><g
|
||||
style="fill:#8ed300;fill-opacity:1"
|
||||
id="g13"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
id="path15"
|
||||
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 l 0.72,-2.573 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 l -0.235,0.842 z"
|
||||
clip-rule="evenodd" /></g><g
|
||||
style="fill:#8ed300;fill-opacity:1"
|
||||
id="g17"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
id="path19"
|
||||
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"
|
||||
clip-rule="evenodd" /></g><g
|
||||
style="fill:#8ed300;fill-opacity:1"
|
||||
id="g21"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
id="path23"
|
||||
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 l 0.719,-2.573 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 l -0.236,0.842 z"
|
||||
clip-rule="evenodd" /></g><g
|
||||
style="fill:#8ed300;fill-opacity:1"
|
||||
id="g25"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
id="path27"
|
||||
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 h 7.338 z"
|
||||
clip-rule="evenodd" /></g><g
|
||||
style="fill:#8ed300;fill-opacity:1"
|
||||
id="g29"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
id="path31"
|
||||
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 h -2.994 z"
|
||||
clip-rule="evenodd" /></g><g
|
||||
style="fill:#ffffff"
|
||||
id="g33"><path
|
||||
style="fill:#ffffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path35"
|
||||
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 h 7.296 z"
|
||||
clip-rule="evenodd" /><g
|
||||
style="fill:#ffffff"
|
||||
id="g37"><path
|
||||
style="fill-rule:evenodd;fill:#ffffff"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path39"
|
||||
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 h 7.296 z"
|
||||
clip-rule="evenodd" /></g></g><g
|
||||
style="fill:#ffffff"
|
||||
id="g41"><path
|
||||
style="fill-rule:evenodd;fill:#ffffff"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path43"
|
||||
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"
|
||||
clip-rule="evenodd" /></g><g
|
||||
style="fill:#ffffff"
|
||||
id="g45"><path
|
||||
style="fill:#ffffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path47"
|
||||
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"
|
||||
clip-rule="evenodd" /><g
|
||||
style="fill:#ffffff"
|
||||
id="g49"><path
|
||||
style="fill-rule:evenodd;fill:#ffffff"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path51"
|
||||
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"
|
||||
clip-rule="evenodd" /></g></g><g
|
||||
style="fill:#8ed300;fill-opacity:1"
|
||||
id="g53"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#8ed300;fill-opacity:1"
|
||||
id="path55"
|
||||
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 h -7.254 z" /></g></g></svg>
|
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,141 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
|
||||
<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="0.92.4 (5da689c313, 2019-01-14)"
|
||||
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="1013"
|
||||
id="namedview57"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.1818851"
|
||||
inkscape:cx="102.98898"
|
||||
inkscape:cy="24.064335"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="30"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<g
|
||||
id="Background">
|
||||
</g>
|
||||
<g
|
||||
id="Guides">
|
||||
</g>
|
||||
<g
|
||||
id="g916"><path
|
||||
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"
|
||||
inkscape:connector-curvature="0"
|
||||
style="clip-rule:evenodd;fill-rule:evenodd" /><path
|
||||
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="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
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="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
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="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
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="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
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="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
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"
|
||||
inkscape:connector-curvature="0"
|
||||
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd" /><path
|
||||
style="clip-rule:evenodd;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path39"
|
||||
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" /><path
|
||||
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"
|
||||
inkscape:connector-curvature="0"
|
||||
style="clip-rule:evenodd;fill-rule:evenodd" /><path
|
||||
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"
|
||||
inkscape:connector-curvature="0"
|
||||
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd" /><path
|
||||
style="clip-rule:evenodd;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path51"
|
||||
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" /><path
|
||||
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:#8ed300;fill-opacity:1"
|
||||
inkscape:connector-curvature="0" /></g>
|
||||
</svg>
|
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,256 @@
|
|||
/* eslint-env node */
|
||||
|
||||
/*
|
||||
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
|
||||
* the ES6 features that are supported by your Node version. https://node.green/
|
||||
*/
|
||||
|
||||
// Configuration for your app
|
||||
// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js
|
||||
|
||||
const ESLintPlugin = require('eslint-webpack-plugin');
|
||||
const path = require('path');
|
||||
|
||||
const { configure } = require('quasar/wrappers');
|
||||
|
||||
module.exports = configure(function (ctx) {
|
||||
return {
|
||||
// https://v2.quasar.dev/quasar-cli-webpack/supporting-ts
|
||||
supportTS: false,
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-webpack/prefetch-feature
|
||||
// preFetch: true,
|
||||
|
||||
// app boot file (/src/boot)
|
||||
// --> boot files are part of "main.js"
|
||||
// https://v2.quasar.dev/quasar-cli-webpack/boot-files
|
||||
boot: ['i18n', 'axios', 'vnDate', 'error-handler', 'app'],
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-css
|
||||
css: ['app.scss', 'width.scss', 'responsive.scss'],
|
||||
|
||||
// https://github.com/quasarframework/quasar/tree/dev/extras
|
||||
extras: [
|
||||
// 'ionicons-v4',
|
||||
// 'mdi-v5',
|
||||
// 'fontawesome-v6',
|
||||
// 'eva-icons',
|
||||
// 'themify',
|
||||
// 'line-awesome',
|
||||
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
|
||||
|
||||
'roboto-font', // optional, you are not bound to it
|
||||
'material-icons' // optional, you are not bound to it
|
||||
],
|
||||
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-build
|
||||
build: {
|
||||
vueRouterMode: 'hash', // available values: 'hash', 'history'
|
||||
|
||||
// transpile: false,
|
||||
// publicPath: '/',
|
||||
|
||||
// Add dependencies for transpiling with Babel (Array of string/regex)
|
||||
// (from node_modules, which are by default not transpiled).
|
||||
// Applies only if "transpile" is set to true.
|
||||
// transpileDependencies: [],
|
||||
|
||||
// rtl: true, // https://quasar.dev/options/rtl-support
|
||||
// preloadChunks: true,
|
||||
// showProgress: false,
|
||||
// gzip: true,
|
||||
// analyze: true,
|
||||
|
||||
// Options below are automatically set depending on the env, set them if you want to override
|
||||
// extractCSS: false,
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-webpack/handling-webpack
|
||||
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
|
||||
|
||||
chainWebpack(chain) {
|
||||
chain
|
||||
.plugin('eslint-webpack-plugin')
|
||||
.use(ESLintPlugin, [{ extensions: ['js', 'vue'] }]);
|
||||
|
||||
chain.module
|
||||
.rule('i18n-resource')
|
||||
.test(/\.(json5?|ya?ml)$/)
|
||||
.include.add(path.resolve(__dirname, './src/i18n'))
|
||||
.end()
|
||||
.type('javascript/auto')
|
||||
.use('i18n-resource')
|
||||
.loader('@intlify/vue-i18n-loader');
|
||||
chain.module
|
||||
.rule('i18n')
|
||||
.resourceQuery(/blockType=i18n/)
|
||||
.type('javascript/auto')
|
||||
.use('i18n')
|
||||
.loader('@intlify/vue-i18n-loader');
|
||||
}
|
||||
},
|
||||
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-devServer
|
||||
devServer: {
|
||||
server: {
|
||||
type: 'http'
|
||||
},
|
||||
port: 8080,
|
||||
open: false,
|
||||
// static: __dirname,
|
||||
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||
// stats: { chunks: false },
|
||||
proxy: {
|
||||
'/api': 'http://localhost:3000',
|
||||
'/': {
|
||||
target: 'http://localhost:3002',
|
||||
bypass: req => (req.path !== '/' ? req.path : null)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-framework
|
||||
framework: {
|
||||
config: {},
|
||||
autoImportComponentCase: 'pascal',
|
||||
// iconSet: 'material-icons', // Quasar icon set
|
||||
// lang: 'en-US', // Quasar language pack
|
||||
|
||||
// For special cases outside of where the auto-import strategy can have an impact
|
||||
// (like functional components as one of the examples),
|
||||
// you can manually specify Quasar components/directives to be available everywhere:
|
||||
//
|
||||
// components: [],
|
||||
// directives: [],
|
||||
|
||||
// Quasar plugins
|
||||
plugins: ['Notify', 'Dialog']
|
||||
},
|
||||
|
||||
// animations: 'all', // --- includes all animations
|
||||
// https://quasar.dev/options/animations
|
||||
animations: [],
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-webpack/developing-ssr/configuring-ssr
|
||||
ssr: {
|
||||
pwa: false,
|
||||
|
||||
// manualStoreHydration: true,
|
||||
// manualPostHydrationTrigger: true,
|
||||
|
||||
prodPort: 3000, // The default port that the production server should use
|
||||
// (gets superseded if process.env.PORT is specified at runtime)
|
||||
|
||||
maxAge: 1000 * 60 * 60 * 24 * 30,
|
||||
// Tell browser when a file from the server should expire from cache (in ms)
|
||||
|
||||
chainWebpackWebserver(chain) {
|
||||
chain
|
||||
.plugin('eslint-webpack-plugin')
|
||||
.use(ESLintPlugin, [{ extensions: ['js'] }]);
|
||||
},
|
||||
|
||||
middlewares: [
|
||||
ctx.prod ? 'compression' : '',
|
||||
'render' // keep this as last one
|
||||
]
|
||||
},
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-webpack/developing-pwa/configuring-pwa
|
||||
pwa: {
|
||||
workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest'
|
||||
workboxOptions: {}, // only for GenerateSW
|
||||
|
||||
// for the custom service worker ONLY (/src-pwa/custom-service-worker.[js|ts])
|
||||
// if using workbox in InjectManifest mode
|
||||
|
||||
chainWebpackCustomSW(chain) {
|
||||
chain
|
||||
.plugin('eslint-webpack-plugin')
|
||||
.use(ESLintPlugin, [{ extensions: ['js'] }]);
|
||||
},
|
||||
|
||||
manifest: {
|
||||
name: 'Hedera',
|
||||
short_name: 'Hedera',
|
||||
description: "Verdnatura's webshop",
|
||||
display: 'standalone',
|
||||
orientation: 'portrait',
|
||||
background_color: '#ffffff',
|
||||
theme_color: '#027be3',
|
||||
icons: [
|
||||
{
|
||||
src: 'icons/icon-128x128.png',
|
||||
sizes: '128x128',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: 'icons/icon-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: 'icons/icon-256x256.png',
|
||||
sizes: '256x256',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: 'icons/icon-384x384.png',
|
||||
sizes: '384x384',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: 'icons/icon-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-cordova-apps/configuring-cordova
|
||||
cordova: {
|
||||
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
|
||||
},
|
||||
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-capacitor-apps/configuring-capacitor
|
||||
capacitor: {
|
||||
hideSplashscreen: true
|
||||
},
|
||||
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-electron-apps/configuring-electron
|
||||
electron: {
|
||||
bundler: 'packager', // 'packager' or 'builder'
|
||||
|
||||
packager: {
|
||||
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
|
||||
// OS X / Mac App Store
|
||||
// appBundleId: '',
|
||||
// appCategoryType: '',
|
||||
// osxSign: '',
|
||||
// protocol: 'myapp://path',
|
||||
// Windows only
|
||||
// win32metadata: { ... }
|
||||
},
|
||||
|
||||
builder: {
|
||||
// https://www.electron.build/configuration/configuration
|
||||
|
||||
appId: 'hedera-web'
|
||||
},
|
||||
|
||||
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
|
||||
|
||||
chainWebpackMain(chain) {
|
||||
chain
|
||||
.plugin('eslint-webpack-plugin')
|
||||
.use(ESLintPlugin, [{ extensions: ['js'] }]);
|
||||
},
|
||||
|
||||
chainWebpackPreload(chain) {
|
||||
chain
|
||||
.plugin('eslint-webpack-plugin')
|
||||
.use(ESLintPlugin, [{ extensions: ['js'] }]);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
<script setup>
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { useUserStore } from 'stores/user';
|
||||
import { onBeforeMount } from 'vue';
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await userStore.init();
|
||||
await appStore.init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
|
@ -0,0 +1,15 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 356 360">
|
||||
<path
|
||||
d="M43.4 303.4c0 3.8-2.3 6.3-7.1 6.3h-15v-22h14.4c4.3 0 6.2 2.2 6.2 5.2 0 2.6-1.5 4.4-3.4 5 2.8.4 4.9 2.5 4.9 5.5zm-8-13H24.1v6.9H35c2.1 0 4-1.3 4-3.8 0-2.2-1.3-3.1-3.7-3.1zm5.1 12.6c0-2.3-1.8-3.7-4-3.7H24.2v7.7h11.7c3.4 0 4.6-1.8 4.6-4zm36.3 4v2.7H56v-22h20.6v2.7H58.9v6.8h14.6v2.3H58.9v7.5h17.9zm23-5.8v8.5H97v-8.5l-11-13.4h3.4l8.9 11 8.8-11h3.4l-10.8 13.4zm19.1-1.8V298c0-7.9 5.2-10.7 12.7-10.7 7.5 0 13 2.8 13 10.7v1.4c0 7.9-5.5 10.8-13 10.8s-12.7-3-12.7-10.8zm22.7 0V298c0-5.7-3.9-8-10-8-6 0-9.8 2.3-9.8 8v1.4c0 5.8 3.8 8.1 9.8 8.1 6 0 10-2.3 10-8.1zm37.2-11.6v21.9h-2.9l-15.8-17.9v17.9h-2.8v-22h3l15.6 18v-18h2.9zm37.9 10.2v1.3c0 7.8-5.2 10.4-12.4 10.4H193v-22h11.2c7.2 0 12.4 2.8 12.4 10.3zm-3 0c0-5.3-3.3-7.6-9.4-7.6h-8.4V307h8.4c6 0 9.5-2 9.5-7.7V298zm50.8-7.6h-9.7v19.3h-3v-19.3h-9.7v-2.6h22.4v2.6zm34.4-2.6v21.9h-3v-10.1h-16.8v10h-2.8v-21.8h2.8v9.2H296v-9.2h2.9zm34.9 19.2v2.7h-20.7v-22h20.6v2.7H316v6.8h14.5v2.3H316v7.5h17.8zM24 340.2v7.3h13.9v2.4h-14v9.6H21v-22h20v2.7H24zm41.5 11.4h-9.8v7.9H53v-22h13.3c5.1 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6H66c3.1 0 5.3-1.5 5.3-4.7 0-3.3-2.2-4.1-5.3-4.1H55.7v8.8zm47.9 6.2H89l-2 4.3h-3.2l10.7-22.2H98l10.7 22.2h-3.2l-2-4.3zm-1-2.3l-6.3-13-6 13h12.2zm46.3-15.3v21.9H146v-17.2L135.7 358h-2.1l-10.2-15.6v17h-2.8v-21.8h3l11 16.9 11.3-17h3zm35 19.3v2.6h-20.7v-22h20.6v2.7H166v6.8h14.5v2.3H166v7.6h17.8zm47-19.3l-8.3 22h-3l-7.1-18.6-7 18.6h-3l-8.2-22h3.3L204 356l6.8-18.5h3.4L221 356l6.6-18.5h3.3zm10 11.6v-1.4c0-7.8 5.2-10.7 12.7-10.7 7.6 0 13 2.9 13 10.7v1.4c0 7.9-5.4 10.8-13 10.8-7.5 0-12.7-3-12.7-10.8zm22.8 0v-1.4c0-5.7-4-8-10-8s-9.9 2.3-9.9 8v1.4c0 5.8 3.8 8.2 9.8 8.2 6.1 0 10-2.4 10-8.2zm28.3 2.4h-9.8v7.9h-2.8v-22h13.2c5.2 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6h10.2c3 0 5.2-1.5 5.2-4.7 0-3.3-2.1-4.1-5.2-4.1h-10.2v8.8zm40.3-1.5l-6.8 5.6v6.4h-2.9v-22h2.9v12.3l15.2-12.2h3.7l-9.9 8.1 10.3 13.8h-3.6l-8.9-12z" />
|
||||
<path fill="#050A14"
|
||||
d="M188.4 71.7a10.4 10.4 0 01-20.8 0 10.4 10.4 0 1120.8 0zM224.2 45c-2.2-3.9-5-7.5-8.2-10.7l-12 7c-3.7-3.2-8-5.7-12.6-7.3a49.4 49.4 0 00-9.7 13.9 59 59 0 0140.1 14l7.6-4.4a57 57 0 00-5.2-12.5zM178 125.1c4.5 0 9-.6 13.4-1.7v-14a40 40 0 0012.5-7.2 47.7 47.7 0 00-7.1-15.3 59 59 0 01-32.2 27.7v8.7c4.4 1.2 8.9 1.8 13.4 1.8zM131.8 45c-2.3 4-4 8.1-5.2 12.5l12 7a40 40 0 000 14.4c5.7 1.5 11.3 2 16.9 1.5a59 59 0 01-8-41.7l-7.5-4.3c-3.2 3.2-6 6.7-8.2 10.6z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M224.2 98.4c2.3-3.9 4-8 5.2-12.4l-12-7a40 40 0 000-14.5c-5.7-1.5-11.3-2-16.9-1.5a59 59 0 018 41.7l7.5 4.4c3.2-3.2 6-6.8 8.2-10.7zm-92.4 0c2.2 4 5 7.5 8.2 10.7l12-7a40 40 0 0012.6 7.3c4-4.1 7.3-8.8 9.7-13.8a59 59 0 01-40-14l-7.7 4.4c1.2 4.3 3 8.5 5.2 12.4zm46.2-80c-4.5 0-9 .5-13.4 1.7V34a40 40 0 00-12.5 7.2c1.5 5.7 4 10.8 7.1 15.4a59 59 0 0132.2-27.7V20a53.3 53.3 0 00-13.4-1.8z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M178 9.2a62.6 62.6 0 11-.1 125.2A62.6 62.6 0 01178 9.2m0-9.2a71.7 71.7 0 100 143.5A71.7 71.7 0 00178 0z" />
|
||||
<path fill="#050A14"
|
||||
d="M96.6 212v4.3c-9.2-.8-15.4-5.8-15.4-17.8V180h4.6v18.4c0 8.6 4 12.6 10.8 13.5zm16-31.9v18.4c0 8.9-4.3 12.8-10.9 13.5v4.4c9.2-.7 15.5-5.6 15.5-18v-18.3h-4.7zM62.2 199v-2.2c0-12.7-8.8-17.4-21-17.4-12.1 0-20.7 4.7-20.7 17.4v2.2c0 12.8 8.6 17.6 20.7 17.6 1.5 0 3-.1 4.4-.3l11.8 6.2 2-3.3-8.2-4-6.4-3.1a32 32 0 01-3.6.2c-9.8 0-16-3.9-16-13.3v-2.2c0-9.3 6.2-13.1 16-13.1 9.9 0 16.3 3.8 16.3 13.1v2.2c0 5.3-2.1 8.7-5.6 10.8l4.8 2.4c3.4-2.8 5.5-7 5.5-13.2zM168 215.6h5.1L156 179.7h-4.8l17 36zM143 205l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.8-3.7H143zm133.7 10.7h5.2l-17.3-35.9h-4.8l17 36zm-25-10.7l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.7-3.7h-14.8zm73.8-2.5c6-1.2 9-5.4 9-11.4 0-8-4.5-10.9-12.9-10.9h-21.4v35.5h4.6v-31.3h16.5c5 0 8.5 1.4 8.5 6.7 0 5.2-3.5 7.7-8.5 7.7h-11.4v4.1h10.7l9.3 12.8h5.5l-9.9-13.2zm-117.4 9.9c-9.7 0-14.7-2.5-18.6-6.3l-2.2 3.8c5.1 5 11 6.7 21 6.7 1.6 0 3.1-.1 4.6-.3l-1.9-4h-3zm18.4-7c0-6.4-4.7-8.6-13.8-9.4l-10.1-1c-6.7-.7-9.3-2.2-9.3-5.6 0-2.5 1.4-4 4.6-5l-1.8-3.8c-4.7 1.4-7.5 4.2-7.5 8.9 0 5.2 3.4 8.7 13 9.6l11.3 1.2c6.4.6 8.9 2 8.9 5.4 0 2.7-2.1 4.7-6 5.8l1.8 3.9c5.3-1.6 8.9-4.7 8.9-10zm-20.3-21.9c7.9 0 13.3 1.8 18.1 5.7l1.8-3.9a30 30 0 00-19.6-5.9c-2 0-4 .1-5.7.3l1.9 4 3.5-.2z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M.5 251.9c29.6-.5 59.2-.8 88.8-1l88.7-.3 88.7.3 44.4.4 44.4.6-44.4.6-44.4.4-88.7.3-88.7-.3a7981 7981 0 01-88.8-1z" />
|
||||
<path fill="none" d="M-565.2 324H-252v15.8h-313.2z" />
|
||||
</svg>
|
After Width: | Height: | Size: 4.4 KiB |
|
@ -0,0 +1,10 @@
|
|||
import { boot } from 'quasar/wrappers';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { useUserStore } from 'stores/user';
|
||||
|
||||
export default boot(({ app }) => {
|
||||
const props = app.config.globalProperties;
|
||||
const userStore = useUserStore();
|
||||
props.$app = useAppStore();
|
||||
props.$user = userStore.user;
|
||||
});
|
|
@ -0,0 +1,67 @@
|
|||
import { boot } from 'quasar/wrappers';
|
||||
import { Connection } from '../js/db/connection';
|
||||
import { useUserStore } from 'stores/user';
|
||||
import axios from 'axios';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
|
||||
const { notify } = useNotify();
|
||||
// Be careful when using SSR for cross-request state pollution
|
||||
// due to creating a Singleton instance here;
|
||||
// If any client changes this (global) instance, it might be a
|
||||
// good idea to move this instance creation inside of the
|
||||
// "export default () => {}" function below (which runs individually
|
||||
// for each client)
|
||||
const api = axios.create({
|
||||
baseURL: `//${location.hostname}:${location.port}/api/`
|
||||
});
|
||||
const jApi = new Connection();
|
||||
|
||||
const onRequestError = error => {
|
||||
return Promise.reject(error);
|
||||
};
|
||||
|
||||
const onResponseError = error => {
|
||||
let message = error.message;
|
||||
|
||||
const response = error.response;
|
||||
const responseData = response && response.data;
|
||||
const responseError = responseData && response.data.error;
|
||||
if (responseError) {
|
||||
message = responseError.message;
|
||||
}
|
||||
|
||||
notify(message, 'negative');
|
||||
|
||||
return Promise.reject(error);
|
||||
};
|
||||
|
||||
export default boot(({ app }) => {
|
||||
const userStore = useUserStore();
|
||||
function addToken(config) {
|
||||
if (userStore.token) {
|
||||
config.headers.Authorization = userStore.token;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
api.interceptors.request.use(addToken, onRequestError);
|
||||
api.interceptors.response.use(response => response, onResponseError);
|
||||
|
||||
jApi.use(addToken);
|
||||
jApi.useErrorInterceptor(onResponseError);
|
||||
|
||||
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||
|
||||
app.config.globalProperties.$axios = axios;
|
||||
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
|
||||
// so you won't necessarily have to import axios in each vue file
|
||||
|
||||
app.config.globalProperties.$api = api;
|
||||
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
|
||||
// so you can easily perform requests against your app's API
|
||||
|
||||
app.config.globalProperties.$jApi = jApi;
|
||||
app.provide('jApi', jApi);
|
||||
app.provide('api', api);
|
||||
});
|
||||
|
||||
export { api, jApi };
|
|
@ -0,0 +1,66 @@
|
|||
export default async ({ app }) => {
|
||||
/*
|
||||
window.addEventListener('error',
|
||||
e => onWindowError(e));
|
||||
window.addEventListener('unhandledrejection',
|
||||
e => onWindowRejection(e));
|
||||
|
||||
,onWindowError(event) {
|
||||
errorHandler(event.error);
|
||||
}
|
||||
,onWindowRejection(event) {
|
||||
errorHandler(event.reason);
|
||||
}
|
||||
*/
|
||||
app.config.errorHandler = (err, vm, info) => {
|
||||
errorHandler(err, vm)
|
||||
}
|
||||
|
||||
function errorHandler (err, vm) {
|
||||
let message
|
||||
let tMessage
|
||||
let res = err.response
|
||||
|
||||
// XXX: Compatibility with old JSON service
|
||||
if (err.name === 'JsonException') {
|
||||
res = {
|
||||
status: err.statusCode,
|
||||
data: { error: { message: err.message } }
|
||||
}
|
||||
}
|
||||
|
||||
if (res) {
|
||||
const status = res.status
|
||||
|
||||
if (status >= 400 && status < 500) {
|
||||
switch (status) {
|
||||
case 401:
|
||||
tMessage = 'loginFailed'
|
||||
break
|
||||
case 403:
|
||||
tMessage = 'authenticationRequired'
|
||||
vm.$router.push('/login')
|
||||
break
|
||||
case 404:
|
||||
tMessage = 'notFound'
|
||||
break
|
||||
default:
|
||||
message = res.data.error.message
|
||||
}
|
||||
} else if (status >= 500) {
|
||||
tMessage = 'internalServerError'
|
||||
}
|
||||
} else {
|
||||
tMessage = 'somethingWentWrong'
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
if (tMessage) {
|
||||
message = vm.$t(tMessage)
|
||||
}
|
||||
vm.$q.notify({
|
||||
message,
|
||||
type: 'negative'
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import { boot } from 'quasar/wrappers';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import messages from 'src/i18n';
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: navigator.language || navigator.userLanguage,
|
||||
fallbackLocale: 'en-US',
|
||||
globalInjection: true,
|
||||
missingWarn: false,
|
||||
fallbackWarn: false,
|
||||
legacy: false,
|
||||
silentTranslationWarn: true,
|
||||
silentFallbackWarn: true,
|
||||
messages
|
||||
});
|
||||
|
||||
export default boot(({ app }) => {
|
||||
// Set i18n instance on app
|
||||
app.use(i18n);
|
||||
});
|
||||
|
||||
export { i18n };
|
|
@ -0,0 +1,20 @@
|
|||
import { boot } from 'quasar/wrappers';
|
||||
|
||||
export default boot(() => {
|
||||
Date.vnUTC = () => {
|
||||
const env = process.env.NODE_ENV;
|
||||
if (!env || env === 'development') {
|
||||
return new Date(Date.UTC(2001, 0, 1, 11));
|
||||
}
|
||||
|
||||
return new Date();
|
||||
};
|
||||
|
||||
Date.vnNew = () => {
|
||||
return new Date(Date.vnUTC());
|
||||
};
|
||||
|
||||
Date.vnNow = () => {
|
||||
return new Date(Date.vnUTC()).getTime();
|
||||
};
|
||||
});
|
|
@ -0,0 +1,274 @@
|
|||
<script setup>
|
||||
import { ref, inject, onMounted, computed, Teleport } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import {
|
||||
generateUpdateSqlQuery,
|
||||
generateInsertSqlQuery
|
||||
} from 'src/js/db/sqlService.js';
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
table: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
schema: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// Objeto que define las pks de la tabla. Usado para generar las queries sql correspondientes.
|
||||
// Debe ser definido como un objeto de pares key-value, donde la clave es el nombre de la columna de la pk.
|
||||
pks: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
createModelDefault: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
field: '',
|
||||
value: ''
|
||||
})
|
||||
},
|
||||
// Objeto que contiene la consulta SQL y los parámetros necesarios para obtener los datos iniciales del formulario.
|
||||
// `query` debe ser una cadena de texto que representa la consulta SQL.
|
||||
// `params` es un objeto que mapea los parámetros de la consulta a sus valores.
|
||||
fetchFormDataSql: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
query: '',
|
||||
params: {}
|
||||
})
|
||||
},
|
||||
// Objeto con los datos iniciales del form, si este objeto es definido, no se ejecuta la query fetchFormDataSql
|
||||
formInitialData: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
// Array de columnas que no se deben actualizar
|
||||
columnsToIgnoreUpdate: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
autoLoad: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
isEditMode: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
defaultActions: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showBottomActions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
saveFn: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
separationBetweenInputs: {
|
||||
type: String,
|
||||
default: 'xs'
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['onDataSaved', 'onDataFetched']);
|
||||
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const { notify } = useNotify();
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const loading = ref(false);
|
||||
const formData = ref({});
|
||||
const addressFormRef = ref(null);
|
||||
const modelInfo = ref(null);
|
||||
// Array de nombre de columnas de la tabla
|
||||
const tableColumns = computed(
|
||||
() => modelInfo.value?.columns.map(col => col.name) || []
|
||||
);
|
||||
// Array de nombre de columnas que fueron actualizadas y no estan en columnsToIgnoreUpdate
|
||||
const updatedColumns = computed(() => {
|
||||
return tableColumns.value.filter(
|
||||
colName =>
|
||||
modelInfo.value?.data[0][colName] !== formData.value[colName] &&
|
||||
!props.columnsToIgnoreUpdate.includes(colName)
|
||||
);
|
||||
});
|
||||
|
||||
const hasChanges = computed(() => !!updatedColumns.value.length);
|
||||
|
||||
const separationBetweenInputs = computed(() => {
|
||||
return `q-gutter-y-${props.separationBetweenInputs}`;
|
||||
});
|
||||
|
||||
const fetchFormData = async () => {
|
||||
if (!props.fetchFormDataSql.query) return;
|
||||
loading.value = true;
|
||||
const { results } = await jApi.execQuery(
|
||||
props.fetchFormDataSql.query,
|
||||
props.fetchFormDataSql.params
|
||||
);
|
||||
|
||||
modelInfo.value = results[0];
|
||||
|
||||
if (!modelInfo.value.data[0]) {
|
||||
modelInfo.value.data[0] = {};
|
||||
// Si no existen datos iniciales, se inicializan con null, en base a las columnas de la tabla
|
||||
modelInfo.value.columns.forEach(
|
||||
col => (modelInfo.value.data[0][col.name] = null)
|
||||
);
|
||||
}
|
||||
|
||||
formData.value = { ...modelInfo.value.data[0] };
|
||||
loading.value = false;
|
||||
emit('onDataFetched', formData.value);
|
||||
};
|
||||
|
||||
const onSubmitSuccess = () => {
|
||||
emit('onDataSaved');
|
||||
notify(t('dataSaved'), 'positive');
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
try {
|
||||
if (props.saveFn) {
|
||||
await props.saveFn(formData.value);
|
||||
} else {
|
||||
if (!hasChanges.value) {
|
||||
return;
|
||||
}
|
||||
const sqlQuery = generateSqlQuery();
|
||||
await jApi.execQuery(sqlQuery, props.pks);
|
||||
modelInfo.value.data[0] = { ...formData.value };
|
||||
}
|
||||
onSubmitSuccess();
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const generateSqlQuery = () => {
|
||||
if (props.isEditMode) {
|
||||
return generateUpdateSqlQuery(
|
||||
props.schema,
|
||||
props.table,
|
||||
props.pks,
|
||||
updatedColumns.value,
|
||||
formData.value
|
||||
);
|
||||
} else {
|
||||
return generateInsertSqlQuery(
|
||||
props.schema,
|
||||
props.table,
|
||||
formData.value,
|
||||
updatedColumns.value,
|
||||
props.createModelDefault
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
if (!props.formInitialData) {
|
||||
fetchFormData();
|
||||
} else {
|
||||
formData.value = { ...props.formInitialData };
|
||||
// Como no se ejecuta la query fetchFormData y no se obtienen las columnas de la tabla, se inicializan con las keys del objeto formInitialData
|
||||
modelInfo.value = {
|
||||
columns: Object.keys(props.formInitialData).map(col => ({
|
||||
name: col
|
||||
})),
|
||||
data: [props.formInitialData]
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
formData,
|
||||
submit
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QCard class="form-container" v-bind="$attrs">
|
||||
<QForm
|
||||
v-if="!loading"
|
||||
ref="addressFormRef"
|
||||
class="form"
|
||||
:class="separationBetweenInputs"
|
||||
>
|
||||
<span v-if="title" class="text-h6 text-bold">
|
||||
{{ title }}
|
||||
</span>
|
||||
<slot name="form" :data="formData" />
|
||||
<slot name="extraForm" :data="formData" />
|
||||
<component
|
||||
v-if="isHeaderMounted"
|
||||
:is="showBottomActions ? 'div' : Teleport"
|
||||
to="#actions"
|
||||
class="flex row justify-end q-gutter-x-sm"
|
||||
:class="{ 'q-mt-md': showBottomActions }"
|
||||
>
|
||||
<QBtn
|
||||
v-if="defaultActions && showBottomActions"
|
||||
:label="t('cancel')"
|
||||
:icon="showBottomActions ? undefined : 'check'"
|
||||
rounded
|
||||
no-caps
|
||||
flat
|
||||
v-close-popup
|
||||
>
|
||||
<QTooltip>{{ t('cancel') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
v-if="defaultActions"
|
||||
:label="t('save')"
|
||||
:icon="showBottomActions ? undefined : 'check'"
|
||||
rounded
|
||||
no-caps
|
||||
flat
|
||||
:disabled="!showBottomActions && !updatedColumns.length"
|
||||
@click="submit()"
|
||||
>
|
||||
<QTooltip>{{ t('save') }}</QTooltip>
|
||||
</QBtn>
|
||||
<slot name="actions" :data="formData" />
|
||||
</component>
|
||||
</QForm>
|
||||
<QSpinner v-else color="primary" size="3em" :thickness="2" />
|
||||
</QCard>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.no-form-container {
|
||||
padding: 0 !important;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
width: 100%;
|
||||
height: max-content;
|
||||
padding: 32px;
|
||||
max-width: 544px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,139 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
'update:options',
|
||||
'keyup.enter',
|
||||
'remove'
|
||||
]);
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
isOutlined: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
info: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const requiredFieldRule = val => !!val || t('globals.fieldRequired');
|
||||
const vnInputRef = ref(null);
|
||||
const value = computed({
|
||||
get() {
|
||||
return props.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value);
|
||||
}
|
||||
});
|
||||
const hover = ref(false);
|
||||
const styleAttrs = computed(() => {
|
||||
return props.isOutlined
|
||||
? { dense: true, outlined: true, rounded: true }
|
||||
: {};
|
||||
});
|
||||
|
||||
const focus = () => {
|
||||
vnInputRef.value.focus();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
focus
|
||||
});
|
||||
|
||||
const inputRules = [
|
||||
val => {
|
||||
const { min, max } = vnInputRef.value.$attrs;
|
||||
if (min >= 0) {
|
||||
if (Math.floor(val) < min) return t('inputMin', { value: min });
|
||||
}
|
||||
if (max > 0) {
|
||||
if (Math.floor(val) > max) return t('inputMax', { value: max });
|
||||
}
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||
@mouseover="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
>
|
||||
<QInput
|
||||
ref="vnInputRef"
|
||||
v-model="value"
|
||||
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||
:type="$attrs.type"
|
||||
:class="{ required: $attrs.required }"
|
||||
:clearable="false"
|
||||
:rules="inputRules"
|
||||
:lazy-rules="true"
|
||||
hide-bottom-space
|
||||
@keyup.enter="emit('keyup.enter')"
|
||||
>
|
||||
<template
|
||||
v-if="$slots.prepend"
|
||||
#prepend
|
||||
>
|
||||
<slot name="prepend" />
|
||||
</template>
|
||||
<template #append>
|
||||
<slot
|
||||
v-if="$slots.append && !$attrs.disabled"
|
||||
name="append"
|
||||
/>
|
||||
<QIcon
|
||||
v-if="hover && value && !$attrs.disabled && props.clearable"
|
||||
name="close"
|
||||
size="xs"
|
||||
@click="
|
||||
() => {
|
||||
value = null;
|
||||
emit('remove');
|
||||
}
|
||||
"
|
||||
/>
|
||||
<QIcon
|
||||
v-if="info"
|
||||
name="info"
|
||||
>
|
||||
<QTooltip max-width="350px">
|
||||
{{ info }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QInput>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
inputMin: Must be more than {value}
|
||||
inputMax: Must be less than {value}
|
||||
es-ES:
|
||||
inputMin: Debe ser mayor a {value}
|
||||
inputMax: Debe ser menor a {value}
|
||||
ca-ES:
|
||||
inputMin: Ha de ser més gran que {value}
|
||||
inputMax: Ha de ser menys que {value}
|
||||
fr-FR:
|
||||
inputMin: Doit être supérieur à {value}
|
||||
inputMax: Doit être supérieur à {value}
|
||||
pt-PT:
|
||||
inputMin: Deve ser maior que {value}
|
||||
inputMax: Deve ser maior que {value}
|
||||
</i18n>
|
|
@ -0,0 +1,174 @@
|
|||
<script setup>
|
||||
import { onMounted, watch, computed, ref } from 'vue';
|
||||
import { date } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const model = defineModel({ type: String });
|
||||
const props = defineProps({
|
||||
isOutlined: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const requiredFieldRule = val => !!val || t('globals.fieldRequired');
|
||||
|
||||
const dateFormat = 'DD/MM/YYYY';
|
||||
const isPopupOpen = ref();
|
||||
const hover = ref();
|
||||
const mask = ref();
|
||||
|
||||
onMounted(() => {
|
||||
// fix quasar bug
|
||||
mask.value = '##/##/####';
|
||||
});
|
||||
|
||||
const styleAttrs = computed(() => {
|
||||
return props.isOutlined
|
||||
? {
|
||||
dense: true,
|
||||
outlined: true,
|
||||
rounded: true
|
||||
}
|
||||
: {};
|
||||
});
|
||||
|
||||
const formattedDate = computed({
|
||||
get() {
|
||||
if (!model.value) return model.value;
|
||||
return date.formatDate(new Date(model.value), dateFormat);
|
||||
},
|
||||
set(value) {
|
||||
if (value === model.value) return;
|
||||
let newDate;
|
||||
if (value) {
|
||||
// parse input
|
||||
if (value.includes('/')) {
|
||||
if (value.length === 6) {
|
||||
value = value + new Date().getFullYear();
|
||||
}
|
||||
if (value.length >= 10) {
|
||||
if (value.at(2) === '/') {
|
||||
value = value.split('/').reverse().join('/');
|
||||
}
|
||||
value = date.formatDate(
|
||||
new Date(value).toISOString(),
|
||||
'YYYY-MM-DDTHH:mm:ss.SSSZ'
|
||||
);
|
||||
}
|
||||
}
|
||||
const [year, month, day] = value.split('-').map(e => parseInt(e));
|
||||
newDate = new Date(year, month - 1, day);
|
||||
if (model.value) {
|
||||
const orgDate =
|
||||
model.value instanceof Date
|
||||
? model.value
|
||||
: new Date(model.value);
|
||||
|
||||
newDate.setHours(
|
||||
orgDate.getHours(),
|
||||
orgDate.getMinutes(),
|
||||
orgDate.getSeconds(),
|
||||
orgDate.getMilliseconds()
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!isNaN(newDate)) model.value = newDate.toISOString();
|
||||
}
|
||||
});
|
||||
|
||||
const popupDate = computed(() =>
|
||||
model.value
|
||||
? date.formatDate(new Date(model.value), 'YYYY/MM/DD')
|
||||
: model.value
|
||||
);
|
||||
|
||||
watch(
|
||||
() => model.value,
|
||||
val => (formattedDate.value = val),
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div @mouseover="hover = true" @mouseleave="hover = false">
|
||||
<QInput
|
||||
v-model="formattedDate"
|
||||
class="vn-input-date"
|
||||
:mask="mask"
|
||||
placeholder="dd/mm/aaaa"
|
||||
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||
:class="{ required: $attrs.required }"
|
||||
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||
:clearable="false"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon
|
||||
name="close"
|
||||
size="xs"
|
||||
v-if="
|
||||
($attrs.clearable == undefined || $attrs.clearable) &&
|
||||
hover &&
|
||||
model &&
|
||||
!$attrs.disable
|
||||
"
|
||||
@click="
|
||||
model = null;
|
||||
isPopupOpen = false;
|
||||
"
|
||||
/>
|
||||
<QIcon
|
||||
name="event"
|
||||
class="cursor-pointer"
|
||||
@click="isPopupOpen = !isPopupOpen"
|
||||
:title="t('openDate')"
|
||||
/>
|
||||
</template>
|
||||
<QMenu
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
v-model="isPopupOpen"
|
||||
anchor="bottom left"
|
||||
self="top start"
|
||||
:no-focus="true"
|
||||
:no-parent-event="true"
|
||||
>
|
||||
<QDate
|
||||
v-model="popupDate"
|
||||
:landscape="true"
|
||||
:today-btn="true"
|
||||
color="accent"
|
||||
@update:model-value="
|
||||
date => {
|
||||
formattedDate = date;
|
||||
isPopupOpen = false;
|
||||
}
|
||||
"
|
||||
/>
|
||||
</QMenu>
|
||||
</QInput>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.vn-input-date.q-field--standard.q-field--readonly .q-field__control:before {
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
.vn-input-date.q-field--outlined.q-field--readonly .q-field__control:before {
|
||||
border-style: solid;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
openDate: Open date
|
||||
es-ES:
|
||||
openDate: Abrir fecha
|
||||
ca-ES:
|
||||
openDate: Obrir data
|
||||
fr-FR:
|
||||
openDate: Ouvrir la date
|
||||
pt-PT:
|
||||
openDate: Abrir data
|
||||
</i18n>
|
|
@ -0,0 +1,195 @@
|
|||
<script setup>
|
||||
import { ref, toRefs, computed, watch, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const emit = defineEmits(['update:modelValue', 'update:options']);
|
||||
|
||||
const $props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number, Object],
|
||||
default: null
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
optionLabel: {
|
||||
type: [String],
|
||||
default: 'name'
|
||||
},
|
||||
optionValue: {
|
||||
type: String,
|
||||
default: 'id'
|
||||
},
|
||||
optionFilter: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
dataQuery: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
filterOptions: {
|
||||
type: [Array],
|
||||
default: () => []
|
||||
},
|
||||
isClearable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
defaultFilter: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
where: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
sortBy: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
limit: {
|
||||
type: [Number, String],
|
||||
default: '30'
|
||||
},
|
||||
focusOnMount: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
useLike: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const requiredFieldRule = val => val ?? t('globals.fieldRequired');
|
||||
|
||||
const { optionLabel, optionValue, options } = toRefs($props);
|
||||
const myOptions = ref([]);
|
||||
const myOptionsOriginal = ref([]);
|
||||
const vnSelectRef = ref();
|
||||
const lastVal = ref();
|
||||
|
||||
const value = computed({
|
||||
get() {
|
||||
return $props.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value);
|
||||
}
|
||||
});
|
||||
|
||||
watch(options, newValue => {
|
||||
setOptions(newValue);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
setOptions(options.value);
|
||||
if ($props.focusOnMount) {
|
||||
setTimeout(() => vnSelectRef.value.showPopup(), 300);
|
||||
}
|
||||
});
|
||||
|
||||
function setOptions(data) {
|
||||
myOptions.value = JSON.parse(JSON.stringify(data));
|
||||
myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
|
||||
}
|
||||
|
||||
function filter(val, options) {
|
||||
const search = val.toString().toLowerCase();
|
||||
|
||||
if (!search) return options;
|
||||
|
||||
return options.filter(row => {
|
||||
if ($props.filterOptions.length) {
|
||||
return $props.filterOptions.some(prop => {
|
||||
const propValue = String(row[prop]).toLowerCase();
|
||||
return propValue.includes(search);
|
||||
});
|
||||
}
|
||||
|
||||
const id = row.id;
|
||||
const optionLabel = String(row[$props.optionLabel]).toLowerCase();
|
||||
|
||||
return id === search || optionLabel.includes(search);
|
||||
});
|
||||
}
|
||||
|
||||
async function filterHandler(val, update) {
|
||||
if (!val && lastVal.value === val) {
|
||||
lastVal.value = val;
|
||||
return update();
|
||||
}
|
||||
lastVal.value = val;
|
||||
|
||||
if (!$props.defaultFilter) return update();
|
||||
const newOptions = filter(val, myOptionsOriginal.value);
|
||||
update(
|
||||
() => {
|
||||
myOptions.value = newOptions;
|
||||
},
|
||||
ref => {
|
||||
if (val !== '' && ref.options.length > 0) {
|
||||
ref.setOptionIndex(-1);
|
||||
ref.moveOptionSelection(1, true);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QSelect
|
||||
v-model="value"
|
||||
:options="myOptions"
|
||||
:option-label="optionLabel"
|
||||
:option-value="optionValue"
|
||||
v-bind="$attrs"
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
@filter="filterHandler"
|
||||
hide-selected
|
||||
fill-input
|
||||
ref="vnSelectRef"
|
||||
lazy-rules
|
||||
:class="{ required: $attrs.required }"
|
||||
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||
virtual-scroll-slice-size="options.length"
|
||||
>
|
||||
<template
|
||||
v-if="isClearable"
|
||||
#append
|
||||
>
|
||||
<QIcon
|
||||
v-show="value"
|
||||
name="close"
|
||||
@click.stop="value = null"
|
||||
class="cursor-pointer"
|
||||
size="xs"
|
||||
/>
|
||||
</template>
|
||||
<template
|
||||
v-for="(_, slotName) in $slots"
|
||||
#[slotName]="slotData"
|
||||
:key="slotName"
|
||||
>
|
||||
<slot
|
||||
:name="slotName"
|
||||
v-bind="slotData ?? {}"
|
||||
:key="slotName"
|
||||
/>
|
||||
</template>
|
||||
</QSelect>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.q-field--outlined {
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,65 @@
|
|||
<script setup>
|
||||
const props = defineProps({
|
||||
clickable: { type: Boolean, default: true },
|
||||
rounded: { type: Boolean, default: false }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['click']);
|
||||
|
||||
const handleClick = () => {
|
||||
if (props.clickable) {
|
||||
emit('click');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QItem
|
||||
v-bind="$attrs"
|
||||
v-ripple="clickable"
|
||||
:clickable="clickable"
|
||||
class="full-width row items-center justify-between card bg-white"
|
||||
:class="[
|
||||
rounded ? 'default-radius' : 'no-radius',
|
||||
{ 'cursor-pointer': clickable }
|
||||
]"
|
||||
@click="handleClick()"
|
||||
>
|
||||
<div
|
||||
class="no-padding content-container"
|
||||
:class="$slots.actions ? 'col-9' : 'full-width'"
|
||||
>
|
||||
<slot name="prepend" />
|
||||
<div class="content">
|
||||
<slot name="content" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="no-padding flex column justify-center">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</QItem>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
border-bottom: 1px solid $gray-light;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
* {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.content-container {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,282 @@
|
|||
<script setup>
|
||||
import { ref, inject, onMounted, nextTick } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnForm from 'src/components/common/VnForm.vue';
|
||||
|
||||
import { useUserStore } from 'stores/user';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const props = defineProps({
|
||||
verificationToken: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['onPasswordChanged']);
|
||||
const showOldPwd = ref(false);
|
||||
const showNewPwd = ref(false);
|
||||
const showCopyPwd = ref(false);
|
||||
|
||||
const { t } = useI18n();
|
||||
const api = inject('api');
|
||||
const userStore = useUserStore();
|
||||
const { notify } = useNotify();
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const oldPasswordRef = ref(null);
|
||||
const newPasswordRef = ref(null);
|
||||
const passwordRequirementsDialogRef = ref(null);
|
||||
const vnFormRef = ref(null);
|
||||
const repeatPassword = ref('');
|
||||
const passwordRequirements = ref(null);
|
||||
|
||||
const formData = ref({
|
||||
userId: userStore?.user?.id,
|
||||
oldPassword: '',
|
||||
newPassword: ''
|
||||
});
|
||||
|
||||
const changePassword = async () => {
|
||||
if (!formData.value.newPassword || !repeatPassword.value) {
|
||||
notify(t('passwordEmpty'), 'negative');
|
||||
throw new Error('Password empty');
|
||||
}
|
||||
if (formData.value.newPassword !== repeatPassword.value) {
|
||||
notify(t('passwordsDoNotMatch'), 'negative');
|
||||
throw new Error('Passwords do not match');
|
||||
}
|
||||
if (props.verificationToken) {
|
||||
await changePasswordWithToken();
|
||||
} else {
|
||||
await changePasswordWithoutToken();
|
||||
}
|
||||
};
|
||||
|
||||
const changePasswordWithToken = async () => {
|
||||
const headers = {
|
||||
Authorization: props.verificationToken
|
||||
};
|
||||
await api.post('VnUsers/reset-password', formData.value, { headers });
|
||||
};
|
||||
|
||||
const changePasswordWithoutToken = async () => {
|
||||
await api.patch('Accounts/change-password', formData.value);
|
||||
};
|
||||
|
||||
const getPasswordRequirements = async () => {
|
||||
try {
|
||||
const { data } = await api.get('UserPasswords/findOne');
|
||||
passwordRequirements.value = data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const login = async () => {
|
||||
await userStore.login(userStore.user.name, formData.value.newPassword);
|
||||
};
|
||||
|
||||
const onPasswordChanged = async () => {
|
||||
await login();
|
||||
emit('onPasswordChanged');
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
getPasswordRequirements();
|
||||
await nextTick();
|
||||
if (props.verificationToken) {
|
||||
newPasswordRef.value.focus();
|
||||
} else {
|
||||
oldPasswordRef.value.focus();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VnForm
|
||||
ref="vnFormRef"
|
||||
:title="t('changePassword')"
|
||||
:form-initial-data="formData"
|
||||
:save-fn="changePassword"
|
||||
show-bottom-actions
|
||||
:default-actions="false"
|
||||
style="max-width: 300px"
|
||||
@on-data-saved="onPasswordChanged()"
|
||||
>
|
||||
<template #form>
|
||||
<VnInput
|
||||
v-if="!verificationToken"
|
||||
ref="oldPasswordRef"
|
||||
v-model="formData.oldPassword"
|
||||
:type="!showOldPwd ? 'password' : 'text'"
|
||||
:label="t('oldPassword')"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon
|
||||
:name="showOldPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="showOldPwd = !showOldPwd"
|
||||
/>
|
||||
</template>
|
||||
</VnInput>
|
||||
<VnInput
|
||||
ref="newPasswordRef"
|
||||
v-model="formData.newPassword"
|
||||
:type="!showNewPwd ? 'password' : 'text'"
|
||||
:label="t('newPassword')"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon
|
||||
:name="showNewPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="showNewPwd = !showNewPwd"
|
||||
/>
|
||||
</template>
|
||||
</VnInput>
|
||||
<VnInput
|
||||
v-model="repeatPassword"
|
||||
:type="!showCopyPwd ? 'password' : 'text'"
|
||||
:label="t('repeatPassword')"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon
|
||||
:name="showCopyPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="showCopyPwd = !showCopyPwd"
|
||||
/>
|
||||
</template>
|
||||
</VnInput>
|
||||
</template>
|
||||
<template v-if="isHeaderMounted" #actions>
|
||||
<QBtn
|
||||
:label="t('requirements')"
|
||||
rounded
|
||||
no-caps
|
||||
flat
|
||||
@click="passwordRequirementsDialogRef.show()"
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('modify')"
|
||||
rounded
|
||||
no-caps
|
||||
flat
|
||||
@click="vnFormRef.submit()"
|
||||
/>
|
||||
</template>
|
||||
</VnForm>
|
||||
<QDialog ref="passwordRequirementsDialogRef">
|
||||
<QCard class="q-px-md q-py-lg column items-center">
|
||||
<span class="text-h6 text-bold q-mb-md">
|
||||
{{ t('passwordRequirements') }}
|
||||
</span>
|
||||
<div class="column" style="max-width: max-content">
|
||||
<span>
|
||||
{{
|
||||
t('charactersLong', {
|
||||
length: passwordRequirements.length
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
<span>
|
||||
{{
|
||||
t('alphabeticCharacters', {
|
||||
nAlpha: passwordRequirements.nAlpha
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
<span>
|
||||
{{
|
||||
t('capitalLetters', {
|
||||
nUpper: passwordRequirements.nUpper
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
<span>
|
||||
{{ t('digits', { nDigits: passwordRequirements.nDigits }) }}
|
||||
</span>
|
||||
<span>
|
||||
{{ t('symbols', { nPunct: passwordRequirements.nPunct }) }}
|
||||
</span>
|
||||
</div>
|
||||
</QCard>
|
||||
</QDialog>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
changePassword: Change password
|
||||
newPassword: New password
|
||||
oldPassword: Old password
|
||||
repeatPassword: Repeat password
|
||||
requirements: Requirements
|
||||
passwordRequirements: Password requirements
|
||||
charactersLong: '{length} characters long'
|
||||
alphabeticCharacters: '{nAlpha} alphabetic characters'
|
||||
capitalLetters: '{nUpper} capital letters'
|
||||
digits: '{nDigits} digits'
|
||||
symbols: '{nPunct} symbols. Ej: $%&.'
|
||||
passwordsDoNotMatch: Passwords do not match
|
||||
passwordEmpty: Password empty
|
||||
es-ES:
|
||||
changePassword: Cambiar contraseña
|
||||
newPassword: Nueva contraseña
|
||||
oldPassword: Contraseña antigua
|
||||
repeatPassword: Repetir contraseña
|
||||
requirements: Requisitos
|
||||
passwordRequirements: Requisitos de contraseña
|
||||
charactersLong: '{length} caracteres de longitud'
|
||||
alphabeticCharacters: '{nAlpha} caracteres alfabéticos'
|
||||
capitalLetters: '{nUpper} letras mayúsculas'
|
||||
digits: '{nDigits} dígitos'
|
||||
symbols: '{nPunct} símbolos. Ej: $%&.'
|
||||
passwordsDoNotMatch: ¡Las contraseñas no coinciden!
|
||||
passwordEmpty: Contraseña vacía
|
||||
ca-ES:
|
||||
changePassword: Canviar contrasenya
|
||||
newPassword: Nova contrasenya
|
||||
oldPassword: Contrasenya antiga
|
||||
repeatPassword: Repetir contrasenya
|
||||
requirements: Requisits
|
||||
passwordRequirements: Requisits de contrasenya
|
||||
charactersLong: '{length} caràcters de longitud'
|
||||
alphabeticCharacters: '{nAlpha} caràcters alfabètics'
|
||||
capitalLetters: '{nUpper} lletres majúscules'
|
||||
digits: '{nDigits} dígits'
|
||||
symbols: '{nPunct} símbols. Ej: $%&.'
|
||||
passwordsDoNotMatch: Les contrasenyes no coincideixen!
|
||||
passwordEmpty: Contrasenya buida
|
||||
fr-FR:
|
||||
changePassword: Changer le mot de passe
|
||||
newPassword: Nouveau mot de passe
|
||||
oldPassword: Ancien mot de passe
|
||||
repeatPassword: Répéter le mot de passe
|
||||
requirements: Exigences
|
||||
passwordRequirements: Mot de passe exigences
|
||||
charactersLong: '{length} caractères de longueur'
|
||||
alphabeticCharacters: '{nAlpha} caractères alphabétiques'
|
||||
capitalLetters: '{nUpper} lettres majuscules'
|
||||
digits: '{nDigits} chiffres'
|
||||
symbols: '{nPunct} symboles. Ej: $%&.'
|
||||
passwordsDoNotMatch: Les mots de passe ne correspondent pas!
|
||||
passwordEmpty: Mots de passe vides
|
||||
pt-PT:
|
||||
changePassword: Alterar palavra-passe
|
||||
newPassword: Nova palavra-passe
|
||||
oldPassword: Palavra-passe antiga
|
||||
repeatPassword: Repetir palavra-passe
|
||||
requirements: Requisitos
|
||||
passwordRequirements: Requisitos de palavra-passe
|
||||
charactersLong: '{length} caracteres de comprimento'
|
||||
alphabeticCharacters: '{nAlpha} caracteres alfabéticos'
|
||||
capitalLetters: '{nUpper} letras maiúsculas'
|
||||
digits: '{nDigits} dígitos'
|
||||
symbols: '{nPunct} símbolos. Ej: $%&.'
|
||||
passwordsDoNotMatch: As palavras-passe não coincidem!
|
||||
passwordEmpty: Palavra-passe vazia
|
||||
</i18n>
|
|
@ -0,0 +1,125 @@
|
|||
<script setup>
|
||||
import { ref, inject } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
|
||||
const props = defineProps({
|
||||
schema: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
imageName: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['close']);
|
||||
|
||||
const api = inject('api');
|
||||
const { t } = useI18n();
|
||||
const { notify } = useNotify();
|
||||
|
||||
const inputFileRef = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const name = ref(props.imageName ?? '');
|
||||
const file = ref(null);
|
||||
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const formData = new FormData();
|
||||
formData.append('name', name.value);
|
||||
formData.append('image', file.value);
|
||||
formData.append('schema', props.schema);
|
||||
formData.append('srv', 'json:image/upload');
|
||||
await api({
|
||||
method: 'post',
|
||||
url: location.origin,
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
notify(t('imageAdded'), 'positive');
|
||||
emit('close');
|
||||
} catch (error) {
|
||||
console.error('Error uploading image:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QForm @submit="onSubmit">
|
||||
<QCard class="q-pa-lg">
|
||||
<VnInput v-model="name" :label="t('name')" />
|
||||
<QFile
|
||||
ref="inputFileRef"
|
||||
:label="t('file')"
|
||||
v-model="file"
|
||||
:multiple="false"
|
||||
class="q-mb-xs"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon
|
||||
name="attach_file"
|
||||
class="cursor-pointer"
|
||||
@click="inputFileRef.pickFiles()"
|
||||
/>
|
||||
</template>
|
||||
</QFile>
|
||||
<div class="flex row justify-end q-gutter-x-sm">
|
||||
<QSpinner
|
||||
v-if="loading"
|
||||
color="primary"
|
||||
size="3em"
|
||||
:thickness="2"
|
||||
/>
|
||||
<QBtn
|
||||
v-else
|
||||
type="submit"
|
||||
:label="t('send')"
|
||||
flat
|
||||
class="q-mt-md"
|
||||
rounded
|
||||
/>
|
||||
</div>
|
||||
</QCard>
|
||||
</QForm>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
name: Name
|
||||
file: File
|
||||
send: Send
|
||||
imageAdded: Image added successfully
|
||||
es-ES:
|
||||
name: Nombre
|
||||
file: Archivo
|
||||
send: Enviar
|
||||
imageAdded: Imagen añadida correctamente
|
||||
ca-ES:
|
||||
name: Nom
|
||||
file: Arxiu
|
||||
send: Enviar
|
||||
imageAdded: Imatge afegida correctament
|
||||
fr-FR:
|
||||
name: Nom
|
||||
file: Fichier
|
||||
send: Envoyer
|
||||
imageAdded: Image ajoutée correctement
|
||||
pt-PT:
|
||||
name: Nome
|
||||
file: Arquivo
|
||||
send: Enviar
|
||||
imageAdded: Imagen adicionada corretamente
|
||||
</i18n>
|
|
@ -0,0 +1,122 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useDialogPluginComponent } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
icon: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
message: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
promise: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
||||
|
||||
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
||||
|
||||
const title = props.title || t('confirm');
|
||||
const message = props.message || t('wantToContinue');
|
||||
const isLoading = ref(false);
|
||||
|
||||
async function confirm() {
|
||||
isLoading.value = true;
|
||||
if (props.promise) {
|
||||
try {
|
||||
await props.promise(props.data);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
onDialogOK(props.data);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<QDialog ref="dialogRef">
|
||||
<QCard class="q-pa-sm">
|
||||
<QCardSection class="row items-center q-pb-none">
|
||||
<QAvatar
|
||||
:icon="icon"
|
||||
color="primary"
|
||||
text-color="white"
|
||||
size="xl"
|
||||
v-if="icon"
|
||||
/>
|
||||
<span class="text-h6 text-grey">{{ title }}</span>
|
||||
<QSpace />
|
||||
<QBtn
|
||||
icon="close"
|
||||
:disable="isLoading"
|
||||
flat
|
||||
round
|
||||
dense
|
||||
v-close-popup
|
||||
/>
|
||||
</QCardSection>
|
||||
<QCardSection class="row items-center">
|
||||
<span v-html="message" />
|
||||
<slot name="customHTML" />
|
||||
</QCardSection>
|
||||
<QCardActions align="right">
|
||||
<QBtn
|
||||
:label="t('cancel')"
|
||||
color="primary"
|
||||
:disable="isLoading"
|
||||
flat
|
||||
v-close-popup
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('confirm')"
|
||||
color="primary"
|
||||
:loading="isLoading"
|
||||
@click="confirm()"
|
||||
unelevated
|
||||
autofocus
|
||||
/>
|
||||
</QCardActions>
|
||||
</QCard>
|
||||
</QDialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.q-card {
|
||||
min-width: 350px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
wantToContinue: Are you sure you want to continue?
|
||||
cancel: Cancel
|
||||
es-ES:
|
||||
wantToContinue: ¿Seguro que quieres continuar?
|
||||
cancel: Cancelar
|
||||
ca-ES:
|
||||
wantToContinue: Segur que vols continuar?
|
||||
cancel: Cancel·lar
|
||||
fr-FR:
|
||||
wantToContinue: Êtes-vous sûr de vouloir continuer?
|
||||
cancel: Annuler
|
||||
pt-PT:
|
||||
wantToContinue: Tem a certeza de que deseja continuar?
|
||||
cancel: Cancelar
|
||||
</i18n>
|
|
@ -0,0 +1,196 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useAppStore } from 'stores/app';
|
||||
|
||||
import ImageEditor from 'src/components/ui/ImageEditor.vue';
|
||||
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const props = defineProps({
|
||||
baseURL: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
storage: {
|
||||
type: [String, Number],
|
||||
default: 'Images'
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'full'
|
||||
},
|
||||
zoomSize: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'lg'
|
||||
},
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
roundedBorders: {
|
||||
type: String,
|
||||
default: 'none'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
editSchema: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
editImageName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
alwaysShowEditButton: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const app = useAppStore();
|
||||
const showZoom = ref(false);
|
||||
const showEditForm = ref(false);
|
||||
const url = computed(() => {
|
||||
return `${props.baseURL ?? app.imageUrl}/${props.storage}/${props.size}/${props.id}`;
|
||||
});
|
||||
|
||||
const rounded = computed(() => {
|
||||
const roundedMap = {
|
||||
none: '',
|
||||
default: 'rounded',
|
||||
full: 'full-rounded',
|
||||
top: 'top-rounded',
|
||||
bottom: 'bottom-rounded'
|
||||
};
|
||||
return roundedMap[props.roundedBorders];
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="relative-position main-image-container">
|
||||
<QBtn
|
||||
v-if="props.editable"
|
||||
icon="add_a_photo"
|
||||
class="show-edit-button absolute-top-left"
|
||||
:class="{ hide: !props.alwaysShowEditButton }"
|
||||
round
|
||||
text-color="black"
|
||||
@click.stop.prevent="showEditForm = !showEditForm"
|
||||
>
|
||||
<QTooltip>{{ t('addOrEditImage') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QImg
|
||||
:class="[rounded, { zoomIn: props.zoomSize }]"
|
||||
class="main-image"
|
||||
:src="url"
|
||||
v-bind="$attrs"
|
||||
@click.stop.prevent="showZoom = !showZoom"
|
||||
spinner-color="primary"
|
||||
:width="props.width"
|
||||
:height="props.height"
|
||||
draggable
|
||||
>
|
||||
<template #error>
|
||||
<div
|
||||
class="full-width full-height flex justify-center items-center"
|
||||
>
|
||||
<QIcon name="image" size="sm" />
|
||||
</div>
|
||||
</template>
|
||||
</QImg>
|
||||
</div>
|
||||
|
||||
<QDialog v-if="props.zoomSize" v-model="showZoom">
|
||||
<QImg
|
||||
:src="url"
|
||||
size="full"
|
||||
class="img_zoom"
|
||||
v-bind="$attrs"
|
||||
spinner-color="primary"
|
||||
draggable
|
||||
/>
|
||||
</QDialog>
|
||||
<QDialog v-if="props.editable" v-model="showEditForm">
|
||||
<ImageEditor
|
||||
class="all-pointer-events"
|
||||
:schema="props.editSchema"
|
||||
:image-name="props.editImageName"
|
||||
@close="showEditForm = false"
|
||||
/>
|
||||
</QDialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main-image-container {
|
||||
&:hover {
|
||||
.show-edit-button {
|
||||
visibility: visible !important;
|
||||
}
|
||||
.main-image {
|
||||
filter: brightness(80%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-image {
|
||||
&.zoomIn {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.show-edit-button {
|
||||
cursor: pointer;
|
||||
background-color: $gray-light;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
border-radius: 0.6em;
|
||||
}
|
||||
|
||||
.full-rounded {
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.rounded-bottom {
|
||||
border-radius: 0.6em 0.6em 0 0;
|
||||
}
|
||||
|
||||
.rounded-top {
|
||||
border-radius: 0 0 0.6em 0.6em;
|
||||
}
|
||||
|
||||
.img_zoom {
|
||||
border-radius: 0%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
addOrEditImage: Add or update an image
|
||||
es-ES:
|
||||
addOrEditImage: Añadir o actualizar imagen
|
||||
ca-ES:
|
||||
addOrEditImage: Afegir o actualitzar Imatge
|
||||
fr-FR:
|
||||
addOrEditImage: Ajouter our mettre à jour l'image
|
||||
pt-PT:
|
||||
addOrEditImage: Adicionar ou atualizar imagem
|
||||
</i18n>
|
|
@ -0,0 +1,38 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
defineProps({
|
||||
emptyMessage: {
|
||||
type: String,
|
||||
default: 'emptyList'
|
||||
},
|
||||
emptyIcon: {
|
||||
type: String,
|
||||
default: 'block'
|
||||
},
|
||||
rows: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QList v-bind="$attrs" class="column items-center">
|
||||
<span
|
||||
v-if="!rows?.length"
|
||||
class="flex items-center q-pa-md justify-center items-center"
|
||||
>
|
||||
<QIcon :name="emptyIcon" size="sm" class="q-mr-sm" />
|
||||
{{ t(emptyMessage) }}
|
||||
</span>
|
||||
<QSpinner v-if="loading" color="primary" size="3em" :thickness="2" />
|
||||
<slot v-else />
|
||||
</QList>
|
||||
</template>
|
|
@ -0,0 +1,101 @@
|
|||
<script setup>
|
||||
import { onMounted, ref, inject } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
|
||||
const props = defineProps({
|
||||
searchFn: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
sqlQuery: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
searchField: {
|
||||
type: String,
|
||||
default: 'search'
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['onSearch', 'onSearchError']);
|
||||
|
||||
const jApi = inject('jApi');
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const searchTerm = ref('');
|
||||
|
||||
const search = async () => {
|
||||
try {
|
||||
let data = null;
|
||||
router.replace({
|
||||
query: searchTerm.value ? { search: searchTerm.value } : {}
|
||||
});
|
||||
|
||||
if (props.sqlQuery) {
|
||||
data = await jApi.query(props.sqlQuery, {
|
||||
[props.searchField]: searchTerm.value
|
||||
});
|
||||
} else if (props.searchFn) {
|
||||
data = props.searchFn(searchTerm.value);
|
||||
}
|
||||
|
||||
emit('onSearch', data);
|
||||
} catch (error) {
|
||||
console.error('Error searching:', error);
|
||||
emit('onSearchError');
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query.search) {
|
||||
searchTerm.value = route.query.search;
|
||||
search();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<VnInput
|
||||
v-model="searchTerm"
|
||||
@keyup.enter="search()"
|
||||
:placeholder="props.placeholder || t('search')"
|
||||
bg-color="white"
|
||||
is-outlined
|
||||
:clearable="false"
|
||||
class="searchbar"
|
||||
>
|
||||
<template #prepend>
|
||||
<QIcon name="search" class="cursor-pointer" @click="search()" />
|
||||
</template>
|
||||
</VnInput>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'src/css/responsive';
|
||||
|
||||
.searchbar {
|
||||
@include mobile {
|
||||
max-width: 120px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
search: Search
|
||||
es-ES:
|
||||
search: Buscar
|
||||
ca-ES:
|
||||
search: Cercar
|
||||
fr-FR:
|
||||
search: Rechercher
|
||||
pt-PT:
|
||||
search: Pesquisar
|
||||
</i18n>
|
|
@ -0,0 +1,41 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
noDataLabel: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
hideBottom: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
rowsPerPageOptions: {
|
||||
type: Array,
|
||||
default: () => [0]
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QTable
|
||||
v-bind="$attrs"
|
||||
:no-data-label="props.noDataLabel || t('noData')"
|
||||
:hide-bottom="props.hideBottom"
|
||||
:rows-per-page-options="props.rowsPerPageOptions"
|
||||
table-header-class="vntable-header-default"
|
||||
>
|
||||
<template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
|
||||
<slot :name="slotName" v-bind="slotProps" />
|
||||
</template>
|
||||
</QTable>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.vntable-header-default {
|
||||
background-color: $accent !important;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,22 @@
|
|||
import { Notify } from 'quasar';
|
||||
import { i18n } from 'src/boot/i18n';
|
||||
|
||||
export default function useNotify() {
|
||||
const notify = (message, type, icon) => {
|
||||
const defaultIcons = {
|
||||
warning: 'warning',
|
||||
negative: 'error',
|
||||
positive: 'check'
|
||||
};
|
||||
|
||||
Notify.create({
|
||||
message: i18n.global.t(message),
|
||||
type,
|
||||
icon: icon || defaultIcons[type]
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
notify
|
||||
};
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import { useUserStore } from 'stores/user';
|
||||
|
||||
import axios from 'axios';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
export function usePrintService() {
|
||||
const quasar = useQuasar();
|
||||
const userStore = useUserStore();
|
||||
const token = userStore.token;
|
||||
|
||||
function sendEmail(path, params) {
|
||||
return axios.post(path, params).then(() =>
|
||||
quasar.notify({
|
||||
message: 'Notification sent',
|
||||
type: 'positive',
|
||||
icon: 'check'
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function openReport(path, params) {
|
||||
params = Object.assign(
|
||||
{
|
||||
access_token: token
|
||||
},
|
||||
params
|
||||
);
|
||||
|
||||
const query = new URLSearchParams(params).toString();
|
||||
|
||||
window.open(`api/${path}?${query}`);
|
||||
}
|
||||
|
||||
return {
|
||||
sendEmail,
|
||||
openReport
|
||||
};
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
export function useVnConfirm() {
|
||||
const quasar = useQuasar();
|
||||
|
||||
const openConfirmationModal = (title, message, promise, successFn) => {
|
||||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title,
|
||||
message,
|
||||
promise
|
||||
}
|
||||
})
|
||||
.onOk(async () => {
|
||||
if (successFn) successFn();
|
||||
});
|
||||
};
|
||||
|
||||
return { openConfirmationModal };
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// app global css in SCSS form
|
||||
|
||||
@font-face {
|
||||
font-family: Poppins;
|
||||
src: url(./poppins.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
src: url(./opensans.ttf) format('truetype');
|
||||
}
|
||||
@mixin mobile {
|
||||
@media screen and (max-width: 960px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Poppins', 'Verdana', 'Sans';
|
||||
background-color: #fafafa;
|
||||
}
|
||||
a.link {
|
||||
text-decoration: none;
|
||||
color: #6a1;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
.default-radius {
|
||||
border-radius: 0.6em;
|
||||
}
|
||||
.q-card {
|
||||
border-radius: 0.6em !important;
|
||||
box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.q-table__container {
|
||||
border-radius: 0.6em !important;
|
||||
}
|
||||
.q-page-sticky.fixed-bottom-right {
|
||||
margin: 18px;
|
||||
}
|
||||
.no-border-radius {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
.no-padding {
|
||||
padding: 0 !important;
|
||||
}
|
||||
input[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// Quasar SCSS (& Sass) Variables
|
||||
// --------------------------------------------------
|
||||
// To customize the look and feel of this app, you can override
|
||||
// the Sass/SCSS variables found in Quasar's source Sass/SCSS files.
|
||||
|
||||
// Check documentation for full list of Quasar variables
|
||||
|
||||
// Your own variables (that are declared here) and Quasar's own
|
||||
// ones will be available out of the box in your .vue/.scss/.sass files
|
||||
|
||||
// It's highly recommended to change the default colors
|
||||
// to match your app's branding.
|
||||
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
||||
|
||||
$primary: #1a1a1a;
|
||||
$secondary: #26a69a;
|
||||
$accent: #8cc63f;
|
||||
$gray-light: #ddd;
|
||||
$dark: #1d1d1d;
|
||||
$dark-page: #121212;
|
||||
|
||||
$positive: #21ba45;
|
||||
$negative: #c10015;
|
||||
$info: #31ccec;
|
||||
$warning: #f2c037;
|
||||
|
||||
// Width
|
||||
|
||||
$width-xs: 400px;
|
||||
$width-sm: 544px;
|
||||
$width-md: 800px;
|
||||
$width-lg: 1280px;
|
||||
$width-xl: 1600px;
|
|
@ -0,0 +1,17 @@
|
|||
@mixin mobile {
|
||||
@media screen and (max-width: 768px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin tablet {
|
||||
@media screen and (min-width: 769px) and (max-width: 1024px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin desktop {
|
||||
@media screen and (min-width: 1025px) {
|
||||
@content;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
%margin-auto {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.vn-w-xs {
|
||||
@extend %margin-auto;
|
||||
max-width: $width-xs;
|
||||
}
|
||||
.vn-w-sm {
|
||||
@extend %margin-auto;
|
||||
max-width: $width-sm;
|
||||
}
|
||||
.vn-w-md {
|
||||
@extend %margin-auto;
|
||||
max-width: $width-md;
|
||||
}
|
||||
.vn-w-lg {
|
||||
@extend %margin-auto;
|
||||
max-width: $width-lg;
|
||||
}
|
||||
.vn-w-xl {
|
||||
@extend %margin-auto;
|
||||
max-width: $width-xl;
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
export default {
|
||||
language: 'Idioma',
|
||||
langs: {
|
||||
en: 'Anglès',
|
||||
es: 'Espanyol',
|
||||
ca: 'Català',
|
||||
fr: 'Francès',
|
||||
mn: 'Rus',
|
||||
pt: 'Portuguès'
|
||||
},
|
||||
date: {
|
||||
days: [
|
||||
'Diumenge',
|
||||
'Dilluns',
|
||||
'Dimarts',
|
||||
'Dimecres',
|
||||
'Dijous',
|
||||
'Divendres',
|
||||
'Dissabte'
|
||||
],
|
||||
daysShort: ['Dg', 'Dl', 'Dt', 'Dc', 'Dj', 'Dv', 'Ds'],
|
||||
months: [
|
||||
'Gener',
|
||||
'Febrer',
|
||||
'Març',
|
||||
'Abril',
|
||||
'Maig',
|
||||
'Juny',
|
||||
'Juliol',
|
||||
'Agost',
|
||||
'Setembre',
|
||||
'Octubre',
|
||||
'Novembre',
|
||||
'Desembre'
|
||||
],
|
||||
monthsShort: [
|
||||
'Gen',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'Mai',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Set',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Des'
|
||||
]
|
||||
},
|
||||
of: 'de',
|
||||
|
||||
// Sections titles
|
||||
titles: {
|
||||
Home: 'Inici',
|
||||
Orders: 'Comandes',
|
||||
Ticket: `Detall de l'encarrec`,
|
||||
'Pending orders': 'Comandes pendents',
|
||||
'Last orders': 'Comandes confirmades',
|
||||
Invoices: 'Factures',
|
||||
Basket: 'Cistella',
|
||||
Catalog: 'Catàleg',
|
||||
Administration: 'Administració',
|
||||
'Control panel': 'Panell de control',
|
||||
Users: 'Usuaris',
|
||||
Connections: 'Connexions',
|
||||
Visits: 'Visites',
|
||||
News: 'Gestió de noticies',
|
||||
Photos: 'Imatges',
|
||||
Images: 'Imatges',
|
||||
Items: 'Articles',
|
||||
Agencies: 'Paquets per agència',
|
||||
Reports: 'Informes',
|
||||
Configuration: 'Configuració',
|
||||
Shelves: 'Prestatgeries',
|
||||
Account: 'Compte',
|
||||
Addresses: 'Adreces',
|
||||
OrderSummary: 'Resum de la comanda',
|
||||
Checkout: `Configurar l'encarrec`,
|
||||
'Address details': 'Configuració',
|
||||
'Admin news details': `Afegir o editar notícia`,
|
||||
'Access log': 'Registre d’accés'
|
||||
},
|
||||
menuTitles: {
|
||||
Home: 'Inici',
|
||||
Orders: 'Comandes',
|
||||
Basket: 'Cistella',
|
||||
Catalog: 'Catàleg',
|
||||
Administration: 'Administració',
|
||||
Agencies: 'Agències',
|
||||
Reports: 'Informes',
|
||||
Shelves: 'Prestatgeries',
|
||||
Configuration: 'Configuració',
|
||||
'Pending orders': 'Pendents',
|
||||
'Last orders': 'Confirmades',
|
||||
Invoices: 'Factures',
|
||||
'Control panel': 'Panell de control',
|
||||
Users: 'Usuaris',
|
||||
Connections: 'Connexions',
|
||||
Visits: 'Visites',
|
||||
News: 'Gestió de notícies',
|
||||
Photos: 'Imatges',
|
||||
Items: 'Articles',
|
||||
Account: 'Compte',
|
||||
Addresses: 'Adreces'
|
||||
},
|
||||
//
|
||||
orderLoadedIntoBasket: 'Comanda carregada a la cistella!',
|
||||
loadAnOrder:
|
||||
'Si us plau carrega una comanda pendent a la cistella o en comença una de nova',
|
||||
at: 'a les',
|
||||
back: 'Tornar',
|
||||
next: 'Següent',
|
||||
remove: 'Esborrar',
|
||||
agency: 'Agència',
|
||||
noData: 'Sense dades',
|
||||
confirm: 'Confirmar',
|
||||
delete: 'Esborrar',
|
||||
confirmDelete: 'Estàs segur que vols esborrar la línia?',
|
||||
emptyList: 'Llista buida',
|
||||
logInAsGuest: `Accedir com a convidat`,
|
||||
logIn: 'Iniciar sessió',
|
||||
haveForgottenPassword: '¿Has oblidat la teva contrasenya?',
|
||||
signUp: 'Registrar-me',
|
||||
notACustomerYet: `Encara no ets client?`,
|
||||
loginPhone: '+34 963 242 100',
|
||||
loginMail: "{'info'}{'@'}{'verdnatura.es'}",
|
||||
remindMe: "Recorda'm",
|
||||
user: 'Usuari',
|
||||
password: 'Contrasenya',
|
||||
modify: 'Modificar',
|
||||
shoppingCart: 'Cistella de la compra',
|
||||
available: 'Disponible',
|
||||
minQuantity: 'Quantitat mínima',
|
||||
introduceSearchTerm: 'Introdueix un terme de cerca',
|
||||
noOrdersFound: `No s'han trobat comandes`,
|
||||
// Image related translations
|
||||
'Cant lock cache': 'No es pot bloquejar la memòria cau',
|
||||
'Bad file format': 'Format de fitxer no reconegut',
|
||||
'File not choosed': 'No has seleccionat cap fitxer',
|
||||
'Permission denied': 'No tens permís per pujar el fitxer',
|
||||
'File upload error':
|
||||
"No s'ha pogut pujar el fitxer, comprova que la mida no sigui massa gran",
|
||||
'File save error': "No s'ha pogut desar el fitxer: %s",
|
||||
'File size error': 'El fitxer no ha de superar %.2f MB',
|
||||
'Bad file name':
|
||||
"El nom del fitxer només ha de contenir lletres minúscules, dígits o el caràcter '_'",
|
||||
'Bad collection name': 'Nom de col·lecció no vàlid',
|
||||
'Collection not exists': 'La col·lecció no existeix',
|
||||
'Unreferenced file': 'El fitxer no està referenciat per la base de dades',
|
||||
'Cannot update matching id': "No es pot actualitzar l'id coincident",
|
||||
'Com error': 'Error de comunicació amb el servidor',
|
||||
'Image open error': "Error en obrir el fitxer d'imatge",
|
||||
'Operation disabled': 'Operació deshabilitada per seguretat',
|
||||
'Image added': 'Imatge afegida correctament',
|
||||
ErrIniSize: 'El fitxer supera la directiva upload_max_filesize a php.ini',
|
||||
ErrFormSize:
|
||||
'El fitxer supera el MAX_FILE_SIZE especificat al formulari HTML',
|
||||
ErrPartial: "El fitxer s'ha pujat parcialment",
|
||||
ErrNoFile: "No s'ha pujat cap fitxer",
|
||||
ErrNoTmpDir: 'Falta una carpeta temporal',
|
||||
ErrCantWrite: "No s'ha pogut escriure el fitxer al disc",
|
||||
ErrExtension: "La pujada del fitxer s'ha aturat per una extensió",
|
||||
ErrDefault: 'Error de pujada desconegut',
|
||||
'Sync complete': 'Sincronització completa'
|
||||
};
|
|
@ -0,0 +1,199 @@
|
|||
// This is just an example,
|
||||
// so you can safely delete all default props below
|
||||
|
||||
export default {
|
||||
failed: 'Action failed',
|
||||
success: 'Action was successful',
|
||||
internalServerError: 'Internal server error',
|
||||
somethingWentWrong: 'Something went wrong',
|
||||
loginFailed: 'Login failed',
|
||||
authenticationRequired: 'Authentication required',
|
||||
notFound: 'Not found',
|
||||
today: 'Today',
|
||||
yesterday: 'Yesterday',
|
||||
tomorrow: 'Tomorrow',
|
||||
language: 'Language',
|
||||
langs: {
|
||||
en: 'English',
|
||||
es: 'Spanish',
|
||||
ca: 'Catalan',
|
||||
fr: 'French',
|
||||
mn: 'Russian',
|
||||
pt: 'Portuguese'
|
||||
},
|
||||
date: {
|
||||
days: [
|
||||
'Sunday',
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday'
|
||||
],
|
||||
daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||
months: [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December'
|
||||
],
|
||||
monthsShort: [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec'
|
||||
]
|
||||
},
|
||||
|
||||
// Sections titles
|
||||
titles: {
|
||||
Home: 'Home',
|
||||
Orders: 'Orders',
|
||||
Ticket: 'Detalle del pedido',
|
||||
'Pending orders': 'Pending orders',
|
||||
'Last orders': 'Confirmed orders',
|
||||
Invoices: 'Invoices',
|
||||
Basket: 'Basket',
|
||||
Catalog: 'Catalog',
|
||||
Administration: 'Administration',
|
||||
'Control panel': 'Control panel',
|
||||
Users: 'Users',
|
||||
Connections: 'Connections',
|
||||
Visits: 'Visits',
|
||||
News: 'News management',
|
||||
Photos: 'Images',
|
||||
Images: 'Images',
|
||||
Items: 'Items',
|
||||
Agencies: 'Bundles by agency',
|
||||
Reports: 'Reports',
|
||||
Configuration: 'Configuration',
|
||||
Shelves: 'Shelves',
|
||||
Account: 'Account',
|
||||
Addresses: 'Addresses',
|
||||
OrderSummary: 'Order summary',
|
||||
Checkout: 'Configure order',
|
||||
'Address details': 'Configuration',
|
||||
'Admin news details': 'Add or edit new',
|
||||
'Access log': 'Access log'
|
||||
},
|
||||
menuTitles: {
|
||||
Home: 'Home',
|
||||
Orders: 'Orders',
|
||||
Basket: 'Basket',
|
||||
Catalog: 'Catalog',
|
||||
Administration: 'Administration',
|
||||
Agencies: 'Agencies',
|
||||
Reports: 'Reports',
|
||||
Shelves: 'Shelves',
|
||||
Configuration: 'Configuration',
|
||||
'Pending orders': 'Pending',
|
||||
'Last orders': 'Confirmed',
|
||||
Invoices: 'Invoices',
|
||||
'Control panel': 'Control panel',
|
||||
Users: 'Users',
|
||||
Connections: 'Connections',
|
||||
Visits: 'Visits',
|
||||
News: 'News',
|
||||
Photos: 'Images',
|
||||
Items: 'Items',
|
||||
Account: 'Account',
|
||||
Addresses: 'Addresses'
|
||||
},
|
||||
|
||||
//
|
||||
orderLoadedIntoBasket: 'Order loaded into basket!',
|
||||
loadAnOrder: 'Please load a pending order to the cart or start a new one',
|
||||
at: 'at',
|
||||
back: 'Back',
|
||||
next: 'Next',
|
||||
remove: 'Remove',
|
||||
agency: 'Agency',
|
||||
noData: 'No data',
|
||||
confirm: 'Confirm',
|
||||
delete: 'Delete',
|
||||
confirmDelete: 'Are you sure you want to delete the line?',
|
||||
emptyList: 'Empty list',
|
||||
|
||||
orders: 'Orders',
|
||||
order: 'Pending order',
|
||||
ticket: 'Order',
|
||||
conditions: 'Conditions',
|
||||
about: 'About us',
|
||||
admin: 'Administration',
|
||||
panel: 'Control panel',
|
||||
users: 'Users',
|
||||
connections: 'Connections',
|
||||
visits: 'Visits',
|
||||
news: 'News',
|
||||
newEdit: 'Edit new',
|
||||
images: 'Images',
|
||||
items: 'Items',
|
||||
config: 'Configuration',
|
||||
user: 'User',
|
||||
addresses: 'Addresses',
|
||||
addressEdit: 'Edit address',
|
||||
dataSaved: 'Data saved',
|
||||
save: 'Save',
|
||||
cancel: 'Cancel',
|
||||
of: 'of',
|
||||
logInAsGuest: 'Login as guest',
|
||||
logIn: 'Log in',
|
||||
haveForgottenPassword: 'Have you forgotten your password?',
|
||||
signUp: 'Sign up',
|
||||
notACustomerYet: 'Not a customer yet?',
|
||||
loginPhone: '+34 963 242 100',
|
||||
loginMail: "{'info'}{'@'}{'verdnatura.es'}",
|
||||
remindMe: 'Remember me',
|
||||
password: 'Password',
|
||||
modify: 'Modify',
|
||||
shoppingCart: 'Shopping cart',
|
||||
available: 'Available',
|
||||
minQuantity: 'Minimum quantity',
|
||||
introduceSearchTerm: 'Enter a search term',
|
||||
noOrdersFound: 'No orders found',
|
||||
// Image related translations
|
||||
'Cant lock cache': 'The cache could not be blocked',
|
||||
'Bad file format': 'Unrecognized file format',
|
||||
'File not choosed': 'You have not selected any file',
|
||||
'Permission denied': 'You are not allowed to upload the file',
|
||||
'File upload error':
|
||||
'Failed to upload the file, check that size is not too large',
|
||||
'File save error': 'Failed to save the file: %s',
|
||||
'File size error': 'The file must be no longer than %.2f MB',
|
||||
'Bad file name':
|
||||
"The file name must contain only lowercase letters, digits or the '_' character",
|
||||
'Bad collection name': 'Invalid collection name',
|
||||
'Collection not exists': 'Collection does not exist',
|
||||
'Unreferenced file': 'The file is not referenced by the database',
|
||||
'Cannot update matching id': 'Cannot update matching id',
|
||||
'Com error': 'Error communicating with the server',
|
||||
'Image open error': 'Error opening the image file',
|
||||
'Operation disabled': 'Operation disabled for security',
|
||||
'Image added': 'Image added correctly',
|
||||
ErrIniSize: 'File exceeds the upload_max_filesize directive in php.ini',
|
||||
ErrFormSize: 'File exceeds the MAX_FILE_SIZE specified in the HTML form',
|
||||
ErrPartial: 'File was partially uploaded',
|
||||
ErrNoFile: 'No file was uploaded',
|
||||
ErrNoTmpDir: 'Missing a temporary folder',
|
||||
ErrCantWrite: 'Failed to write file to disk',
|
||||
ErrExtension: 'File upload stopped by extension',
|
||||
ErrDefault: 'Unknown upload error',
|
||||
'Sync complete': 'Synchronization complete'
|
||||
};
|
|
@ -0,0 +1,198 @@
|
|||
export default {
|
||||
failed: 'Acción fallida',
|
||||
success: 'Acción exitosa',
|
||||
internalServerError: 'Error interno del servidor',
|
||||
somethingWentWrong: 'Algo salió mal',
|
||||
loginFailed: 'Usuario o contraseña incorrectos',
|
||||
authenticationRequired: 'Autenticación requerida',
|
||||
notFound: 'No encontrado',
|
||||
today: 'Hoy',
|
||||
yesterday: 'Ayer',
|
||||
tomorrow: 'Mañana',
|
||||
language: 'Idioma',
|
||||
langs: {
|
||||
en: 'Inglés',
|
||||
es: 'Español',
|
||||
ca: 'Catalán',
|
||||
fr: 'Francés',
|
||||
mn: 'Ruso',
|
||||
pt: 'Portugés'
|
||||
},
|
||||
date: {
|
||||
days: [
|
||||
'Domingo',
|
||||
'Lunes',
|
||||
'Martes',
|
||||
'Miércoles',
|
||||
'Jueves',
|
||||
'Viernes',
|
||||
'Sábado'
|
||||
],
|
||||
daysShort: ['Do', 'Lu', 'Mi', 'Mi', 'Ju', 'Vi', 'Sa'],
|
||||
months: [
|
||||
'Enero',
|
||||
'Febrero',
|
||||
'Marzo',
|
||||
'Abril',
|
||||
'Mayo',
|
||||
'Junio',
|
||||
'Julio',
|
||||
'Agosto',
|
||||
'Septiembre',
|
||||
'Octubre',
|
||||
'Noviembre',
|
||||
'Diciembre'
|
||||
],
|
||||
monthsShort: [
|
||||
'Ene',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dic'
|
||||
]
|
||||
},
|
||||
|
||||
// Sections titles
|
||||
titles: {
|
||||
Home: 'Inicio',
|
||||
Orders: 'Pedidos',
|
||||
Ticket: 'Pedido',
|
||||
'Pending orders': 'Pedidos pendientes',
|
||||
'Last orders': 'Pedidos confirmados',
|
||||
Invoices: 'Facturas',
|
||||
Basket: 'Cesta',
|
||||
Catalog: 'Catálogo',
|
||||
Administration: 'Administración',
|
||||
'Control panel': 'Panel de control',
|
||||
Users: 'Usuarios',
|
||||
Connections: 'Conexiones',
|
||||
Visits: 'Visitas',
|
||||
News: 'Gestión de noticias',
|
||||
Photos: 'Imágenes',
|
||||
Images: 'Imágenes',
|
||||
Items: 'Artículos',
|
||||
Agencies: 'Bultos por agencia',
|
||||
Reports: 'Informes',
|
||||
Configuration: 'Configuración',
|
||||
Shelves: 'Estanterías',
|
||||
Account: 'Cuenta',
|
||||
Addresses: 'Direcciones',
|
||||
OrderSummary: 'Resumen del pedido',
|
||||
Checkout: 'Configurar pedido',
|
||||
'Address details': 'Configuración',
|
||||
'Admin news details': 'Añadir o editar noticia',
|
||||
'Access log': 'Registro de accesos'
|
||||
},
|
||||
menuTitles: {
|
||||
Home: 'Inicio',
|
||||
Orders: 'Pedidos',
|
||||
Basket: 'Cesta',
|
||||
Catalog: 'Catálogo',
|
||||
Administration: 'Administración',
|
||||
Agencies: 'Agencias',
|
||||
Reports: 'Informes',
|
||||
Shelves: 'Estanterías',
|
||||
Configuration: 'Configuración',
|
||||
'Pending orders': 'Pendientes',
|
||||
'Last orders': 'Confirmados',
|
||||
Invoices: 'Facturas',
|
||||
'Control panel': 'Panel de control',
|
||||
Users: 'Usuarios',
|
||||
Connections: 'Conexiones',
|
||||
Visits: 'Visitas',
|
||||
News: 'Noticias',
|
||||
Photos: 'Imágenes',
|
||||
Items: 'Artículos',
|
||||
Account: 'Cuenta',
|
||||
Addresses: 'Direcciones'
|
||||
},
|
||||
|
||||
//
|
||||
orderLoadedIntoBasket: '¡Pedido cargado en la cesta!',
|
||||
loadAnOrder:
|
||||
'Por favor carga un pedido pendiente en la cesta o empieza uno nuevo',
|
||||
at: 'a las',
|
||||
back: 'Volver',
|
||||
next: 'Siguiente',
|
||||
remove: 'Borrar',
|
||||
agency: 'Agencia',
|
||||
noData: 'Sin datos',
|
||||
confirm: 'Confirmar',
|
||||
delete: 'Borrar',
|
||||
confirmDelete: '¿Estás seguro de que quieres borrar la línea?',
|
||||
emptyList: 'Lista vacía',
|
||||
|
||||
orders: 'Pedidos',
|
||||
order: 'Pedido pendiente',
|
||||
ticket: 'Pedido',
|
||||
conditions: 'Condiciones',
|
||||
about: 'Sobre nosotros',
|
||||
admin: 'Administración',
|
||||
panel: 'Panel de control',
|
||||
users: 'Usuarios',
|
||||
connections: 'Conexiones',
|
||||
visits: 'Visitas',
|
||||
news: 'Noticias',
|
||||
newEdit: 'Editar noticia',
|
||||
images: 'Imágenes',
|
||||
items: 'Artículos',
|
||||
config: 'Configuración',
|
||||
user: 'Usuario',
|
||||
password: 'Contraseña',
|
||||
remindMe: 'Recuérdame',
|
||||
logInAsGuest: 'Entrar como invitado',
|
||||
logIn: 'Iniciar sesión',
|
||||
loginMail: "{'info'}{'@'}{'verdnatura.es'}",
|
||||
loginPhone: '+34 963 242 100',
|
||||
haveForgottenPassword: '¿Has olvidado tu contraseña?',
|
||||
notACustomerYet: '¿Todavía no eres cliente?',
|
||||
signUp: 'Registrarme',
|
||||
addresses: 'Direcciones',
|
||||
addressEdit: 'Editar dirección',
|
||||
dataSaved: 'Datos guardados',
|
||||
save: 'Guardar',
|
||||
cancel: 'Cancelar',
|
||||
of: 'de',
|
||||
modify: 'Modificar',
|
||||
shoppingCart: 'Cesta de la compra',
|
||||
available: 'Disponible',
|
||||
minQuantity: 'Cantidad mínima',
|
||||
introduceSearchTerm: 'Introduce un término de búsqueda',
|
||||
noOrdersFound: 'No se encontrado pedidos',
|
||||
// Image related translations
|
||||
'Cant lock cache': 'La caché no pudo ser bloqueada',
|
||||
'Bad file format': 'Formato de archivo no reconocido',
|
||||
'File not choosed': 'No has seleccionado ningún archivo',
|
||||
'Permission denied': 'No tienes permiso para subir el fichero',
|
||||
'File upload error':
|
||||
'Error al subir el fichero, comprueba que su tamaño no sea demasiado grande',
|
||||
'File save error': 'Error al guardar el fichero: %s',
|
||||
'File size error': 'El fichero no debe ocupar más de %.2f MB',
|
||||
'Bad file name':
|
||||
"El nombre del archivo solo debe contener letras minúsculas, dígitos o el carácter '_'",
|
||||
'Bad collection name': 'Nombre de colección no válido',
|
||||
'Collection not exists': 'La colección no existe',
|
||||
'Unreferenced file': 'El archivo no está referenciado por la base de datos',
|
||||
'Cannot update matching id':
|
||||
'No es posible actualizar los ítems con id coincidente',
|
||||
'Com error': 'Error en la comunicación con el servidor',
|
||||
'Image open error': 'Error al abrir el archivo de imagen',
|
||||
'Operation disabled': 'Operación deshabilitada por seguridad',
|
||||
'Image added': 'Imagen añadida correctamente',
|
||||
ErrIniSize: 'File exceeds the upload_max_filesize directive in php.ini',
|
||||
ErrFormSize: 'File exceeds the MAX_FILE_SIZE specified in the HTML form',
|
||||
ErrPartial: 'File was partially uploaded',
|
||||
ErrNoFile: 'No file was uploaded',
|
||||
ErrNoTmpDir: 'Missing a temporary folder',
|
||||
ErrCantWrite: 'Failed to write file to disk',
|
||||
ErrExtension: 'File upload stopped by extension',
|
||||
ErrDefault: 'Unknown upload error',
|
||||
'Sync complete': 'Sincronización completada'
|
||||
};
|
|
@ -0,0 +1,170 @@
|
|||
export default {
|
||||
language: 'Langue',
|
||||
langs: {
|
||||
en: 'Anglais',
|
||||
es: 'Espagnol',
|
||||
ca: 'Catalan',
|
||||
fr: 'Français',
|
||||
mn: 'Russe',
|
||||
pt: 'Portugais'
|
||||
},
|
||||
date: {
|
||||
days: [
|
||||
'Dimanche',
|
||||
'Lundi',
|
||||
'Mardi',
|
||||
'Mercredi',
|
||||
'Jeudi',
|
||||
'Vendredi',
|
||||
'Samedi'
|
||||
],
|
||||
daysShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
|
||||
months: [
|
||||
'Janvier',
|
||||
'Février',
|
||||
'Mars',
|
||||
'Avril',
|
||||
'Mai',
|
||||
'Juin',
|
||||
'Juillet',
|
||||
'Août',
|
||||
'Septembre',
|
||||
'Octobre',
|
||||
'Novembre',
|
||||
'Décembre'
|
||||
],
|
||||
monthsShort: [
|
||||
'Jan',
|
||||
'Fév',
|
||||
'Mar',
|
||||
'Avr',
|
||||
'Mai',
|
||||
'Juin',
|
||||
'Juil',
|
||||
'Aoû',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Déc'
|
||||
]
|
||||
},
|
||||
of: 'de',
|
||||
|
||||
// Sections titles
|
||||
titles: {
|
||||
Home: 'Accueil',
|
||||
Orders: 'Commandes',
|
||||
Ticket: 'Détail de la commande',
|
||||
'Pending orders': 'Commandes en attente',
|
||||
'Last orders': 'Commandes confirmées',
|
||||
Invoices: 'Factures',
|
||||
Basket: 'Panier',
|
||||
Catalog: 'Catalogue',
|
||||
Administration: 'Administration',
|
||||
'Control panel': 'Panneau de configuration',
|
||||
Users: 'Utilisateurs',
|
||||
Connections: 'Connexions',
|
||||
Visits: 'Visites',
|
||||
News: 'Gestion des nouvelles',
|
||||
Photos: 'Images',
|
||||
Images: 'Images',
|
||||
Items: 'Articles',
|
||||
Agencies: 'Liste par agence',
|
||||
Reports: 'Rapports',
|
||||
Configuration: 'Configuration',
|
||||
Shelves: 'Étagères',
|
||||
Account: 'Compte',
|
||||
Addresses: 'Adresses',
|
||||
OrderSummary: 'Résumé de la commande',
|
||||
Checkout: 'Configurer la commande',
|
||||
'Address details': 'Configuration',
|
||||
'Admin news details': 'Ajouter ou éditer une nouvelle',
|
||||
'Access log': "Journal d'accès"
|
||||
},
|
||||
menuTitles: {
|
||||
Home: 'Accueil',
|
||||
Orders: 'Commandes',
|
||||
Basket: 'Panier',
|
||||
Catalog: 'Catalogue',
|
||||
Administration: 'Administration',
|
||||
Agencies: 'Agences',
|
||||
Reports: 'Rapports',
|
||||
Shelves: 'Étagères',
|
||||
Configuration: 'Configuration',
|
||||
'Pending orders': 'En attente',
|
||||
'Last orders': 'Confirmées',
|
||||
Invoices: 'Factures',
|
||||
'Control panel': 'Panneau de configuration',
|
||||
Users: 'Utilisateurs',
|
||||
Connections: 'Connexions',
|
||||
Visits: 'Visites',
|
||||
News: 'Nouvelles',
|
||||
Photos: 'Images',
|
||||
Items: 'Articles',
|
||||
Account: 'Compte',
|
||||
Addresses: 'Adresses'
|
||||
},
|
||||
|
||||
//
|
||||
orderLoadedIntoBasket: 'Commande chargée dans le panier!',
|
||||
loadAnOrder:
|
||||
'Veuillez télécharger une commande en attente dans le panier ou en démarrer une nouvelle',
|
||||
at: 'à',
|
||||
back: 'Retour',
|
||||
next: 'Suivant',
|
||||
remove: 'Effacer',
|
||||
agency: 'Agence',
|
||||
noData: 'Aucune donnée',
|
||||
confirm: 'Confirmer',
|
||||
delete: 'Effacer',
|
||||
emptyList: 'Vider la liste',
|
||||
confirmDelete: 'Voulez-vous vraiment supprimer la ligne?',
|
||||
logInAsGuest: `Entrez en tant qu'invité`,
|
||||
logIn: 'Se connecter',
|
||||
haveForgottenPassword: 'Avez-vous oublié votre mot de passe?',
|
||||
signUp: `S'inscrire`,
|
||||
notACustomerYet: `Pas encore client?`,
|
||||
loginPhone: '+34 963 242 100',
|
||||
loginMail: "{'info'}{'@'}{'verdnatura.es'}",
|
||||
remindMe: `Rappelle-moi`,
|
||||
user: 'Utilisateur',
|
||||
password: 'Mot de passe',
|
||||
modify: 'Modifier',
|
||||
shoppingCart: 'Panier',
|
||||
available: 'Disponible',
|
||||
minQuantity: 'Quantité minimum',
|
||||
introduceSearchTerm: 'Entrez un terme de recherche',
|
||||
noOrdersFound: 'Aucune commande trouvée',
|
||||
// Image related translations
|
||||
'Cant lock cache': "Le cache n'a pas pu être verrouillé",
|
||||
'Bad file format': 'Format de fichier non reconnu',
|
||||
'File not choosed': "Vous n'avez sélectionné aucun fichier",
|
||||
'Permission denied': "Vous n'êtes pas autorisé à télécharger le fichier",
|
||||
'File upload error':
|
||||
"Échec du téléchargement du fichier, vérifiez que la taille n'est pas trop grande",
|
||||
'File save error': "Échec de l'enregistrement du fichier : %s",
|
||||
'File size error': 'Le fichier ne doit pas dépasser %.2f MB',
|
||||
'Bad file name':
|
||||
"Le nom du fichier ne doit contenir que des lettres minuscules, des chiffres ou le caractère '_'",
|
||||
'Bad collection name': 'Nom de collection invalide',
|
||||
'Collection not exists': "La collection n'existe pas",
|
||||
'Unreferenced file':
|
||||
"Le fichier n'est pas référencé par la base de données",
|
||||
'Cannot update matching id':
|
||||
"Impossible de mettre à jour l'ID correspondant",
|
||||
'Com error': 'Erreur de communication avec le serveur',
|
||||
'Image open error': "Erreur lors de l'ouverture du fichier image",
|
||||
'Operation disabled': 'Opération désactivée pour des raisons de sécurité',
|
||||
'Image added': 'Image ajoutée correctement',
|
||||
ErrIniSize:
|
||||
'Le fichier dépasse la directive upload_max_filesize dans php.ini',
|
||||
ErrFormSize:
|
||||
'Le fichier dépasse la taille maximale spécifiée dans le formulaire HTML',
|
||||
ErrPartial: 'Le fichier a été partiellement téléchargé',
|
||||
ErrNoFile: "Aucun fichier n'a été téléchargé",
|
||||
ErrNoTmpDir: 'Il manque un dossier temporaire',
|
||||
ErrCantWrite: "Échec de l'écriture du fichier sur le disque",
|
||||
ErrExtension: 'Téléchargement du fichier arrêté par extension',
|
||||
ErrDefault: 'Erreur de téléchargement inconnue',
|
||||
'Sync complete': 'Synchronisation terminée'
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
import enUS from './en-US';
|
||||
import esES from './es-ES';
|
||||
import frFR from './fr-FR';
|
||||
import ptPT from './pt-PT';
|
||||
import caES from './ca-ES';
|
||||
|
||||
export default {
|
||||
'en-US': enUS,
|
||||
'es-ES': esES,
|
||||
'fr-FR': frFR,
|
||||
'pt-PT': ptPT,
|
||||
'ca-ES': caES
|
||||
};
|
|
@ -0,0 +1,163 @@
|
|||
export default {
|
||||
language: 'Língua',
|
||||
langs: {
|
||||
en: 'Inglês',
|
||||
es: 'Espanhol',
|
||||
ca: 'Catalão',
|
||||
fr: 'Francês',
|
||||
mn: 'Russo',
|
||||
pt: 'Português'
|
||||
},
|
||||
date: {
|
||||
days: [
|
||||
'Domingo',
|
||||
'Segunda-feira',
|
||||
'Terça-feira',
|
||||
'Quarta-feira',
|
||||
'Quinta-feira',
|
||||
'Sexta-feira',
|
||||
'Sábado'
|
||||
],
|
||||
daysShort: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
|
||||
months: [
|
||||
'Janeiro',
|
||||
'Fevereiro',
|
||||
'Março',
|
||||
'Abril',
|
||||
'Maio',
|
||||
'Junho',
|
||||
'Julho',
|
||||
'Agosto',
|
||||
'Setembro',
|
||||
'Outubro',
|
||||
'Novembro',
|
||||
'Dezembro'
|
||||
],
|
||||
monthsShort: [
|
||||
'Jan',
|
||||
'Fev',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'Mai',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Set',
|
||||
'Out',
|
||||
'Nov',
|
||||
'Dez'
|
||||
]
|
||||
},
|
||||
of: 'de',
|
||||
// Sections titles
|
||||
titles: {
|
||||
Home: 'Início',
|
||||
Orders: 'Pedidos',
|
||||
Ticket: 'Detalhe do pedido',
|
||||
'Pending orders': 'Pedidos pendentes',
|
||||
'Last orders': 'Pedidos confirmados',
|
||||
Invoices: 'Faturas',
|
||||
Basket: 'Carrinho',
|
||||
Catalog: 'Catálogo',
|
||||
Administration: 'Administração',
|
||||
'Control panel': 'Painel de controle',
|
||||
Users: 'Usuários',
|
||||
Connections: 'Conexões',
|
||||
Visits: 'Visitas',
|
||||
News: 'Gestão de noticias',
|
||||
Photos: 'Imagens',
|
||||
Images: 'Imagens',
|
||||
Items: 'Artigos',
|
||||
Agencies: 'Bultos por agencia',
|
||||
Reports: 'Informes',
|
||||
Configuration: 'Configuração',
|
||||
Shelves: 'Estantes',
|
||||
Account: 'Conta',
|
||||
Addresses: 'Moradas',
|
||||
OrderSummary: 'Resumo da encomenda',
|
||||
Checkout: 'Configurar encomenda',
|
||||
'Address details': 'Configuração',
|
||||
'Admin news details': 'Adicionar ou editar notícia',
|
||||
'Access log': 'Registo de acessos'
|
||||
},
|
||||
menuTitles: {
|
||||
Home: 'Início',
|
||||
Orders: 'Pedidos',
|
||||
Basket: 'Carrinho',
|
||||
Catalog: 'Catálogo',
|
||||
Administration: 'Administração',
|
||||
Agencies: 'Agências',
|
||||
Reports: 'Informes',
|
||||
Shelves: 'Estantes',
|
||||
Configuration: 'Configuração',
|
||||
'Pending orders': 'Pendentes',
|
||||
'Last orders': 'Confirmados',
|
||||
Invoices: 'Faturas',
|
||||
'Control panel': 'Painel de controle',
|
||||
Users: 'Usuários',
|
||||
Connections: 'Conexões',
|
||||
Visits: 'Visitas',
|
||||
News: 'Notícias',
|
||||
Photos: 'Imagens',
|
||||
Items: 'Artigos',
|
||||
Account: 'Conta',
|
||||
Addresses: 'Moradas'
|
||||
},
|
||||
//
|
||||
orderLoadedIntoBasket: 'Pedido carregado na cesta!',
|
||||
loadAnOrder: 'Carregue um pedido pendente no carrinho ou inicie um novo',
|
||||
at: 'às',
|
||||
back: 'Voltar',
|
||||
next: 'Seguinte',
|
||||
remove: 'Eliminar',
|
||||
agency: 'Agência',
|
||||
noData: 'Sem dados',
|
||||
confirm: 'Confirme',
|
||||
delete: 'Eliminar',
|
||||
confirmDelete: 'Tens certeza que queres eliminar esta linha?',
|
||||
emptyList: 'Lista vazia',
|
||||
logInAsGuest: 'Entrar como convidado',
|
||||
logIn: 'Iniciar sessão',
|
||||
haveForgottenPassword: 'Esqueceu a senha?',
|
||||
signUp: 'Registar',
|
||||
notACustomerYet: 'Ainda não é cliente?',
|
||||
loginPhone: '+34 963 242 100',
|
||||
loginMail: "{'info'}{'@'}{'verdnatura.es'}",
|
||||
remindMe: 'Lembrar-me',
|
||||
user: 'Utilizador',
|
||||
password: 'Senha',
|
||||
modify: 'Modificar',
|
||||
shoppingCart: 'Cesta da compra',
|
||||
available: 'Disponível',
|
||||
minQuantity: 'Quantidade mínima',
|
||||
introduceSearchTerm: 'Digite um termo de pesquisa',
|
||||
noOrdersFound: 'Nenhum pedido encontrado',
|
||||
// Image related translations
|
||||
'Cant lock cache': 'O cache não pôde ser bloqueado',
|
||||
'Bad file format': 'Formato de arquivo inválido',
|
||||
'File not choosed': 'Não selecionastes nenhum arquivo',
|
||||
'Permission denied': 'Não estas autorizado a subir o arquivo',
|
||||
'File upload error': 'Erro ao subir o arquivo, verifique o tamanho',
|
||||
'File save error': 'Erro ao salvar o arquivo: %s',
|
||||
'File size error': 'O arquivo não deve ser maior que %.2f MB',
|
||||
'Bad file name':
|
||||
"O nome do arquivo deve conter somente letras minusculas, numeros ou '_'",
|
||||
'Bad collection name': 'Nome de coleção inválido',
|
||||
'Collection not exists': 'Coleção não existe',
|
||||
'Unreferenced file': 'O arquivo não é referenciado pelo banco de dados',
|
||||
'Cannot update matching id':
|
||||
'Não é possível atualizar os itens com id coincidente',
|
||||
'Com error': 'Erro de comunicação com o servidor',
|
||||
'Image open error': 'Erro ao abrir a imagem',
|
||||
'Operation disabled': 'Operação desativada por segurança',
|
||||
'Image added': 'Imagem adicionada corretamente',
|
||||
ErrIniSize: 'Arquivo supera o tamanho maximo de protocolo em php.ini',
|
||||
ErrFormSize: 'Arquivo supera o tamanho maximo de protocolo em HTML form',
|
||||
ErrPartial: 'Arquivo subido parcialmente',
|
||||
ErrNoFile: 'Nenhum arquivo subido',
|
||||
ErrNoTmpDir: 'Falta a pasta de arquivo temporal',
|
||||
ErrCantWrite: 'Erro ao gravar arquivo no disco',
|
||||
ErrExtension: 'Erro de extensão do arquivo',
|
||||
ErrDefault: 'Erro desconhecido ao subir arquivo',
|
||||
'Sync complete': 'Sincronização completa'
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title><%= productName %></title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="<%= productDescription %>">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
|
||||
<link rel="icon" type="image/ico" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<!-- DO NOT touch the following DIV -->
|
||||
<div id="q-app"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,187 @@
|
|||
import { JsonConnection } from '../vn/json-connection';
|
||||
import { ResultSet } from './result-set';
|
||||
|
||||
/**
|
||||
* Simulates a connection to a database by making asynchronous requests to a
|
||||
* remote REST service that returns the results in JSON format.
|
||||
* Using this class can perform any operation that can be done with a database,
|
||||
* like open/close a connection or selecion/updating queries.
|
||||
*
|
||||
* Warning! You should set a well defined dababase level privileges to use this
|
||||
* class or you could have a serious security hole in you application becasuse
|
||||
* the user can send any statement to the server. For example: DROP DATABASE
|
||||
*/
|
||||
const Flag = {
|
||||
NOT_NULL: 1,
|
||||
PRI_KEY: 2,
|
||||
AI: 512 | 2 | 1
|
||||
};
|
||||
|
||||
const Type = {
|
||||
BOOLEAN: 1,
|
||||
INTEGER: 3,
|
||||
DOUBLE: 4,
|
||||
STRING: 5,
|
||||
DATE: 8,
|
||||
DATE_TIME: 9
|
||||
};
|
||||
|
||||
export class Connection extends JsonConnection {
|
||||
static Flag = Flag;
|
||||
static Type = Type;
|
||||
|
||||
/**
|
||||
* Runs a SQL query on the database.
|
||||
*
|
||||
* @param {String} sql The SQL statement
|
||||
* @return {ResultSet} The result
|
||||
*/
|
||||
async execSql(sql) {
|
||||
const json = await this.send('core/query', { sql });
|
||||
const results = [];
|
||||
let err;
|
||||
|
||||
if (json) {
|
||||
try {
|
||||
if (json && json instanceof Array) {
|
||||
for (let i = 0; i < json.length; i++) {
|
||||
if (json[i] !== true) {
|
||||
const rows = json[i].data;
|
||||
const columns = json[i].columns;
|
||||
|
||||
const data = new Array(rows.length);
|
||||
results.push({
|
||||
data,
|
||||
columns,
|
||||
tables: json[i].tables
|
||||
});
|
||||
|
||||
for (let j = 0; j < rows.length; j++) {
|
||||
const row = (data[j] = {});
|
||||
for (let k = 0; k < columns.length; k++) {
|
||||
row[columns[k].name] = rows[j][k];
|
||||
}
|
||||
}
|
||||
|
||||
for (let j = 0; j < columns.length; j++) {
|
||||
let castFunc = null;
|
||||
const col = columns[j];
|
||||
|
||||
switch (col.type) {
|
||||
case Type.DATE:
|
||||
case Type.DATE_TIME:
|
||||
case Type.TIMESTAMP:
|
||||
castFunc = this.valueToDate;
|
||||
break;
|
||||
}
|
||||
|
||||
if (castFunc !== null) {
|
||||
if (col.def != null) {
|
||||
col.def = castFunc(col.def);
|
||||
}
|
||||
|
||||
for (let k = 0; k < data.length; k++) {
|
||||
if (data[k][col.name] != null) {
|
||||
data[k][col.name] = castFunc(
|
||||
data[k][col.name]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
results.push(json[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
}
|
||||
|
||||
return new ResultSet(results, err);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a query on the database.
|
||||
*
|
||||
* @param {String} query The SQL statement
|
||||
* @param {Object} params The query params
|
||||
* @return {ResultSet} The result
|
||||
*/
|
||||
async execQuery(query, params) {
|
||||
const sql = query.replace(/#\w+/g, key => {
|
||||
const value = params[key.substring(1)];
|
||||
return value ? this.renderValue(value) : key;
|
||||
});
|
||||
|
||||
return await this.execSql(sql);
|
||||
}
|
||||
|
||||
async query(query, params) {
|
||||
const res = await this.execQuery(query, params);
|
||||
return res.fetchData();
|
||||
}
|
||||
|
||||
async getObject(query, params) {
|
||||
const res = await this.execQuery(query, params);
|
||||
return res.fetchObject();
|
||||
}
|
||||
|
||||
async getValue(query, params) {
|
||||
const res = await this.execQuery(query, params);
|
||||
return res.fetchValue();
|
||||
}
|
||||
|
||||
renderValue(v) {
|
||||
switch (typeof v) {
|
||||
case 'number':
|
||||
return v;
|
||||
case 'boolean':
|
||||
return v ? 'TRUE' : 'FALSE';
|
||||
case 'string':
|
||||
return "'" + v.replace(this.regexp, this.replaceFunc) + "'";
|
||||
default:
|
||||
if (v instanceof Date) {
|
||||
if (!isNaN(v.getTime())) {
|
||||
const unixTime = parseInt(fixTz(v).getTime() / 1000);
|
||||
return 'DATE(FROM_UNIXTIME(' + unixTime + '))';
|
||||
} else {
|
||||
return '0000-00-00';
|
||||
}
|
||||
} else {
|
||||
return 'NULL';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a value to date.
|
||||
*/
|
||||
valueToDate(value) {
|
||||
return fixTz(new Date(value));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Read time zone from db configuration
|
||||
const tz = { timeZone: 'Europe/Madrid' };
|
||||
const isLocal =
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone === tz.timeZone;
|
||||
|
||||
function fixTz(date) {
|
||||
if (isLocal) return date;
|
||||
|
||||
const localDate = new Date(date.toLocaleString('en-US', tz));
|
||||
const hasTime =
|
||||
localDate.getHours() ||
|
||||
localDate.getMinutes() ||
|
||||
localDate.getSeconds() ||
|
||||
localDate.getMilliseconds();
|
||||
|
||||
if (!hasTime) {
|
||||
date.setHours(date.getHours() + 12);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
import { Result } from './result';
|
||||
|
||||
/**
|
||||
* This class stores the database results.
|
||||
*/
|
||||
export class ResultSet {
|
||||
results = null;
|
||||
error = null;
|
||||
|
||||
/**
|
||||
* Initilizes the resultset object.
|
||||
*/
|
||||
constructor(results, error) {
|
||||
this.results = results;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the query error.
|
||||
*
|
||||
* @return {Db.Err} the error or null if no errors hapened
|
||||
*/
|
||||
getError() {
|
||||
return this.error;
|
||||
}
|
||||
|
||||
fetch() {
|
||||
if (this.error) {
|
||||
throw this.error;
|
||||
}
|
||||
|
||||
if (this.results !== null && this.results.length > 0) {
|
||||
return this.results.shift();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetchs the next result from the resultset.
|
||||
*
|
||||
* @return {Db.Result} the result or %null if error or there are no more results
|
||||
*/
|
||||
fetchResult() {
|
||||
const result = this.fetch();
|
||||
|
||||
if (result !== null) {
|
||||
if (result.data instanceof Array) {
|
||||
return new Result(result);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetchs the first row object from the next resultset.
|
||||
*
|
||||
* @return {Array} the row if success, %null otherwise
|
||||
*/
|
||||
fetchObject() {
|
||||
const result = this.fetch();
|
||||
|
||||
if (
|
||||
result !== null &&
|
||||
result.data instanceof Array &&
|
||||
result.data.length > 0
|
||||
) {
|
||||
return result.data[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetchs data from the next resultset.
|
||||
*
|
||||
* @return {Array} the data
|
||||
*/
|
||||
fetchData() {
|
||||
const result = this.fetch();
|
||||
|
||||
if (result !== null && result.data instanceof Array) {
|
||||
return result.data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetchs the first row and column value from the next resultset.
|
||||
*
|
||||
* @return {Object} the value if success, %null otherwise
|
||||
*/
|
||||
fetchValue() {
|
||||
const row = this.fetchRow();
|
||||
if (row instanceof Array && row.length > 0) {
|
||||
return row[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetchs the first row from the next resultset.
|
||||
*
|
||||
* @return {Array} the row if success, %null otherwise
|
||||
*/
|
||||
fetchRow() {
|
||||
const result = this.fetch();
|
||||
if (
|
||||
result !== null &&
|
||||
result.data instanceof Array &&
|
||||
result.data.length > 0
|
||||
) {
|
||||
const object = result.data[0];
|
||||
const row = new Array(result.columns.length);
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
row[i] = object[result.columns[i].name];
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* This class stores a database result.
|
||||
*/
|
||||
export class Result {
|
||||
/**
|
||||
* Initilizes the result object.
|
||||
*/
|
||||
constructor (result) {
|
||||
this.data = result.data
|
||||
this.tables = result.tables
|
||||
this.columns = result.columns
|
||||
this.row = -1
|
||||
|
||||
if (this.columns) {
|
||||
this.columnMap = {}
|
||||
|
||||
for (let i = 0; i < this.columns.length; i++) {
|
||||
const col = this.columns[i]
|
||||
col.index = i
|
||||
this.columnMap[col.name] = col
|
||||
}
|
||||
} else {
|
||||
this.columnMap = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from de result.
|
||||
*
|
||||
* @param {String} columnName The column name
|
||||
* @return {Object} The cell value
|
||||
*/
|
||||
get (columnName) {
|
||||
return this.data[this.row][columnName]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a row.
|
||||
*
|
||||
* @return {Object} The cell value
|
||||
*/
|
||||
getObject () {
|
||||
return this.data[this.row]
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the result iterator.
|
||||
*/
|
||||
reset () {
|
||||
this.row = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the internal iterator to the next row.
|
||||
*/
|
||||
next () {
|
||||
this.row++
|
||||
|
||||
if (this.row >= this.data.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
const sanitizeValue = value => {
|
||||
if (typeof value === 'string') {
|
||||
return `'${value}'`;
|
||||
} else if (value === null) {
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export const generateUpdateSqlQuery = (
|
||||
schema,
|
||||
table,
|
||||
pks,
|
||||
columnsUpdated,
|
||||
formData
|
||||
) => {
|
||||
const setClauses = columnsUpdated
|
||||
.map(colName => `${colName} = ${sanitizeValue(formData[colName])}`)
|
||||
.join(', ');
|
||||
const whereClause = Object.keys(pks)
|
||||
.map(pk => `${pk} = ${pks[pk]}`)
|
||||
.join(' AND ');
|
||||
|
||||
return `
|
||||
START TRANSACTION;
|
||||
UPDATE ${schema}.${table} SET ${setClauses} WHERE (${whereClause});
|
||||
SELECT ${columnsUpdated.join(', ')} FROM ${schema}.${table} WHERE (${whereClause});
|
||||
COMMIT;
|
||||
`;
|
||||
};
|
||||
|
||||
export const generateInsertSqlQuery = (
|
||||
schema,
|
||||
table,
|
||||
formData,
|
||||
columnsUpdated,
|
||||
createModelDefault
|
||||
) => {
|
||||
const columns = createModelDefault.field
|
||||
? [createModelDefault.field, ...columnsUpdated].join(', ')
|
||||
: columnsUpdated.join(', ');
|
||||
|
||||
const values = createModelDefault.value
|
||||
? [
|
||||
createModelDefault.value,
|
||||
...columnsUpdated.map(colName => sanitizeValue(formData[colName]))
|
||||
].join(', ')
|
||||
: columnsUpdated
|
||||
.map(colName => sanitizeValue(formData[colName]))
|
||||
.join(', ');
|
||||
return `
|
||||
START TRANSACTION;
|
||||
INSERT INTO ${schema}.${table} (${columns}) VALUES (${values});
|
||||
SELECT id, ${columnsUpdated.join(', ')} FROM ${schema}.${table} WHERE (id = LAST_INSERT_ID());
|
||||
COMMIT;
|
||||
`;
|
||||
};
|
|
@ -0,0 +1,208 @@
|
|||
import { VnObject } from './object';
|
||||
import { JsonException } from './json-exception';
|
||||
|
||||
/**
|
||||
* Handler for JSON rest connections.
|
||||
*/
|
||||
export class JsonConnection extends VnObject {
|
||||
_connected = false;
|
||||
_requestsCount = 0;
|
||||
token = null;
|
||||
interceptors = [];
|
||||
errorInterceptor = null;
|
||||
|
||||
use(fn) {
|
||||
this.interceptors.push(fn);
|
||||
}
|
||||
|
||||
useErrorInterceptor(fn) {
|
||||
this.errorInterceptor = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the specified REST service with the given params and calls
|
||||
* the callback when response is received.
|
||||
*
|
||||
* @param {String} url The service path
|
||||
* @param {Object} params The params to pass to the service
|
||||
* @return {Object} The parsed JSON response
|
||||
*/
|
||||
async send(url, params) {
|
||||
if (!params) params = {};
|
||||
params.srv = `json:${url}`;
|
||||
return this.sendWithUrl('POST', '.', params);
|
||||
}
|
||||
|
||||
async sendForm(form) {
|
||||
const params = {};
|
||||
const elements = form.elements;
|
||||
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
if (elements[i].name) {
|
||||
params[elements[i].name] = elements[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
return this.sendWithUrl('POST', form.action, params);
|
||||
}
|
||||
|
||||
async sendFormMultipart(form) {
|
||||
return this.request({
|
||||
method: 'POST',
|
||||
url: form.action,
|
||||
data: new FormData(form)
|
||||
});
|
||||
}
|
||||
|
||||
async sendFormData(formData) {
|
||||
return this.request({
|
||||
method: 'POST',
|
||||
url: '',
|
||||
data: formData
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when REST response is received.
|
||||
*/
|
||||
async sendWithUrl(method, url, params) {
|
||||
const urlParams = new URLSearchParams();
|
||||
for (const key in params) {
|
||||
if (params[key] != null) {
|
||||
urlParams.set(key, params[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return this.request({
|
||||
method,
|
||||
url,
|
||||
data: urlParams.toString(),
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async request(config) {
|
||||
const request = new XMLHttpRequest();
|
||||
request.open(config.method, config.url, true);
|
||||
|
||||
for (const fn of this.interceptors) {
|
||||
config = fn(config);
|
||||
}
|
||||
|
||||
const headers = config.headers;
|
||||
if (headers) {
|
||||
for (const header in headers) {
|
||||
request.setRequestHeader(header, headers[header]);
|
||||
}
|
||||
}
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
request.onreadystatechange = () =>
|
||||
this._onStateChange(request, resolve, reject);
|
||||
});
|
||||
|
||||
request.send(config.data);
|
||||
|
||||
this._requestsCount++;
|
||||
|
||||
if (this._requestsCount === 1) {
|
||||
this.emit('loading-changed', true);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
_onStateChange(request, resolve, reject) {
|
||||
if (request.readyState !== 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._requestsCount--;
|
||||
|
||||
if (this._requestsCount === 0) {
|
||||
this.emit('loading-changed', false);
|
||||
}
|
||||
|
||||
let data = null;
|
||||
let error = null;
|
||||
try {
|
||||
if (request.status === 0) {
|
||||
const err = new JsonException();
|
||||
err.message =
|
||||
'The server does not respond, please check your Internet connection';
|
||||
err.statusCode = request.status;
|
||||
throw err;
|
||||
}
|
||||
|
||||
let contentType = null;
|
||||
|
||||
try {
|
||||
contentType = request
|
||||
.getResponseHeader('Content-Type')
|
||||
.split(';')[0]
|
||||
.trim();
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
}
|
||||
|
||||
if (contentType !== 'application/json') {
|
||||
const err = new JsonException();
|
||||
err.message = request.statusText;
|
||||
err.statusCode = request.status;
|
||||
throw err;
|
||||
}
|
||||
|
||||
let json;
|
||||
let jsData;
|
||||
|
||||
if (request.responseText) {
|
||||
json = JSON.parse(request.responseText);
|
||||
}
|
||||
if (json) {
|
||||
jsData = json.data || json;
|
||||
}
|
||||
|
||||
if (request.status >= 200 && request.status < 300) {
|
||||
data = jsData;
|
||||
} else {
|
||||
let exception = jsData.exception;
|
||||
|
||||
const err = new JsonException();
|
||||
err.statusCode = request.status;
|
||||
|
||||
if (exception) {
|
||||
exception = exception
|
||||
.replace(/\\/g, '.')
|
||||
.replace(/Exception$/, '')
|
||||
.replace(/^Vn\.Web\./, '');
|
||||
|
||||
err.exception = exception;
|
||||
err.message = jsData.message;
|
||||
err.code = jsData.code;
|
||||
err.file = jsData.file;
|
||||
err.line = jsData.line;
|
||||
err.trace = jsData.trace;
|
||||
} else {
|
||||
err.message = request.statusText;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
} catch (e) {
|
||||
data = null;
|
||||
error = e;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
if (this.errorInterceptor) {
|
||||
this.errorInterceptor(error);
|
||||
}
|
||||
this.emit('error', error);
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* This class stores the database errors.
|
||||
*/
|
||||
export class JsonException {
|
||||
constructor (exception, message, code, file, line, trace, statucCode) {
|
||||
this.name = 'JsonException'
|
||||
this.exception = exception
|
||||
this.message = message
|
||||
this.code = code
|
||||
this.file = file
|
||||
this.line = line
|
||||
this.trace = trace
|
||||
this.statusCode = statucCode
|
||||
}
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
/**
|
||||
* The main base class. Manages the signal system. Objects based on this class
|
||||
* can be instantiated declaratively using XML.
|
||||
*/
|
||||
export class VnObject {
|
||||
/**
|
||||
* Tag to be used when the class instance is defined via XML. All classes
|
||||
* must define this attribute, even if it is not used.
|
||||
*/
|
||||
static Tag = 'vn-object'
|
||||
|
||||
/**
|
||||
* Class public properties.
|
||||
*/
|
||||
static Properties = {}
|
||||
|
||||
/*
|
||||
* Reference count.
|
||||
*/
|
||||
_refCount = 1
|
||||
|
||||
/*
|
||||
* Signal handlers data.
|
||||
*/
|
||||
_thisArg = null
|
||||
|
||||
/**
|
||||
* Initializes the object and sets all properties passed to the class
|
||||
* constructor.
|
||||
*
|
||||
* @param {Object} props The properties passed to the contructor
|
||||
*/
|
||||
constructor (props) {
|
||||
this.setProperties(props)
|
||||
}
|
||||
|
||||
initialize (props) {
|
||||
this.setProperties(props)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a group of object properties.
|
||||
*
|
||||
* @param {Object} props Properties
|
||||
*/
|
||||
setProperties (props) {
|
||||
for (const prop in props) {
|
||||
this[prop] = props[prop]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the object reference count.
|
||||
*/
|
||||
ref () {
|
||||
this._refCount++
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases the object reference count.
|
||||
*/
|
||||
unref () {
|
||||
this._refCount--
|
||||
|
||||
if (this._refCount === 0) {
|
||||
this._destroy()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from @Vn.Builder when it finds a custom tag as a child of the
|
||||
* element.
|
||||
*
|
||||
* @param {Vn.Scope} scope The scope instance
|
||||
* @param {Node} node The custom tag child nodes
|
||||
*/
|
||||
loadXml () {}
|
||||
|
||||
/**
|
||||
* Called from @Vn.Builder when it finds a a child tag that isn't
|
||||
* associated to any property.
|
||||
*
|
||||
* @param {Object} child The child object instance
|
||||
*/
|
||||
appendChild () {}
|
||||
|
||||
/**
|
||||
* Conects a signal with a function.
|
||||
*
|
||||
* @param {string} id The signal identifier
|
||||
* @param {function} callback The callback
|
||||
* @param {Object} instance The instance
|
||||
*/
|
||||
on (id, callback, instance) {
|
||||
if (!(callback instanceof Function)) {
|
||||
console.warn("Vn.Object: Invalid callback for signal '%s'", id)
|
||||
return
|
||||
}
|
||||
|
||||
this._signalInit()
|
||||
let callbacks = this._thisArg.signals[id]
|
||||
|
||||
if (!callbacks) {
|
||||
callbacks = this._thisArg.signals[id] = []
|
||||
}
|
||||
|
||||
callbacks.push({
|
||||
blocked: false,
|
||||
callback,
|
||||
instance
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks/Unlocks a signal emission to the specified object.
|
||||
*
|
||||
* @param {string} id The signal identifier
|
||||
* @param {function} callback The callback
|
||||
* @param {boolean} block %true for lock the signal, %false for unlock
|
||||
*/
|
||||
blockSignal (id, callback, block, instance) {
|
||||
if (!this._thisArg) {
|
||||
return
|
||||
}
|
||||
|
||||
const callbacks = this._thisArg.signals[id]
|
||||
|
||||
if (!callbacks) {
|
||||
return
|
||||
}
|
||||
|
||||
for (let i = 0; i < callbacks.length; i++) {
|
||||
if (
|
||||
callbacks[i].callback === callback &&
|
||||
callbacks[i].instance === instance
|
||||
) {
|
||||
callbacks[i].blocked = block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a signal in the object.
|
||||
*
|
||||
* @param {string} id The signal identifier
|
||||
*/
|
||||
emit (id) {
|
||||
if (!this._thisArg) {
|
||||
return
|
||||
}
|
||||
|
||||
const callbacks = this._thisArg.signals[id]
|
||||
|
||||
if (!callbacks) {
|
||||
return
|
||||
}
|
||||
|
||||
const callbackArgs = []
|
||||
callbackArgs.push(this)
|
||||
|
||||
for (let i = 1; i < arguments.length; i++) {
|
||||
callbackArgs.push(arguments[i])
|
||||
}
|
||||
|
||||
for (let i = 0; i < callbacks.length; i++) {
|
||||
if (!callbacks[i].blocked) {
|
||||
callbacks[i].callback.apply(callbacks[i].instance, callbackArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects a signal from current object.
|
||||
*
|
||||
* @param {string} id The signal identifier
|
||||
* @param {function} callback The connected callback
|
||||
* @param {Object} instance The instance
|
||||
*/
|
||||
disconnect (id, callback, instance) {
|
||||
if (!this._thisArg) {
|
||||
return
|
||||
}
|
||||
|
||||
const callbacks = this._thisArg.signals[id]
|
||||
|
||||
if (callbacks) {
|
||||
for (let i = callbacks.length; i--;) {
|
||||
if (
|
||||
callbacks[i].callback === callback &&
|
||||
callbacks[i].instance === instance
|
||||
) {
|
||||
callbacks.splice(i, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects all signals for the given instance.
|
||||
*
|
||||
* @param {Object} instance The instance
|
||||
*/
|
||||
disconnectByInstance (instance) {
|
||||
if (!this._thisArg) {
|
||||
return
|
||||
}
|
||||
|
||||
const signals = this._thisArg.signals
|
||||
|
||||
for (const signalId in signals) {
|
||||
const callbacks = signals[signalId]
|
||||
|
||||
if (callbacks) {
|
||||
for (let i = callbacks.length; i--;) {
|
||||
if (callbacks[i].instance === instance) {
|
||||
callbacks.splice(i, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the object, this method should only be called before losing
|
||||
* the last reference to the object. It can be overwritten by child classes
|
||||
* but should always call the parent method.
|
||||
*/
|
||||
_destroy () {
|
||||
if (!this._thisArg) {
|
||||
return
|
||||
}
|
||||
|
||||
const links = this._thisArg.links
|
||||
|
||||
for (const key in links) {
|
||||
this._unlink(links[key])
|
||||
}
|
||||
|
||||
this._thisArg = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Links the object with another object.
|
||||
*
|
||||
* @param {Object} prop The linked property
|
||||
* @param {Object} handlers The object events to listen with
|
||||
*/
|
||||
link (prop, handlers) {
|
||||
this._signalInit()
|
||||
const links = this._thisArg.links
|
||||
|
||||
for (const key in prop) {
|
||||
const newObject = prop[key]
|
||||
const oldObject = this[key]
|
||||
|
||||
if (oldObject) {
|
||||
this._unlink(oldObject)
|
||||
}
|
||||
|
||||
this[key] = newObject
|
||||
|
||||
if (newObject) {
|
||||
links[key] = newObject.ref()
|
||||
|
||||
for (const signal in handlers) {
|
||||
newObject.on(signal, handlers[signal], this)
|
||||
}
|
||||
} else if (oldObject) {
|
||||
links[key] = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_unlink (object) {
|
||||
if (!object) return
|
||||
object.disconnectByInstance(this)
|
||||
object.unref()
|
||||
}
|
||||
|
||||
_signalInit () {
|
||||
if (!this._thisArg) {
|
||||
this._thisArg = {
|
||||
signals: {},
|
||||
links: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<template>
|
||||
<QLayout
|
||||
id="bg"
|
||||
class="fullscreen row justify-center items-center layout-view scroll"
|
||||
>
|
||||
<div class="column q-pa-md row items-center justify-center">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition>
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</QLayout>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#bg {
|
||||
background: white;
|
||||
}
|
||||
.column {
|
||||
width: 270px;
|
||||
overflow: hidden;
|
||||
|
||||
& > * {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'LoginLayout'
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,224 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { useUserStore } from 'stores/user';
|
||||
import { useAppStore } from 'stores/app';
|
||||
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
const hiddenMenuItems = new Set(['Reports']);
|
||||
|
||||
const refreshContentKey = ref(0);
|
||||
const { user, supplantedUser } = storeToRefs(userStore);
|
||||
const { menuEssentialLinks, title, subtitle, useRightDrawer, rightDrawerOpen } =
|
||||
storeToRefs(appStore);
|
||||
const actions = ref(null);
|
||||
const leftDrawerOpen = ref(false);
|
||||
const filteredMenuItems = computed(() =>
|
||||
menuEssentialLinks.value.filter(
|
||||
item => !hiddenMenuItems.has(item.description)
|
||||
)
|
||||
);
|
||||
const toggleLeftDrawer = () => {
|
||||
leftDrawerOpen.value = !leftDrawerOpen.value;
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await appStore.loadConfig();
|
||||
await appStore.getMenuLinks();
|
||||
appStore.isHeaderMounted = true;
|
||||
});
|
||||
|
||||
const logout = async () => {
|
||||
await userStore.logout();
|
||||
router.push('/login');
|
||||
};
|
||||
|
||||
const logoutSupplantedUser = async () => {
|
||||
await userStore.logoutSupplantedUser();
|
||||
await appStore.getMenuLinks();
|
||||
refreshContentKey.value++;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QLayout view="lHh Lpr lFf">
|
||||
<QHeader>
|
||||
<QToolbar>
|
||||
<QBtn
|
||||
flat
|
||||
dense
|
||||
round
|
||||
icon="menu"
|
||||
aria-label="Menu"
|
||||
@click="toggleLeftDrawer"
|
||||
/>
|
||||
<QToolbarTitle>
|
||||
{{ title }}
|
||||
<div v-if="subtitle" class="subtitle text-caption">
|
||||
{{ subtitle }}
|
||||
</div>
|
||||
</QToolbarTitle>
|
||||
<div id="actions" ref="actions" class="flex items-center"></div>
|
||||
<QBtn
|
||||
v-if="useRightDrawer"
|
||||
@click="rightDrawerOpen = !rightDrawerOpen"
|
||||
aria-label="Menu"
|
||||
flat
|
||||
dense
|
||||
round
|
||||
>
|
||||
<QIcon name="menu" />
|
||||
</QBtn>
|
||||
</QToolbar>
|
||||
</QHeader>
|
||||
<QDrawer v-model="leftDrawerOpen" :width="250" show-if-above>
|
||||
<QToolbar class="logo">
|
||||
<img src="statics/logo-dark.svg" />
|
||||
</QToolbar>
|
||||
<div class="user-info">
|
||||
<div>
|
||||
<span id="user-name">{{ user?.nickname }}</span>
|
||||
<QBtn flat icon="logout" alt="_Exit" @click="logout()" />
|
||||
</div>
|
||||
<div v-if="supplantedUser" id="supplant" class="supplant">
|
||||
<span id="supplanted">
|
||||
{{ supplantedUser?.nickname }}
|
||||
</span>
|
||||
<QBtn
|
||||
flat
|
||||
icon="logout"
|
||||
alt="_Exit"
|
||||
@click="logoutSupplantedUser()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<QList v-for="item in filteredMenuItems" :key="item.id">
|
||||
<QItem v-if="!item.childs" :to="`/${item.path}`">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
$t(`menuTitles.${item.description}`)
|
||||
}}</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QExpansionItem
|
||||
v-if="item.childs"
|
||||
:label="$t(`menuTitles.${item.description}`)"
|
||||
expand-separator
|
||||
>
|
||||
<QList>
|
||||
<QItem
|
||||
v-for="subitem in item.childs"
|
||||
:key="subitem.id"
|
||||
:to="`/${subitem.path}`"
|
||||
class="q-pl-lg"
|
||||
>
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
{{
|
||||
$t(`menuTitles.${subitem.description}`)
|
||||
}}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QExpansionItem>
|
||||
</QList>
|
||||
</QDrawer>
|
||||
<QPageContainer :key="refreshContentKey">
|
||||
<router-view />
|
||||
</QPageContainer>
|
||||
</QLayout>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.q-toolbar {
|
||||
min-height: 64px;
|
||||
}
|
||||
.logo {
|
||||
background-color: $primary;
|
||||
justify-content: center;
|
||||
|
||||
& > img {
|
||||
width: 160px;
|
||||
}
|
||||
}
|
||||
.user-info {
|
||||
margin: 25px;
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
border: 1px solid #eaeaea;
|
||||
|
||||
& > span {
|
||||
padding: 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
}
|
||||
.q-btn {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 9px;
|
||||
border-radius: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: #1a1a1a;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
&.supplant {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import 'src/css/responsive';
|
||||
|
||||
.q-drawer {
|
||||
.q-item {
|
||||
padding-left: 38px;
|
||||
}
|
||||
.q-list .q-list .q-item {
|
||||
padding-left: 50px;
|
||||
}
|
||||
}
|
||||
.q-page-container > * {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
@include mobile {
|
||||
#actions {
|
||||
.q-btn {
|
||||
border-radius: 50%;
|
||||
padding: 10px;
|
||||
|
||||
&__content {
|
||||
& > .q-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
& > .block {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
visitor: Visitor
|
||||
es-ES:
|
||||
visitor: Visitante
|
||||
</i18n>
|
|
@ -0,0 +1,117 @@
|
|||
import { i18n } from 'src/boot/i18n';
|
||||
import { date as qdate, format } from 'quasar';
|
||||
import { useAppStore } from 'stores/app';
|
||||
const { pad } = format;
|
||||
|
||||
export function currency(val) {
|
||||
return typeof val === 'number' ? val.toFixed(2) + '€' : val;
|
||||
}
|
||||
|
||||
export function date(val, format = 'YYYY-MM-DD') {
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val);
|
||||
}
|
||||
return qdate.formatDate(val, format, i18n.global.tm('date'));
|
||||
}
|
||||
|
||||
export const formatDate = (timeStamp, format = 'YYYY-MM-DD') => {
|
||||
if (!timeStamp) return '';
|
||||
const appStore = useAppStore();
|
||||
return qdate.formatDate(timeStamp, format, appStore.localeDates);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Date} timeStamp - La marca de tiempo que se va a formatear. Si no se proporciona, la función devolverá una cadena vacía.
|
||||
* @param {Object} options - Un objeto que contiene las opciones de formato.
|
||||
* @param {boolean} options.showTime - Indica si se debe mostrar la hora en el formato de la fecha.
|
||||
* @param {boolean} options.showSeconds - Indica si se deben mostrar los segundos en el formato de la hora. Solo se aplica si showTime es true.
|
||||
* @param {boolean} options.shortDay - Indica si se debe usar una versión corta del día (por ejemplo, "Mon" en lugar de "Monday").
|
||||
* @returns {string} La fecha formateada como un título.
|
||||
*/
|
||||
export const formatDateTitle = (
|
||||
timeStamp,
|
||||
options = {
|
||||
showTime: false,
|
||||
showSeconds: false,
|
||||
shortDay: false,
|
||||
shortMonth: false,
|
||||
includeOfString: true
|
||||
}
|
||||
) => {
|
||||
if (!timeStamp) return '';
|
||||
const { t } = i18n.global;
|
||||
|
||||
const timeFormat = options.showTime
|
||||
? options.showSeconds
|
||||
? ` [${t('at')}] HH:mm:ss`
|
||||
: ` [${t('at')}] HH:mm`
|
||||
: '';
|
||||
const day = options.shortDay ? 'dd' : 'dddd';
|
||||
const month = options.shortMonth ? 'MMM' : 'MMMM';
|
||||
const ofString = options.includeOfString ? ` [${t('of')}] ` : '';
|
||||
|
||||
const formatString = `${day}, D${ofString} ${month}${ofString} YYYY${timeFormat}`;
|
||||
|
||||
const formattedString = formatDate(timeStamp, formatString);
|
||||
return formattedString;
|
||||
};
|
||||
|
||||
export function relDate(val) {
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val);
|
||||
}
|
||||
|
||||
const dif = qdate.getDateDiff(new Date(), val, 'days');
|
||||
let day;
|
||||
|
||||
switch (dif) {
|
||||
case 0:
|
||||
day = 'today';
|
||||
break;
|
||||
case 1:
|
||||
day = 'yesterday';
|
||||
break;
|
||||
case -1:
|
||||
day = 'tomorrow';
|
||||
break;
|
||||
}
|
||||
|
||||
if (day) {
|
||||
day = i18n.global.t(day);
|
||||
} else {
|
||||
if (dif > 0 && dif <= 7) {
|
||||
day = qdate.formatDate(val, 'ddd', i18n.global.tm('date'));
|
||||
} else {
|
||||
day = qdate.formatDate(val, 'ddd, MMMM Do', i18n.global.tm('date'));
|
||||
}
|
||||
}
|
||||
|
||||
return day;
|
||||
}
|
||||
|
||||
export function relTime(val) {
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val);
|
||||
}
|
||||
return relDate(val) + ' ' + qdate.formatDate(val, 'H:mm:ss');
|
||||
}
|
||||
|
||||
export function elapsedTime(val) {
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val);
|
||||
}
|
||||
const now = new Date().getTime();
|
||||
val = Math.floor((now - val.getTime()) / 1000);
|
||||
|
||||
const hours = Math.floor(val / 3600);
|
||||
val -= hours * 3600;
|
||||
const minutes = Math.floor(val / 60);
|
||||
val -= minutes * 60;
|
||||
const seconds = val;
|
||||
|
||||
return `${pad(hours, 2)}:${pad(minutes, 2)}:${pad(seconds, 2)}`;
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
<script setup>
|
||||
import { ref, inject, onMounted, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnForm from 'src/components/common/VnForm.vue';
|
||||
import ChangePasswordForm from 'src/components/ui/ChangePasswordForm.vue';
|
||||
|
||||
import { useUserStore } from 'stores/user';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
const { user } = storeToRefs(userStore);
|
||||
|
||||
const vnFormRef = ref(null);
|
||||
const vnFormRef2 = ref(null);
|
||||
const changePasswordFormDialog = ref(null);
|
||||
const showChangePasswordForm = ref(false);
|
||||
const langOptions = ref([]);
|
||||
const pks = computed(() => ({ id: userStore?.user?.id }));
|
||||
const fetchConfigDataSql = {
|
||||
query: `
|
||||
SELECT u.id, u.name, u.email, u.nickname,
|
||||
u.lang, c.isToBeMailed, c.id clientFk
|
||||
FROM account.myUser u
|
||||
LEFT JOIN myClient c
|
||||
ON u.id = c.id`,
|
||||
params: {}
|
||||
};
|
||||
|
||||
const fetchLanguagesSql = async () => {
|
||||
try {
|
||||
const data = await jApi.query(
|
||||
'SELECT code, name FROM language WHERE isActive'
|
||||
);
|
||||
langOptions.value = data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const updateUserNickname = async nickname => {
|
||||
try {
|
||||
await vnFormRef.value.submit();
|
||||
user.value.nickname = nickname;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const formatMailData = data => {
|
||||
data.isToBeMailed = Boolean(data.isToBeMailed);
|
||||
};
|
||||
|
||||
onMounted(() => fetchLanguagesSql());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage>
|
||||
<QPage class="q-pa-md flex justify-center">
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QBtn
|
||||
:label="t('addresses')"
|
||||
icon="location_on"
|
||||
rounded
|
||||
no-caps
|
||||
:to="{ name: 'addressesList' }"
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('changePassword')"
|
||||
icon="lock_reset"
|
||||
rounded
|
||||
no-caps
|
||||
@click="showChangePasswordForm = true"
|
||||
/>
|
||||
</Teleport>
|
||||
<VnForm
|
||||
ref="vnFormRef"
|
||||
:title="t('personalInformation')"
|
||||
:fetch-form-data-sql="fetchConfigDataSql"
|
||||
:pks="pks"
|
||||
table="myUser"
|
||||
schema="account"
|
||||
:default-actions="false"
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnInput
|
||||
v-model="data.name"
|
||||
:label="t('name')"
|
||||
disable
|
||||
:clearable="false"
|
||||
/>
|
||||
<VnInput
|
||||
v-model="data.email"
|
||||
:label="t('email')"
|
||||
@keyup.enter="vnFormRef.submit()"
|
||||
@blur="vnFormRef.submit()"
|
||||
/>
|
||||
<VnInput
|
||||
v-model="data.nickname"
|
||||
:label="t('nickname')"
|
||||
@keyup.enter="updateUserNickname(data.nickname)"
|
||||
@blur="updateUserNickname(data.nickname)"
|
||||
/>
|
||||
<VnSelect
|
||||
v-model="data.lang"
|
||||
:label="t('lang')"
|
||||
option-label="name"
|
||||
option-value="code"
|
||||
:options="langOptions"
|
||||
@update:model-value="
|
||||
userStore.updateUserLang(data.lang)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template #extraForm>
|
||||
<VnForm
|
||||
class="no-form-container q-mt-md"
|
||||
ref="vnFormRef2"
|
||||
:pks="pks"
|
||||
table="myClient"
|
||||
schema="hedera"
|
||||
:fetch-form-data-sql="fetchConfigDataSql"
|
||||
:default-actions="false"
|
||||
@on-data-fetched="$event => formatMailData($event)"
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<QCheckbox
|
||||
v-model="data.isToBeMailed"
|
||||
:label="t('isToBeMailed')"
|
||||
:toggle-indeterminate="false"
|
||||
@update:model-value="vnFormRef2.submit()"
|
||||
dense
|
||||
/>
|
||||
</template>
|
||||
</VnForm>
|
||||
</template>
|
||||
</VnForm>
|
||||
</QPage>
|
||||
<QDialog
|
||||
ref="changePasswordFormDialog"
|
||||
v-model="showChangePasswordForm"
|
||||
>
|
||||
<ChangePasswordForm
|
||||
@on-password-changed="showChangePasswordForm = false"
|
||||
/>
|
||||
</QDialog>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
personalInformation: Personal Information
|
||||
isToBeMailed: Receive invoices by email
|
||||
name: Name
|
||||
email: Email
|
||||
nickname: Display name
|
||||
lang: Language
|
||||
receiveInvoicesByMail: Receive invoices by mail
|
||||
addresses: Addresses
|
||||
changePassword: Change password
|
||||
es-ES:
|
||||
personalInformation: Datos personales
|
||||
isToBeMailed: Recibir facturas por correo electrónico
|
||||
name: Nombre
|
||||
email: Correo electrónico
|
||||
nickname: Nombre a mostrar
|
||||
lang: Idioma
|
||||
receiveInvoicesByMail: Recibir facturas por correo
|
||||
addresses: Direcciones
|
||||
changePassword: Cambiar contraseña
|
||||
ca-ES:
|
||||
personalInformation: Dades personals
|
||||
isToBeMailed: Rebre factures per correu electrònic
|
||||
name: Nom
|
||||
email: Correu electrònic
|
||||
nickname: Nom a mostrar
|
||||
lang: Idioma
|
||||
receiveInvoicesByMail: Rebre factures per correu
|
||||
addresses: Adreces
|
||||
changePassword: Canviar contrasenya
|
||||
fr-FR:
|
||||
personalInformation: Informations personnelles
|
||||
isToBeMailed: Recevoir des factures par e-mail
|
||||
name: Nom
|
||||
email: E-mail
|
||||
nickname: Nom à afficher
|
||||
lang: Langue
|
||||
receiveInvoicesByMail: Recevoir des factures par courrier
|
||||
addresses: Adresses
|
||||
changePassword: Changer le mot de passe
|
||||
pt-PT:
|
||||
personalInformation: Dados pessoais
|
||||
isToBeMailed: Receber facturas por e-mail
|
||||
name: Nome
|
||||
email: E-mail
|
||||
nickname: Nom à afficher
|
||||
lang: Língua
|
||||
receiveInvoicesByMail: Receber faturas por correio
|
||||
addresses: Endereços
|
||||
changePassword: Alterar palavra-passe
|
||||
</i18n>
|
|
@ -0,0 +1,182 @@
|
|||
<script setup>
|
||||
import { ref, inject, onMounted, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnForm from 'src/components/common/VnForm.vue';
|
||||
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const vnFormRef = ref(null);
|
||||
const countriesOptions = ref([]);
|
||||
const provincesOptions = ref([]);
|
||||
const pks = { id: route.params.id };
|
||||
const isEditMode = route.params.id !== '0';
|
||||
const fetchAddressDataSql = {
|
||||
query: `
|
||||
SELECT a.id, a.street, a.nickname, a.city, a.postalCode, a.provinceFk, p.countryFk
|
||||
FROM myAddress a
|
||||
LEFT JOIN vn.province p ON p.id = a.provinceFk
|
||||
WHERE a.id = #address
|
||||
`,
|
||||
params: { address: route.params.id }
|
||||
};
|
||||
|
||||
watch(
|
||||
() => vnFormRef?.value?.formData?.countryFk,
|
||||
async val => await getProvinces(val)
|
||||
);
|
||||
|
||||
const goBack = () => router.push({ name: 'addressesList' });
|
||||
|
||||
const getCountries = async () => {
|
||||
countriesOptions.value = await jApi.query(
|
||||
`SELECT id, name FROM vn.country
|
||||
ORDER BY name`
|
||||
);
|
||||
};
|
||||
|
||||
const getProvinces = async countryFk => {
|
||||
if (!countryFk) return;
|
||||
provincesOptions.value = await jApi.query(
|
||||
`SELECT id, name FROM vn.province
|
||||
WHERE countryFk = #id
|
||||
ORDER BY name`,
|
||||
{ id: countryFk }
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(() => getCountries());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="q-pa-md flex justify-center">
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QBtn
|
||||
:label="t('back')"
|
||||
icon="close"
|
||||
rounded
|
||||
no-caps
|
||||
@click="goBack()"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('back') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</Teleport>
|
||||
<VnForm
|
||||
ref="vnFormRef"
|
||||
:fetch-form-data-sql="fetchAddressDataSql"
|
||||
:columns-to-ignore-update="['countryFk']"
|
||||
:create-model-default="{
|
||||
field: 'clientFk',
|
||||
value: 'account.myUser_getId()'
|
||||
}"
|
||||
:pks="pks"
|
||||
:is-edit-mode="isEditMode"
|
||||
:title="t(isEditMode ? 'editAddress' : 'addAddress')"
|
||||
table="myAddress"
|
||||
schema="hedera"
|
||||
@on-data-saved="goBack()"
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnInput v-model="data.nickname" :label="t('name')" />
|
||||
<VnInput v-model="data.street" :label="t('address')" />
|
||||
<VnInput v-model="data.city" :label="t('city')" />
|
||||
<VnInput
|
||||
v-model="data.postalCode"
|
||||
type="number"
|
||||
:label="t('postalCode')"
|
||||
/>
|
||||
<VnSelect
|
||||
v-model="data.countryFk"
|
||||
:label="t('country')"
|
||||
:options="countriesOptions"
|
||||
@update:model-value="data.provinceFk = null"
|
||||
/>
|
||||
<VnSelect
|
||||
v-model="data.provinceFk"
|
||||
:label="t('province')"
|
||||
:options="provincesOptions"
|
||||
/>
|
||||
</template>
|
||||
</VnForm>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.form-container {
|
||||
width: 100%;
|
||||
height: max-content;
|
||||
max-width: 544px;
|
||||
padding: 32px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
accept: Accept
|
||||
name: Consignee
|
||||
address: Address
|
||||
city: City
|
||||
postalCode: Zip code
|
||||
country: Country
|
||||
province: Province
|
||||
addressChangedSuccessfully: Address changed successfully
|
||||
addAddress: Add address
|
||||
editAddress: Edit address
|
||||
es-ES:
|
||||
accept: Aceptar
|
||||
name: Consignatario
|
||||
address: Dirección
|
||||
city: Ciudad
|
||||
postalCode: Código postal
|
||||
country: País
|
||||
province: Provincia
|
||||
addressChangedSuccessfully: Dirección modificada correctamente
|
||||
addAddress: Añadir dirección
|
||||
editAddress: Modificar dirección
|
||||
ca-ES:
|
||||
accept: Acceptar
|
||||
name: Consignatari
|
||||
address: Direcció
|
||||
city: Ciutat
|
||||
postalCode: Codi postal
|
||||
country: País
|
||||
province: Província
|
||||
addressChangedSuccessfully: Adreça modificada correctament
|
||||
addAddress: Afegir adreça
|
||||
editAddress: Modificar adreça
|
||||
fr-FR:
|
||||
accept: Accepter
|
||||
name: Destinataire
|
||||
address: Numéro Rue
|
||||
city: Ville
|
||||
postalCode: Code postal
|
||||
country: Pays
|
||||
province: Province
|
||||
addressChangedSuccessfully: Adresse modifié avec succès
|
||||
addAddress: Ajouter adresse
|
||||
editAddress: Modifier adresse
|
||||
pt-PT:
|
||||
accept: Aceitar
|
||||
name: Consignatario
|
||||
address: Morada
|
||||
city: Concelho
|
||||
postalCode: Código postal
|
||||
country: País
|
||||
province: Distrito
|
||||
addressChangedSuccessfully: Morada modificada corretamente
|
||||
addAddress: Adicionar morada
|
||||
editAddress: Modificar morada
|
||||
</i18n>
|
|
@ -0,0 +1,201 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ref, onMounted, inject } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
import VnList from 'src/components/ui/VnList.vue';
|
||||
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { useVnConfirm } from 'src/composables/useVnConfirm.js';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const router = useRouter();
|
||||
const jApi = inject('jApi');
|
||||
const { notify } = useNotify();
|
||||
const { t } = useI18n();
|
||||
const { openConfirmationModal } = useVnConfirm();
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const addresses = ref([]);
|
||||
const defaultAddress = ref(null);
|
||||
const clientId = ref(null);
|
||||
|
||||
const goToAddressDetails = (id = 0) =>
|
||||
router.push({ name: 'addressDetails', params: { id } });
|
||||
|
||||
const getDefaultAddress = async () => {
|
||||
try {
|
||||
const [address] = await jApi.query(
|
||||
'SELECT id, defaultAddressFk FROM myClient c'
|
||||
);
|
||||
defaultAddress.value = address.defaultAddressFk;
|
||||
clientId.value = address.id;
|
||||
} catch (error) {
|
||||
console.error('Error getting default address:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const getActiveAddresses = async () => {
|
||||
try {
|
||||
addresses.value = await jApi.query(
|
||||
`SELECT a.id, a.nickname, p.name province, a.postalCode, a.city, a.street, a.isActive
|
||||
FROM myAddress a
|
||||
LEFT JOIN vn.province p ON p.id = a.provinceFk
|
||||
WHERE a.isActive`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error getting active addresses:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const changeDefaultAddress = async () => {
|
||||
if (!clientId.value) return;
|
||||
await jApi.execQuery(
|
||||
`UPDATE myClient
|
||||
SET defaultAddressFk = #defaultAddress
|
||||
WHERE id = #id;`,
|
||||
{
|
||||
defaultAddress: defaultAddress.value,
|
||||
id: clientId.value
|
||||
}
|
||||
);
|
||||
notify(t('defaultAddressModified'), 'positive');
|
||||
};
|
||||
|
||||
const removeAddress = async id => {
|
||||
try {
|
||||
await jApi.execQuery(
|
||||
`START TRANSACTION;
|
||||
UPDATE hedera.myAddress SET isActive = FALSE
|
||||
WHERE ((id = #id));
|
||||
SELECT isActive FROM hedera.myAddress WHERE ((id = #id));
|
||||
COMMIT`,
|
||||
{
|
||||
id
|
||||
}
|
||||
);
|
||||
getActiveAddresses();
|
||||
notify(t('dataSaved'), 'positive');
|
||||
} catch (error) {
|
||||
console.error('Error removing address:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
getDefaultAddress();
|
||||
getActiveAddresses();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QBtn
|
||||
:label="t('addAddress')"
|
||||
icon="add"
|
||||
@click="goToAddressDetails()"
|
||||
rounded
|
||||
no-caps
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('addAddress') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</Teleport>
|
||||
<QPage class="vn-w-sm">
|
||||
<VnList
|
||||
class="rounded-borders shadow-1 shadow-transition"
|
||||
separator
|
||||
:rows="addresses"
|
||||
>
|
||||
<CardList
|
||||
v-for="(address, index) in addresses"
|
||||
:key="index"
|
||||
:rounded="false"
|
||||
tag="label"
|
||||
>
|
||||
<template #prepend>
|
||||
<QRadio
|
||||
v-model="defaultAddress"
|
||||
:val="address.id"
|
||||
class="q-mr-sm"
|
||||
@update:model-value="changeDefaultAddress"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<span class="text-bold q-mb-sm">
|
||||
{{ address.nickname }}
|
||||
</span>
|
||||
<span>{{ address.street }}</span>
|
||||
<span>
|
||||
{{ address.postalCode }},
|
||||
{{ address.city }}
|
||||
</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<QBtn
|
||||
icon="delete"
|
||||
flat
|
||||
rounded
|
||||
@click.stop="
|
||||
openConfirmationModal(
|
||||
null,
|
||||
t('confirmDeleteAddress'),
|
||||
() => removeAddress(address.id)
|
||||
)
|
||||
"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('deleteAddress') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
icon="edit"
|
||||
flat
|
||||
rounded
|
||||
@click.stop="goToAddressDetails(address.id)"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('editAddress') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</template>
|
||||
</CardList>
|
||||
</VnList>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
addAddress: Add address
|
||||
defaultAddressModified: Default address modified
|
||||
confirmDeleteAddress: Are you sure you want to delete the address?
|
||||
editAddress: Edit address
|
||||
deleteAddress: Delete address
|
||||
es-ES:
|
||||
addAddress: Añadir dirección
|
||||
defaultAddressModified: Dirección por defecto modificada
|
||||
confirmDeleteAddress: ¿Estás seguro de que quieres borrar la dirección?
|
||||
editAddress: Modificar dirección
|
||||
deleteAddress: Borrar dirección
|
||||
ca-ES:
|
||||
addAddress: Afegir adreça
|
||||
defaultAddressModified: Adreça per defecte modificada
|
||||
confirmDeleteAddress: Estàs segur que vols esborrar l'adreça?
|
||||
editAddress: Modificar adreça
|
||||
deleteAddress: Esborrar adreça
|
||||
fr-FR:
|
||||
addAddress: Ajouter une adresse
|
||||
defaultAddressModified: Adresse par défaut modifiée
|
||||
confirmDeleteAddress: Êtes-vous sûr de vouloir supprimer l'adresse?
|
||||
editAddress: Modifier adresse
|
||||
deleteAddress: Supprimer adresse
|
||||
pt-PT:
|
||||
addAddress: Adicionar Morada
|
||||
defaultAddressModified: Endereço padrão modificado
|
||||
confirmDeleteAddress: Tem a certeza de que deseja excluir o endereço?
|
||||
editAddress: Modificar morada
|
||||
deleteAddress: Eliminar morada
|
||||
</i18n>
|
|
@ -0,0 +1,107 @@
|
|||
<script setup>
|
||||
import { onMounted, inject, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
import VnList from 'src/components/ui/VnList.vue';
|
||||
|
||||
import { formatDateTitle } from 'src/lib/filters.js';
|
||||
|
||||
const jApi = inject('jApi');
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const accessLogs = ref([]);
|
||||
const user = ref(null);
|
||||
|
||||
const getUser = async () => {
|
||||
try {
|
||||
if (!route.params.id) return;
|
||||
const [data] = await jApi.query(
|
||||
`SELECT u.id, u.name user, u.nickname, u.email, c.phone, r.name role
|
||||
FROM account.user u
|
||||
JOIN account.role r ON r.id = u.role
|
||||
LEFT JOIN vn.client c ON c.id = u.id
|
||||
WHERE u.id = #user`,
|
||||
{ user: route.params.id }
|
||||
);
|
||||
user.value = data;
|
||||
} catch (error) {
|
||||
console.error('Error getting user:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const getAccessLogs = async () => {
|
||||
try {
|
||||
accessLogs.value = await jApi.query(
|
||||
`SELECT u.stamp, a.platform, a.browser, a.version, a.javascript, a.cookies
|
||||
FROM visitUser u
|
||||
JOIN visitAccess c ON c.id = u.accessFk
|
||||
JOIN visitAgent a ON a.id = c.agentFk
|
||||
WHERE u.userFk = #user
|
||||
ORDER BY u.stamp DESC
|
||||
LIMIT 8`,
|
||||
{ user: route.params.id }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error getting access logs:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
getUser();
|
||||
getAccessLogs();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="vn-w-xs">
|
||||
<CardList
|
||||
v-if="user"
|
||||
:key="index"
|
||||
:clickable="false"
|
||||
rounded
|
||||
class="q-mb-md"
|
||||
>
|
||||
<template #content>
|
||||
<span class="text-bold text-h5 q-mb-sm">
|
||||
{{ user?.nickname }}
|
||||
</span>
|
||||
<span>#{{ user?.id }} - {{ user.user }} </span>
|
||||
<span>{{ user?.role }} </span>
|
||||
<span>{{ user?.email }} </span>
|
||||
<span>{{ user?.phone }} </span>
|
||||
</template>
|
||||
</CardList>
|
||||
<VnList :rows="accessLogs">
|
||||
<CardList
|
||||
v-for="(accessLog, index) in accessLogs"
|
||||
:key="index"
|
||||
:clickable="false"
|
||||
>
|
||||
<template #content>
|
||||
<span>
|
||||
{{
|
||||
formatDateTitle(accessLog.stamp, {
|
||||
showTime: true,
|
||||
shortDay: true,
|
||||
shortMonth: true,
|
||||
showSeconds: true
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
<span
|
||||
v-if="
|
||||
accessLog.platform &&
|
||||
accessLog.browser &&
|
||||
accessLog.version
|
||||
"
|
||||
>
|
||||
{{ accessLog.platform }} - {{ accessLog.browser }} -
|
||||
{{ accessLog.version }}
|
||||
</span>
|
||||
</template>
|
||||
</CardList>
|
||||
</VnList>
|
||||
</QPage>
|
||||
</template>
|
|
@ -0,0 +1,159 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, inject, onBeforeUnmount } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
import VnList from 'src/components/ui/VnList.vue';
|
||||
|
||||
import { date as qdate } from 'quasar';
|
||||
import { useUserStore } from 'stores/user';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const connections = ref([]);
|
||||
const loading = ref(false);
|
||||
const intervalId = ref(null);
|
||||
|
||||
const getConnections = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
connections.value = await jApi.query(
|
||||
`SELECT vu.userFk userId, vu.stamp, u.nickname, s.lastUpdate,
|
||||
a.platform, a.browser, a.version, u.name user
|
||||
FROM userSession s
|
||||
JOIN visitUser vu ON vu.id = s.userVisitFk
|
||||
JOIN visitAccess ac ON ac.id = vu.accessFk
|
||||
JOIN visitAgent a ON a.id = ac.agentFk
|
||||
JOIN visit v ON v.id = a.visitFk
|
||||
JOIN account.user u ON u.id = vu.userFk
|
||||
ORDER BY lastUpdate DESC`
|
||||
);
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.error('Error getting connections:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const supplantUser = async user => {
|
||||
try {
|
||||
await userStore.supplantUser(user);
|
||||
await appStore.getMenuLinks();
|
||||
router.push({ name: 'confirmedOrders' });
|
||||
} catch (error) {
|
||||
console.error('Error supplanting user:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
getConnections();
|
||||
intervalId.value = setInterval(getConnections, 60000);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => clearInterval(intervalId.value));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<div class="flex">
|
||||
<QBtn
|
||||
:label="t('refresh')"
|
||||
icon="refresh"
|
||||
@click="getConnections()"
|
||||
rounded
|
||||
no-caps
|
||||
class="q-mr-sm"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('refresh') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
<QBadge class="q-pa-sm" v-if="connections.length" color="blue">
|
||||
{{ connections?.length }} {{ t('connections') }}
|
||||
</QBadge>
|
||||
</div>
|
||||
</Teleport>
|
||||
<QPage class="vn-w-xs">
|
||||
<VnList
|
||||
class="flex justify-center"
|
||||
:loading="loading"
|
||||
:rows="connections"
|
||||
>
|
||||
<CardList
|
||||
v-for="(connection, index) in connections"
|
||||
:key="index"
|
||||
:to="{ name: 'accessLog', params: { id: connection.userId } }"
|
||||
>
|
||||
<template #content>
|
||||
<span class="text-bold q-mb-sm">
|
||||
{{ connection.nickname }}
|
||||
</span>
|
||||
<span>
|
||||
{{
|
||||
qdate.formatDate(connection.stamp, 'dd, hh:mm:ss A')
|
||||
}}
|
||||
-
|
||||
{{
|
||||
qdate.formatDate(
|
||||
connection.lastUpdate,
|
||||
'hh:mm:ss A'
|
||||
)
|
||||
}}</span
|
||||
>
|
||||
<span
|
||||
v-if="
|
||||
connection.platform &&
|
||||
connection.browser &&
|
||||
connection.version
|
||||
"
|
||||
>
|
||||
{{ connection.platform }} - {{ connection.browser }} -
|
||||
{{ connection.version }}
|
||||
</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<QBtn
|
||||
icon="people"
|
||||
flat
|
||||
rounded
|
||||
@click.stop.prevent="supplantUser(connection.user)"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('supplantUser') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</template>
|
||||
</CardList>
|
||||
</VnList>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
refresh: Refresh
|
||||
connections: Connections
|
||||
supplantUser: Supplant user
|
||||
es-ES:
|
||||
refresh: Actualizar
|
||||
connections: Conexiones
|
||||
supplantUser: Suplantar usuario
|
||||
ca-ES:
|
||||
refresh: Actualitzar
|
||||
connections: Connexions
|
||||
supplantUser: Suplantar usuari
|
||||
fr-FR:
|
||||
refresh: Rafraîchir
|
||||
connections: Connexions
|
||||
supplantUser: Supplanter l'utilisateur
|
||||
pt-PT:
|
||||
refresh: Atualizar
|
||||
connections: Conexões
|
||||
supplantUser: Suplantar usuário
|
||||
</i18n>
|
|
@ -0,0 +1,81 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
import VnSearchBar from 'src/components/ui/VnSearchBar.vue';
|
||||
import VnList from 'src/components/ui/VnList.vue';
|
||||
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const loading = ref(false);
|
||||
const items = ref([]);
|
||||
|
||||
const query = `SELECT i.id, i.longName, i.size, i.category,
|
||||
i.value5, i.value6, i.value7,
|
||||
i.image, im.updated
|
||||
FROM vn.item i
|
||||
LEFT JOIN image im
|
||||
ON im.collectionFk = 'catalog'
|
||||
AND im.name = i.image
|
||||
WHERE i.longName LIKE CONCAT('%', #search, '%')
|
||||
OR i.id = #search
|
||||
ORDER BY i.longName LIMIT 50`;
|
||||
|
||||
const onSearch = data => (items.value = data || []);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<VnSearchBar
|
||||
:sql-query="query"
|
||||
@on-search="onSearch"
|
||||
@on-search-error="items = []"
|
||||
/>
|
||||
</Teleport>
|
||||
<QPage class="vn-w-xs">
|
||||
<VnList
|
||||
class="flex justify-center"
|
||||
empty-message="introduceSearchTerm"
|
||||
empty-icon="refresh"
|
||||
:loading="loading"
|
||||
:rows="items"
|
||||
>
|
||||
<CardList
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
:clickable="false"
|
||||
>
|
||||
<template #prepend>
|
||||
<VnImg
|
||||
storage="catalog"
|
||||
size="200x200"
|
||||
:id="item.id"
|
||||
width="80px"
|
||||
height="80px"
|
||||
class="q-mr-md"
|
||||
rounded
|
||||
editable
|
||||
edit-schema="catalog"
|
||||
:edit-image-name="item.image"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<span class="text-bold q-mb-sm">
|
||||
{{ item.longName }}
|
||||
</span>
|
||||
<span>
|
||||
{{ item.value5 }} {{ item.value6 }}
|
||||
{{ item.value7 }}
|
||||
</span>
|
||||
<span>{{ item.id }}</span>
|
||||
<span>{{ item.image }}</span>
|
||||
</template>
|
||||
</CardList>
|
||||
</VnList>
|
||||
</QPage>
|
||||
</template>
|
|
@ -0,0 +1,66 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, inject } from 'vue';
|
||||
|
||||
const jApi = inject('jApi');
|
||||
|
||||
const links = ref([]);
|
||||
|
||||
const getLinks = async () => {
|
||||
try {
|
||||
links.value = await jApi.query(
|
||||
`SELECT image, name, description, link FROM link
|
||||
ORDER BY name`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error getting links:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => getLinks());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage>
|
||||
<QList class="flex justify-center q-gutter-md">
|
||||
<QItem
|
||||
v-for="(link, index) in links"
|
||||
:key="index"
|
||||
:href="link.link"
|
||||
target="_blank"
|
||||
class="flex no-padding"
|
||||
>
|
||||
<QCard class="card-container">
|
||||
<QImg
|
||||
:src="`http://cdn.verdnatura.es/image/link/full/${link.image}`"
|
||||
width="60px"
|
||||
height="60px"
|
||||
/>
|
||||
<span class="card-title q-mt-md">{{ link.name }}</span>
|
||||
<p class="card-description">{{ link.description }}</p>
|
||||
</QCard>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-container {
|
||||
width: 140px;
|
||||
height: 170px;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.card-description {
|
||||
font-size: 0.65rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,250 @@
|
|||
<script setup>
|
||||
import { onMounted, ref, inject, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
import VnForm from 'src/components/common/VnForm.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const jApi = inject('jApi');
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const newsTags = ref([]);
|
||||
const pks = computed(() => ({ id: route.params.id }));
|
||||
const isEditMode = !!route.params.id;
|
||||
const formData = ref(
|
||||
!route.params.id
|
||||
? {
|
||||
title: '',
|
||||
tag: '',
|
||||
priority: '',
|
||||
text: ''
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
|
||||
const fetchNewDataSql = computed(() => {
|
||||
if (!route.params.id) return undefined;
|
||||
return {
|
||||
query: `
|
||||
SELECT id, title, text, tag, priority, image
|
||||
FROM news WHERE id = #id`,
|
||||
params: { id: route.params.id }
|
||||
};
|
||||
});
|
||||
|
||||
const getNewsTag = async () => {
|
||||
try {
|
||||
newsTags.value = await jApi.query(
|
||||
`SELECT name, description FROM newsTag
|
||||
ORDER BY description`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error getting newsTag:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const goBack = () => router.push({ name: 'adminNews' });
|
||||
|
||||
onMounted(async () => {
|
||||
getNewsTag();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="vn-w-sm">
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QBtn
|
||||
:label="t('back')"
|
||||
icon="close"
|
||||
rounded
|
||||
no-caps
|
||||
@click="goBack()"
|
||||
>
|
||||
<QTooltip>{{ t('back') }}</QTooltip>
|
||||
</QBtn>
|
||||
</Teleport>
|
||||
<VnForm
|
||||
ref="vnFormRef"
|
||||
:fetch-form-data-sql="fetchNewDataSql"
|
||||
:form-initial-data="formData"
|
||||
:create-model-default="{
|
||||
field: 'userFk',
|
||||
value: 'account.myUser_getId()'
|
||||
}"
|
||||
:pks="pks"
|
||||
:is-edit-mode="isEditMode"
|
||||
table="news"
|
||||
schema="hedera"
|
||||
separation-between-inputs="lg"
|
||||
@on-data-saved="goBack()"
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnImg
|
||||
:id="data.image"
|
||||
:edit-image-name="data.image"
|
||||
storage="news"
|
||||
edit-schema="news"
|
||||
size="200x200"
|
||||
width="80px"
|
||||
height="80px"
|
||||
class="full-width"
|
||||
rounded
|
||||
editable
|
||||
always-show-edit-button
|
||||
/>
|
||||
<VnInput
|
||||
v-model="data.title"
|
||||
:label="t('title')"
|
||||
:clearable="false"
|
||||
/>
|
||||
<div class="row justify-between q-gutter-x-md">
|
||||
<VnSelect
|
||||
v-model="data.tag"
|
||||
:label="t('tag')"
|
||||
option-label="description"
|
||||
option-value="name"
|
||||
:options="newsTags"
|
||||
class="col"
|
||||
/>
|
||||
<VnInput
|
||||
v-model="data.priority"
|
||||
:label="t('priority')"
|
||||
:clearable="false"
|
||||
class="col"
|
||||
/>
|
||||
</div>
|
||||
<QEditor
|
||||
v-model="data.text"
|
||||
:toolbar="[
|
||||
[
|
||||
{
|
||||
label: $q.lang.editor.align,
|
||||
icon: $q.iconSet.editor.align,
|
||||
fixedLabel: true,
|
||||
options: ['left', 'center', 'right', 'justify']
|
||||
}
|
||||
],
|
||||
[
|
||||
'bold',
|
||||
'italic',
|
||||
'strike',
|
||||
'underline',
|
||||
'subscript',
|
||||
'superscript'
|
||||
],
|
||||
['token', 'hr', 'link', 'custom_btn'],
|
||||
['print', 'fullscreen'],
|
||||
[
|
||||
{
|
||||
label: $q.lang.editor.formatting,
|
||||
icon: $q.iconSet.editor.formatting,
|
||||
list: 'no-icons',
|
||||
options: [
|
||||
'p',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'code'
|
||||
]
|
||||
},
|
||||
{
|
||||
label: $q.lang.editor.fontSize,
|
||||
icon: $q.iconSet.editor.fontSize,
|
||||
fixedLabel: true,
|
||||
fixedIcon: true,
|
||||
list: 'no-icons',
|
||||
options: [
|
||||
'size-1',
|
||||
'size-2',
|
||||
'size-3',
|
||||
'size-4',
|
||||
'size-5',
|
||||
'size-6',
|
||||
'size-7'
|
||||
]
|
||||
},
|
||||
{
|
||||
label: $q.lang.editor.defaultFont,
|
||||
icon: $q.iconSet.editor.font,
|
||||
fixedIcon: true,
|
||||
list: 'no-icons',
|
||||
options: [
|
||||
'default_font',
|
||||
'arial',
|
||||
'arial_black',
|
||||
'comic_sans',
|
||||
'courier_new',
|
||||
'impact',
|
||||
'lucida_grande',
|
||||
'times_new_roman',
|
||||
'verdana'
|
||||
]
|
||||
},
|
||||
'removeFormat'
|
||||
],
|
||||
['quote', 'unordered', 'ordered', 'outdent', 'indent'],
|
||||
|
||||
['undo', 'redo'],
|
||||
['viewsource']
|
||||
]"
|
||||
:fonts="{
|
||||
arial: 'Arial',
|
||||
arial_black: 'Arial Black',
|
||||
comic_sans: 'Comic Sans MS',
|
||||
courier_new: 'Courier New',
|
||||
impact: 'Impact',
|
||||
lucida_grande: 'Lucida Grande',
|
||||
times_new_roman: 'Times New Roman',
|
||||
verdana: 'Verdana'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
</VnForm>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
addNew: Add new
|
||||
confirmDeleteAddress: Are you sure you want to delete this new?
|
||||
title: Title
|
||||
tag: Tag
|
||||
priority: Priority
|
||||
es-ES:
|
||||
addNew: Añadir noticia
|
||||
confirmDeleteAddress: ¿Estás seguro de que quieres eliminar esta noticia?
|
||||
title: Título
|
||||
tag: Etiqueta
|
||||
priority: Prioridad
|
||||
ca-ES:
|
||||
addNew: Afegir noticia
|
||||
confirmDeleteAddress: Estàs segur que vols eliminar aquesta notícia?
|
||||
title: Títol
|
||||
tag: Etiqueta
|
||||
priority: Prioritat
|
||||
fr-FR:
|
||||
addNew: Ajouter nouvelles
|
||||
confirmDeleteAddress: Êtes-vous sûr de vouloir supprimer cette nouvelle?
|
||||
title: Titre
|
||||
tag: Tag
|
||||
priority: Priorité
|
||||
pt-PT:
|
||||
addNew: Adicionar noticia
|
||||
confirmDeleteAddress: Tem a certeza que deseja eliminar esta notícia?
|
||||
title: Título
|
||||
tag: Etiqueta
|
||||
priority: Prioridade
|
||||
</i18n>
|
|
@ -0,0 +1,134 @@
|
|||
<script setup>
|
||||
import { onMounted, ref, inject } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
import VnList from 'src/components/ui/VnList.vue';
|
||||
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useVnConfirm } from 'src/composables/useVnConfirm.js';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
|
||||
const jApi = inject('jApi');
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStore();
|
||||
const { openConfirmationModal } = useVnConfirm();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
const { notify } = useNotify();
|
||||
|
||||
const loading = ref(false);
|
||||
const news = ref([]);
|
||||
|
||||
const getNews = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
news.value = await jApi.query(
|
||||
`SELECT n.id, u.nickname, n.priority, n.image, n.title
|
||||
FROM news n
|
||||
JOIN account.user u ON u.id = n.userFk
|
||||
ORDER BY priority, n.created DESC`
|
||||
);
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.error('Error getting news:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteNew = async (id, index) => {
|
||||
try {
|
||||
await jApi.execQuery(
|
||||
`START TRANSACTION;
|
||||
DELETE FROM hedera.news WHERE ((id = #id));
|
||||
COMMIT`,
|
||||
{
|
||||
id
|
||||
}
|
||||
);
|
||||
news.value.splice(index, 1);
|
||||
notify(t('dataSaved'), 'positive');
|
||||
} catch (error) {
|
||||
console.error('Error deleting news:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => getNews());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QBtn
|
||||
:label="t('addNew')"
|
||||
icon="add"
|
||||
:to="{ name: 'adminNewsDetails' }"
|
||||
rounded
|
||||
no-caps
|
||||
>
|
||||
<QTooltip>{{ t('addNew') }}</QTooltip>
|
||||
</QBtn>
|
||||
</Teleport>
|
||||
<QPage class="vn-w-sm">
|
||||
<VnList class="flex justify-center" :loading="loading" :rows="news">
|
||||
<CardList
|
||||
v-for="(newsItem, index) in news"
|
||||
:key="index"
|
||||
:to="{ name: 'adminNewsDetails', params: { id: newsItem.id } }"
|
||||
>
|
||||
<template #prepend>
|
||||
<VnImg
|
||||
:id="newsItem.image"
|
||||
:edit-image-name="newsItem.image"
|
||||
storage="news"
|
||||
edit-schema="news"
|
||||
size="200x200"
|
||||
width="80px"
|
||||
height="80px"
|
||||
class="q-mr-md"
|
||||
rounded
|
||||
editable
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<span class="text-bold q-mb-sm">{{ newsItem.title }} </span>
|
||||
<span>{{ newsItem.nickname }} </span>
|
||||
<span>{{ newsItem.priority }}</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<QBtn
|
||||
icon="delete"
|
||||
flat
|
||||
rounded
|
||||
@click.stop.prevent="
|
||||
openConfirmationModal(
|
||||
null,
|
||||
t('confirmDeleteAddress'),
|
||||
() => deleteNew(newsItem.id, index)
|
||||
)
|
||||
"
|
||||
>
|
||||
<QTooltip>{{ t('remove') }}</QTooltip>
|
||||
</QBtn>
|
||||
</template>
|
||||
</CardList>
|
||||
</VnList>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
addNew: Add new
|
||||
confirmDeleteAddress: Are you sure you want to delete this new?
|
||||
es-ES:
|
||||
addNew: Añadir noticia
|
||||
confirmDeleteAddress: ¿Estás seguro de que quieres eliminar esta noticia?
|
||||
ca-ES:
|
||||
addNew: Afegir noticia
|
||||
confirmDeleteAddress: Estàs segur que vols eliminar aquesta notícia?
|
||||
fr-FR:
|
||||
addNew: Ajouter nouvelles
|
||||
confirmDeleteAddress: Êtes-vous sûr de vouloir supprimer cette nouvelle?
|
||||
pt-PT:
|
||||
addNew: Adicionar noticia
|
||||
confirmDeleteAddress: Tem a certeza que deseja eliminar esta notícia?
|
||||
</i18n>
|
|
@ -0,0 +1,321 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ref, onMounted, inject, reactive, computed } from 'vue';
|
||||
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnForm from 'src/components/common/VnForm.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
|
||||
const jApi = inject('jApi');
|
||||
const api = inject('api');
|
||||
const { t } = useI18n();
|
||||
const { notify } = useNotify();
|
||||
|
||||
const fileUploaderRef = ref(null);
|
||||
|
||||
const statusIcons = {
|
||||
uploading: {
|
||||
icon: 'cloud_upload',
|
||||
tooltip: 'Uploading'
|
||||
},
|
||||
fulfilled: {
|
||||
icon: 'cloud_done',
|
||||
tooltip: 'imageUploaded'
|
||||
},
|
||||
rejected: {
|
||||
icon: 'error',
|
||||
tooltip: 'Error'
|
||||
},
|
||||
pending: {
|
||||
icon: 'add',
|
||||
tooltip: 'pendingUpload'
|
||||
}
|
||||
};
|
||||
const formInitialData = reactive({
|
||||
schema: 'catalog',
|
||||
updateMatching: true
|
||||
});
|
||||
const imageCollections = ref([]);
|
||||
const addedFiles = ref([]);
|
||||
|
||||
const isSubmitable = computed(() =>
|
||||
addedFiles.value.some(file => file.uploadStatus === 'pending')
|
||||
);
|
||||
|
||||
const getImageCollections = async () => {
|
||||
try {
|
||||
imageCollections.value = await jApi.query(
|
||||
'SELECT name, `desc` FROM imageCollection ORDER BY `desc`'
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error getting image collections:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async data => {
|
||||
if (!addedFiles.value.length) {
|
||||
notify(t('noFilesToUpload'), 'warning');
|
||||
return;
|
||||
}
|
||||
const filteredFiles = addedFiles.value.filter(
|
||||
file => file.uploadStatus === 'pending'
|
||||
);
|
||||
const promises = filteredFiles.map((file, index) => {
|
||||
const fileIndex = filteredFiles[index].index;
|
||||
addedFiles.value[fileIndex].uploadStatus = 'uploading';
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('updateMatching', data.updateMatching);
|
||||
formData.append('image', file.file);
|
||||
formData.append('name', file.name);
|
||||
formData.append('schema', data.schema);
|
||||
formData.append('srv', 'json:image/upload');
|
||||
return api({
|
||||
method: 'post',
|
||||
url: location.origin,
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const results = await Promise.allSettled(promises);
|
||||
results.forEach((result, index) => {
|
||||
const fileIndex = filteredFiles[index].index;
|
||||
addedFiles.value[fileIndex].uploadStatus = result.status;
|
||||
|
||||
if (result.status === 'rejected') {
|
||||
addedFiles.value[fileIndex].errorMessage = t(
|
||||
result.reason?.response?.data?.data?.message
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const allSuccessful = results.every(
|
||||
result => result.status === 'fulfilled'
|
||||
);
|
||||
if (allSuccessful) {
|
||||
notify(t('uploadSuccess'), 'positive');
|
||||
} else {
|
||||
notify(t('uploadError'), 'negative');
|
||||
}
|
||||
};
|
||||
|
||||
const onFilesAdded = files => {
|
||||
const initialFilesLength = addedFiles.value.length;
|
||||
files.forEach((file, index) => {
|
||||
const [name] = file.name.split('.');
|
||||
const fileData = {
|
||||
name,
|
||||
file,
|
||||
index: initialFilesLength + index,
|
||||
uploadStatus: 'pending'
|
||||
};
|
||||
addedFiles.value.push(fileData);
|
||||
});
|
||||
};
|
||||
|
||||
const recalculateFilesIndexes = () => {
|
||||
addedFiles.value.forEach((_, index) => {
|
||||
addedFiles.value[index].index = index;
|
||||
});
|
||||
};
|
||||
|
||||
const removeFile = (file, index) => {
|
||||
fileUploaderRef.value.removeFile(file);
|
||||
addedFiles.value.splice(index, 1);
|
||||
recalculateFilesIndexes();
|
||||
};
|
||||
|
||||
const clearFiles = () => {
|
||||
fileUploaderRef.value.reset();
|
||||
addedFiles.value = [];
|
||||
};
|
||||
|
||||
onMounted(async () => getImageCollections());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="vn-w-sm">
|
||||
<VnForm
|
||||
ref="vnFormRef"
|
||||
:default-actions="false"
|
||||
:form-initial-data="formInitialData"
|
||||
separation-between-inputs="md"
|
||||
show-bottom-actions
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnSelect
|
||||
v-model="data.schema"
|
||||
:label="t('collection')"
|
||||
option-label="desc"
|
||||
option-value="name"
|
||||
:options="imageCollections"
|
||||
/>
|
||||
<QUploader
|
||||
ref="fileUploaderRef"
|
||||
:label="t('dropYourFiles')"
|
||||
class="full-width"
|
||||
square
|
||||
flat
|
||||
multiple
|
||||
bordered
|
||||
hide-upload-btn
|
||||
@added="onFilesAdded"
|
||||
>
|
||||
<template #list="scope">
|
||||
<QList v-if="addedFiles.length" separator>
|
||||
<QItem
|
||||
v-for="(file, index) in scope.files"
|
||||
:key="file.__key"
|
||||
class="flex full-width row items-center justify-center"
|
||||
>
|
||||
<img
|
||||
:src="file.__img.src"
|
||||
style="width: 28px; height: 21px"
|
||||
class="q-mr-md"
|
||||
/>
|
||||
<VnInput
|
||||
v-model="addedFiles[index].name"
|
||||
:clearable="false"
|
||||
dense
|
||||
class="full-width"
|
||||
/>
|
||||
<QSpinner
|
||||
v-if="
|
||||
addedFiles[index].uploadStatus ===
|
||||
'uploading'
|
||||
"
|
||||
color="primary"
|
||||
size="2em"
|
||||
:thickness="1"
|
||||
/>
|
||||
<QIcon
|
||||
v-else-if="
|
||||
addedFiles[index].uploadStatus &&
|
||||
addedFiles[index].uploadStatus !==
|
||||
'uploading'
|
||||
"
|
||||
:name="
|
||||
statusIcons[
|
||||
addedFiles[index].uploadStatus
|
||||
].icon
|
||||
"
|
||||
size="sm"
|
||||
>
|
||||
<QTooltip>
|
||||
{{
|
||||
addedFiles[index].errorMessage ||
|
||||
t(
|
||||
statusIcons[
|
||||
addedFiles[index]
|
||||
.uploadStatus
|
||||
].tooltip
|
||||
)
|
||||
}}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
<QBtn
|
||||
v-if="
|
||||
addedFiles[index].uploadStatus !==
|
||||
'uploading'
|
||||
"
|
||||
class="gt-xs"
|
||||
size="md"
|
||||
flat
|
||||
dense
|
||||
round
|
||||
icon="delete"
|
||||
@click="removeFile(file, index)"
|
||||
>
|
||||
<QTooltip>{{ t('remove') }}</QTooltip>
|
||||
</QBtn>
|
||||
</QItem>
|
||||
</QList>
|
||||
</template>
|
||||
</QUploader>
|
||||
<QCheckbox
|
||||
v-model="data.updateMatching"
|
||||
:label="t('updateMatching')"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ data }">
|
||||
<QBtn
|
||||
:label="t('clearAll')"
|
||||
rounded
|
||||
no-caps
|
||||
flat
|
||||
@click="clearFiles()"
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('uploadFiles')"
|
||||
rounded
|
||||
no-caps
|
||||
flat
|
||||
:disable="!isSubmitable"
|
||||
@click="onSubmit(data)"
|
||||
/>
|
||||
</template>
|
||||
</VnForm>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
collection: Collection
|
||||
updateMatching: Update items with matching id
|
||||
dropYourFiles: Click or drop files here
|
||||
clearAll: Clear all
|
||||
uploadFiles: Upload files
|
||||
uploadSuccess: Upload finished successfully
|
||||
uploadError: Some errors happened on upload
|
||||
noFilesToUpload: There are no files to upload
|
||||
pendingUpload: Pending upload
|
||||
imageUploaded: Image uploaded
|
||||
es-ES:
|
||||
collection: Colección
|
||||
updateMatching: Actualizar artículos con id coincidente
|
||||
dropYourFiles: Pulsa o suelta los archivos aquí
|
||||
clearAll: Limpiar todo
|
||||
uploadFiles: Subir archivos
|
||||
uploadSuccess: Imágenes subidas correctamente
|
||||
uploadError: Ocurrieron errores al subir alguna de las imágenes
|
||||
noFilesToUpload: No se han seleccionado archivos para subir
|
||||
pendingUpload: Subida pendiente
|
||||
imageUploaded: Imagen subida
|
||||
ca-ES:
|
||||
collection: Col·lecció
|
||||
updateMatching: Actualitzar els elements amb id coincident
|
||||
dropYourFiles: Prem o deixa anar els arxius aquí
|
||||
clearAll: Netejar tot
|
||||
uploadFiles: Pujar arxius
|
||||
uploadSuccess: Imatges pujades correctament
|
||||
uploadError: Van ocórrer errors en pujar alguna de les imatges
|
||||
noFilesToUpload: No s'ha seleccionat arxius per pujar
|
||||
pendingUpload: Pujada pendent
|
||||
imageUploaded: Imatge pujada
|
||||
fr-FR:
|
||||
collection: Collection
|
||||
updateMatching: Mettre à jour les éléments avec l'identifiant correspondant
|
||||
dropYourFiles: Cliquez ici ou déposer des fichiers
|
||||
clearAll: Tout effacer
|
||||
uploadFiles: Upload Files
|
||||
uploadSuccess: Les images téléchargées correctement
|
||||
uploadError: Des erreurs sont survenues lors du téléchargement des images
|
||||
noFilesToUpload: Aucun fichier sélectionné pour télécharger
|
||||
pendingUpload: Téléchargement en attente
|
||||
imageUploaded: Image téléchargée
|
||||
pt-PT:
|
||||
collection: Coleção
|
||||
updateMatching: Atualizar itens com id correspondente
|
||||
dropYourFiles: Clique ou solte arquivos aqui
|
||||
clearAll: Limpar tudo
|
||||
uploadFiles: Fazer upload de arquivos
|
||||
uploadSuccess: Upload concluído com sucesso
|
||||
uploadError: Ocorreram erros ao subir alguma das imagens
|
||||
noFilesToUpload: Não há arquivos selecionados para upload
|
||||
pendingUpload: Upload pendente
|
||||
imageUploaded: Imagem carregada
|
||||
</i18n>
|
|
@ -0,0 +1,116 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
import VnSearchBar from 'src/components/ui/VnSearchBar.vue';
|
||||
import VnList from 'src/components/ui/VnList.vue';
|
||||
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useUserStore } from 'stores/user';
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const loading = ref(false);
|
||||
const users = ref([]);
|
||||
|
||||
const query = `SELECT u.id, u.name, u.nickname, u.active
|
||||
FROM account.user u
|
||||
WHERE u.name LIKE CONCAT('%', #user, '%')
|
||||
OR u.nickname LIKE CONCAT('%', #user, '%')
|
||||
OR u.id = #user
|
||||
ORDER BY u.name LIMIT 200`;
|
||||
|
||||
const onSearch = data => (users.value = data || []);
|
||||
|
||||
const supplantUser = async user => {
|
||||
try {
|
||||
await userStore.supplantUser(user);
|
||||
await appStore.getMenuLinks();
|
||||
router.push({ name: 'confirmedOrders' });
|
||||
} catch (error) {
|
||||
console.error('Error supplanting user:', error);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<VnSearchBar
|
||||
:sql-query="query"
|
||||
search-field="user"
|
||||
@on-search="onSearch"
|
||||
@on-search-error="users = []"
|
||||
/>
|
||||
</Teleport>
|
||||
<QPage class="vn-w-xs">
|
||||
<VnList
|
||||
class="flex justify-center"
|
||||
empty-message="noData"
|
||||
empty-icon="refresh"
|
||||
:loading="loading"
|
||||
:rows="users"
|
||||
>
|
||||
<CardList
|
||||
v-for="(user, index) in users"
|
||||
:key="index"
|
||||
:to="{ name: 'accessLog', params: { id: user.id } }"
|
||||
>
|
||||
<template #content>
|
||||
<span class="text-bold q-mb-sm">
|
||||
{{ user.nickname }}
|
||||
</span>
|
||||
<span>#{{ user.id }} - {{ user.name }} </span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<QBtn
|
||||
v-if="user.active"
|
||||
icon="people"
|
||||
flat
|
||||
rounded
|
||||
@click.stop.prevent="supplantUser(user.name)"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Impersonate user') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
<QBadge v-else color="negative">{{ t('Disabled') }}</QBadge>
|
||||
</template>
|
||||
</CardList>
|
||||
</VnList>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
User management: User management
|
||||
Disabled: Disabled
|
||||
Impersonate user: Impersonate user
|
||||
Access log: Access log
|
||||
es-ES:
|
||||
User management: Gestión de usuarios
|
||||
Disabled: Desactivado
|
||||
Impersonate user: Suplantar usuario
|
||||
Access log: Registro de accesos
|
||||
ca-ES:
|
||||
User management: Gestió d'usuaris
|
||||
Disabled: Deshabilitat
|
||||
Impersonate user: Suplantar usuari
|
||||
Access log: Registre d'accessos
|
||||
fr-FR:
|
||||
User management: Gestion des utilisateurs
|
||||
Disabled: Désactivé
|
||||
Impersonate user: Accès utilisateur
|
||||
Access log: Journal des accès
|
||||
pt-PT:
|
||||
User management: Gestão de usuarios
|
||||
Disabled: Desativado
|
||||
Impersonate user: Suplantar usuario
|
||||
Access log: Registro de acessos
|
||||
</i18n>
|
|
@ -0,0 +1,176 @@
|
|||
<script setup>
|
||||
import { ref, inject, watch, computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
|
||||
import { formatDateTitle, date } from 'src/lib/filters.js';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const loading = ref(false);
|
||||
const from = ref(Date.vnNew(route.query.from) || Date.vnNew());
|
||||
const to = ref(Date.vnNew(route.query.to) || Date.vnNew());
|
||||
const visitsData = ref(null);
|
||||
|
||||
const getVisits = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const [visitsResponse] = await jApi.query(
|
||||
`SELECT browser,
|
||||
MIN(CAST(version AS DECIMAL(4,1))) minVersion,
|
||||
MAX(CAST(version AS DECIMAL(4,1))) maxVersion,
|
||||
MAX(c.stamp) lastVisit,
|
||||
COUNT(DISTINCT c.id) visits,
|
||||
SUM(a.firstAccessFk = c.id AND v.firstAgentFk = a.id) newVisits
|
||||
FROM visitUser e
|
||||
JOIN visitAccess c ON c.id = e.accessFk
|
||||
JOIN visitAgent a ON a.id = c.agentFk
|
||||
JOIN visit v ON v.id = a.visitFk
|
||||
WHERE c.stamp BETWEEN TIMESTAMP(#from,'00:00:00') AND TIMESTAMP(#to,'23:59:59')
|
||||
GROUP BY browser ORDER BY visits DESC`,
|
||||
{
|
||||
from: date(from.value),
|
||||
to: date(to.value)
|
||||
}
|
||||
);
|
||||
visitsData.value = visitsResponse;
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.error('Error getting visits:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const visitsCardText = computed(
|
||||
() =>
|
||||
`${visitsData?.value?.visits || 0} ${t('visits')}, ${visitsData?.value?.newVisits || 0} ${t('news')}`
|
||||
);
|
||||
|
||||
watch(
|
||||
[() => from.value, () => to.value],
|
||||
async () => {
|
||||
await router.replace({
|
||||
query: {
|
||||
from: date(from.value),
|
||||
to: date(to.value)
|
||||
}
|
||||
});
|
||||
await getVisits();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QBtn
|
||||
:label="t('refresh')"
|
||||
icon="refresh"
|
||||
@click="getVisits()"
|
||||
rounded
|
||||
no-caps
|
||||
class="q-mr-sm"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('refresh') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
:label="t('connections')"
|
||||
icon="visibility"
|
||||
rounded
|
||||
no-caps
|
||||
:to="{ name: 'adminConnections' }"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('connections') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</Teleport>
|
||||
<QPage class="vn-w-xs column">
|
||||
<QCard class="column q-pa-lg q-mb-md">
|
||||
<VnInputDate :label="t('from')" v-model="from" class="q-mb-sm" />
|
||||
<VnInputDate :label="t('to')" v-model="to" />
|
||||
</QCard>
|
||||
<QCard v-if="!loading" class="q-pa-lg flex q-mb-md">
|
||||
<span class="full-width text-right text-h6">
|
||||
{{ visitsCardText }}
|
||||
</span>
|
||||
</QCard>
|
||||
<QCard v-if="!loading" class="q-pa-lg column">
|
||||
<span
|
||||
v-if="
|
||||
visitsData?.browser &&
|
||||
visitsData?.minVersion &&
|
||||
visitsData?.maxVersion
|
||||
"
|
||||
>
|
||||
{{ visitsData?.browser }} - {{ visitsData?.minVersion }} -
|
||||
{{ visitsData?.maxVersion }}
|
||||
</span>
|
||||
<span>{{ visitsCardText }}</span>
|
||||
<span v-if="visitsData">
|
||||
{{
|
||||
formatDateTitle(visitsData.lastVisit, {
|
||||
showTime: true,
|
||||
showSeconds: true,
|
||||
shortDay: true
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
</QCard>
|
||||
<QSpinner
|
||||
v-else
|
||||
color="primary"
|
||||
size="3em"
|
||||
:thickness="2"
|
||||
style="margin: 0 auto"
|
||||
/>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
from: From
|
||||
to: To
|
||||
visits: Visits
|
||||
news: New
|
||||
connections: Connections
|
||||
refresh: Refresh
|
||||
es-ES:
|
||||
from: Desde
|
||||
to: Hasta
|
||||
visits: Visitas
|
||||
news: Nuevas
|
||||
connections: Conexiones
|
||||
refresh: Actualizar
|
||||
ca-ES:
|
||||
from: Desde
|
||||
to: Fins
|
||||
visits: Visites
|
||||
news: Noves
|
||||
connections: Connexions
|
||||
refresh: Actualitzar
|
||||
fr-FR:
|
||||
from: À partir de
|
||||
to: Jusqu'à
|
||||
visits: Visites
|
||||
news: Nouveau
|
||||
connections: Connexions
|
||||
refresh: Rafraîchir
|
||||
pt-PT:
|
||||
from: Desde
|
||||
to: Até
|
||||
visits: Visitas
|
||||
news: Novo
|
||||
connections: Conexões
|
||||
refresh: Atualizar
|
||||
</i18n>
|
|
@ -0,0 +1,88 @@
|
|||
<script setup>
|
||||
import { ref, inject, onMounted, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnTable from 'src/components/ui/VnTable.vue';
|
||||
|
||||
const jApi = inject('jApi');
|
||||
const { t } = useI18n();
|
||||
|
||||
const packages = ref([]);
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
label: t('agency'),
|
||||
name: 'agency',
|
||||
field: 'Agencia',
|
||||
align: 'left',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
label: t('expeditions'),
|
||||
name: 'expeditions',
|
||||
field: 'expediciones',
|
||||
align: 'right',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
label: t('bundles'),
|
||||
name: 'bundles',
|
||||
field: 'Bultos',
|
||||
align: 'right',
|
||||
sortable: true
|
||||
},
|
||||
|
||||
{
|
||||
label: t('prevision'),
|
||||
name: 'prevision',
|
||||
field: 'Faltan',
|
||||
align: 'right',
|
||||
sortable: true
|
||||
}
|
||||
]);
|
||||
|
||||
const getPackages = async () => {
|
||||
try {
|
||||
const data = await jApi.query('CALL vn.agencyVolume()');
|
||||
packages.value = data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => getPackages());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="flex justify-center q-pa-md">
|
||||
<VnTable
|
||||
:columns="columns"
|
||||
:rows="packages"
|
||||
:loading="loading"
|
||||
style="height: max-content; max-width: 100%"
|
||||
/>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
bundles: Bundles
|
||||
expeditions: Exps.
|
||||
prevision: Prev.
|
||||
es-ES:
|
||||
bundles: Bultos
|
||||
expeditions: Exps.
|
||||
prevision: Prev.
|
||||
ca-ES:
|
||||
bundles: Paquets
|
||||
expeditions: Exps.
|
||||
prevision: Prev.
|
||||
fr-FR:
|
||||
bundles: Cartons
|
||||
expeditions: Exps.
|
||||
prevision: Prev.
|
||||
pt-PT:
|
||||
bundles: Bultos
|
||||
expeditions: Exps.
|
||||
prevision: Prev.
|
||||
</i18n>
|