diff --git a/.eslintrc.js b/.eslintrc.js index dd427155..40e9ff14 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,22 +15,8 @@ module.exports = { 'vue/setup-compiler-macros': true }, - 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) - - 'standard' - ], - + extends: ['standard'], plugins: ['vue', 'prettier'], - globals: { ga: 'readonly', // Google Analytics cordova: 'readonly', @@ -66,28 +52,34 @@ module.exports = { 'prefer-promise-reject-errors': 'off', semi: 'off', // allow debugger during development only - 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', - 'vue/html-indent': [ - 'error', - 4, - { - attribute: 1, - baseIndent: 1, - closeBracket: 0, - alignAttributesVertically: true, - ignores: [] - } - ] + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' }, overrides: [ { - extends: ['plugin:vue/vue3-essential'], - files: ['src/**/*.{js,vue,scss}'], // Aplica ESLint solo a archivos .js, .vue y .scss dentro de src (Proyecto de quasar) + 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', - indent: ['error', 4, { SwitchCase: 1 }], - 'space-before-function-paren': 'off' - } + 'space-before-function-paren': 'off', + 'prefer-promise-reject-errors': 'off', + 'vue/no-multiple-template-root': 'off' + }, + parserOptions: { + ecmaVersion: '2021' + }, + plugins: ['vue'] } ] }; diff --git a/.vscode/settings.json b/.vscode/settings.json index 0eda1dfd..b194dfb7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,13 +1,9 @@ { - "files.eol": "\n", - "eslint.autoFixOnSave": true, "editor.bracketPairColorization.enabled": true, "editor.guides.bracketPairs": true, - "editor.formatOnSave": false, - "editor.defaultFormatter": null, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": ["source.fixAll.eslint"], "eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"], - "[sql]": { - "editor.formatOnSave": true - } + "cSpell.words": ["axios", "composables"] } diff --git a/quasar.config.js b/quasar.config.js index abd52035..a657b9de 100644 --- a/quasar.config.js +++ b/quasar.config.js @@ -14,249 +14,243 @@ const path = require('path'); const { configure } = require('quasar/wrappers'); module.exports = configure(function (ctx) { - return { - // fix: true, - // include = [], - // exclude = [], - // rawOptions = {}, - warnings: true, - errors: true, - // https://v2.quasar.dev/quasar-cli-webpack/supporting-ts - supportTS: false, + return { + // https://v2.quasar.dev/quasar-cli-webpack/supporting-ts + supportTS: false, - // https://v2.quasar.dev/quasar-cli-webpack/prefetch-feature - // preFetch: true, + // 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'], + // 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://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! + // 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 - ], + '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' + // 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: '/', + // 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: [], + // 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, + // 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, + // 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 + // 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'] }]); + 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'); - } - }, + 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:3001', - bypass: req => (req.path !== '/' ? req.path : null) + // 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'] }]); + } } - } - }, - - // 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'] }]); - } - } - }; + }; }); diff --git a/src/boot/app.js b/src/boot/app.js index 0f2f46e0..51734ba5 100644 --- a/src/boot/app.js +++ b/src/boot/app.js @@ -1,10 +1,10 @@ import { boot } from 'quasar/wrappers'; import { useAppStore } from 'stores/app'; -import { userStore } from 'stores/user'; +import { useUserStore } from 'stores/user'; export default boot(({ app }) => { const props = app.config.globalProperties; + const userStore = useUserStore(); props.$app = useAppStore(); - props.$user = userStore(); - props.$actions = document.createElement('div'); + props.$user = userStore.user; }); diff --git a/src/boot/axios.js b/src/boot/axios.js index 8902e070..b923ccdc 100644 --- a/src/boot/axios.js +++ b/src/boot/axios.js @@ -1,6 +1,6 @@ import { boot } from 'quasar/wrappers'; import { Connection } from '../js/db/connection'; -import { userStore } from 'stores/user'; +import { useUserStore } from 'stores/user'; import axios from 'axios'; import useNotify from 'src/composables/useNotify.js'; @@ -36,10 +36,10 @@ const onResponseError = error => { }; export default boot(({ app }) => { - const user = userStore(); + const userStore = useUserStore(); function addToken(config) { - if (user.token) { - config.headers.Authorization = user.token; + if (userStore.token) { + config.headers.Authorization = userStore.token; } return config; } diff --git a/src/components/common/VnForm.vue b/src/components/common/VnForm.vue index 98a46ac5..b918c9a7 100644 --- a/src/components/common/VnForm.vue +++ b/src/components/common/VnForm.vue @@ -2,6 +2,8 @@ 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, @@ -73,6 +75,10 @@ const props = defineProps({ saveFn: { type: Function, default: null + }, + separationBetweenInputs: { + type: String, + default: 'xs' } }); @@ -81,6 +87,8 @@ const emit = defineEmits(['onDataSaved']); const { t } = useI18n(); const jApi = inject('jApi'); const { notify } = useNotify(); +const appStore = useAppStore(); +const { isHeaderMounted } = storeToRefs(appStore); const loading = ref(false); const formData = ref({}); @@ -101,6 +109,10 @@ const updatedColumns = computed(() => { 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; @@ -167,8 +179,17 @@ const generateSqlQuery = () => { }; onMounted(async () => { - if (!props.formInitialData && props.autoLoad) { + 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] + }; } }); @@ -186,9 +207,10 @@ defineExpose({ - + {{ title }} @@ -213,7 +236,9 @@ defineExpose({ no-caps flat v-close-popup - /> + > + {{ t('cancel') }} + - + > + {{ t('save') }} + + diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue new file mode 100644 index 00000000..bfd499f5 --- /dev/null +++ b/src/components/common/VnInputDate.vue @@ -0,0 +1,174 @@ + + + + + + +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 + diff --git a/src/components/ui/CardList.vue b/src/components/ui/CardList.vue index 58af109a..eef1c06b 100644 --- a/src/components/ui/CardList.vue +++ b/src/components/ui/CardList.vue @@ -22,20 +22,15 @@ const handleClick = () => { :class="{ 'cursor-pointer': clickable, 'no-radius': !rounded }" @click="handleClick()" > - -
- -
- -
+
+ +
+
- - +
+
- +
@@ -44,4 +39,20 @@ const handleClick = () => { border-bottom: 1px solid $gray-light; padding: 20px; } + +.content { + display: flex; + flex-direction: column; + overflow: hidden; + + * { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +} + +.content-container { + display: flex; +} diff --git a/src/components/ui/ChangePasswordForm.vue b/src/components/ui/ChangePasswordForm.vue index 93a7e9be..3f5afd86 100644 --- a/src/components/ui/ChangePasswordForm.vue +++ b/src/components/ui/ChangePasswordForm.vue @@ -5,8 +5,10 @@ import { useI18n } from 'vue-i18n'; import VnInput from 'src/components/common/VnInput.vue'; import VnForm from 'src/components/common/VnForm.vue'; -import { userStore as useUserStore } from 'stores/user'; +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: { @@ -24,6 +26,8 @@ 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); @@ -33,7 +37,7 @@ const repeatPassword = ref(''); const passwordRequirements = ref(null); const formData = ref({ - userId: userStore.id, + userId: userStore?.user?.id, oldPassword: '', newPassword: '' }); @@ -75,7 +79,7 @@ const getPasswordRequirements = async () => { }; const login = async () => { - await userStore.login(userStore.name, formData.value.newPassword); + await userStore.login(userStore.user.name, formData.value.newPassword); }; const onPasswordChanged = async () => { @@ -149,7 +153,7 @@ onMounted(async () => { -