{{ card.subName }}
@@ -57,11 +57,12 @@ const card = toRef(props, 'item');diff --git a/Jenkinsfile b/Jenkinsfile
index 59bf09e22..27bf4f82f 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -108,7 +108,6 @@ pipeline {
}
stage('E2E') {
environment {
- CREDS = credentials('docker-registry')
COMPOSE_PROJECT = "${PROJECT_NAME}-${env.BUILD_ID}"
COMPOSE_PARAMS = "-p ${env.COMPOSE_PROJECT} -f test/cypress/docker-compose.yml --project-directory ."
}
@@ -118,23 +117,24 @@ pipeline {
sh 'rm -rf test/cypress/screenshots'
env.COMPOSE_TAG = PROTECTED_BRANCH.contains(env.CHANGE_TARGET) ? env.CHANGE_TARGET : 'dev'
- def image = docker.build('lilium-dev', '-f docs/Dockerfile.dev docs')
-
- sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY'
- sh "docker-compose ${env.COMPOSE_PARAMS} pull back"
- sh "docker-compose ${env.COMPOSE_PARAMS} pull db"
- sh "docker-compose ${env.COMPOSE_PARAMS} up -d"
-
- def modules = sh(script: "node test/cypress/docker/find/find.js ${env.COMPOSE_TAG}", returnStdout: true).trim()
+ def modules = sh(
+ script: "node test/cypress/docker/find/find.js ${env.COMPOSE_TAG}",
+ returnStdout: true
+ ).trim()
echo "E2E MODULES: ${modules}"
- image.inside("--network ${env.COMPOSE_PROJECT}_default -e CI -e TZ --init") {
- sh "sh test/cypress/docker/cypressParallel.sh 1 '${modules}'"
+
+ script {
+ def image = docker.build('lilium-dev', '-f docs/Dockerfile.dev docs')
+ sh "docker compose ${env.COMPOSE_PARAMS} up -d"
+ image.inside("--network ${env.COMPOSE_PROJECT}_default -e CI -e TZ --init") {
+ sh "sh test/cypress/docker/cypressParallel.sh 1 '${modules}'"
+ }
}
}
}
post {
always {
- sh "docker-compose ${env.COMPOSE_PARAMS} down -v"
+ sh "docker compose ${env.COMPOSE_PARAMS} down -v"
archiveArtifacts artifacts: 'test/cypress/screenshots/**/*', allowEmptyArchive: true
junit(
testResults: 'junit/e2e-*.xml',
@@ -153,17 +153,8 @@ pipeline {
VERSION = readFile 'VERSION.txt'
}
steps {
- script {
- sh 'quasar build'
-
- def baseImage = "salix-frontend:${env.VERSION}"
- def image = docker.build(baseImage, ".")
- docker.withRegistry("https://${env.REGISTRY}", 'docker-registry') {
- image.push()
- image.push(env.BRANCH_NAME)
- if (IS_LATEST) image.push('latest')
- }
- }
+ sh 'quasar build'
+ dockerBuild 'salix-frontend', '.'
}
}
stage('Deploy') {
@@ -186,3 +177,53 @@ pipeline {
}
}
+def dockerBuild(imageName, context, dockerfile = null) {
+ if (dockerfile == null)
+ dockerfile = "${context}/Dockerfile"
+
+ def certDir = '/home/jenkins/.buildkit/certs'
+ def buildxArgs = [
+ "--name=buildkitd",
+ "--driver=remote",
+ "--driver-opt="
+ + "cacert=${certDir}/ca.pem,"
+ + "cert=${certDir}/cert.pem,"
+ + "key=${certDir}/key.pem,"
+ + "servername=buildkitd",
+ "tcp://buildkitd:1234"
+ ]
+
+ def cacheImage = "${env.REGISTRY_CACHE}/${imageName}"
+ def pushImage = "${env.REGISTRY}/${imageName}"
+ def baseImage = "${pushImage}:${env.VERSION}"
+
+ def buildArgs = [
+ context,
+ "--push",
+ "--builder=buildkitd",
+ "--file=${dockerfile}",
+ "--cache-from=type=registry,ref=${cacheImage}:cache-${env.BRANCH_NAME}",
+ "--cache-from=type=registry,ref=${cacheImage}:cache-dev",
+ "--cache-to=type=registry,ref=${cacheImage}:cache-${env.BRANCH_NAME},mode=max",
+ "--tag=${pushImage}:${env.BRANCH_NAME}"
+ ]
+
+ def isLatest = ['master', 'main'].contains(env.BRANCH_NAME)
+ if (isLatest)
+ buildArgs.push("--tag=${pushImage}:latest")
+
+ // FIXME: Nested docker.withRegistry() does not work
+ // https://issues.jenkins.io/browse/JENKINS-59777
+ withCredentials([usernamePassword(
+ credentialsId: 'registry-cache',
+ usernameVariable: 'CACHE_USR',
+ passwordVariable: 'CACHE_PSW')
+ ]) {
+ docker.withRegistry("https://${env.REGISTRY}", 'docker-registry') {
+ sh 'echo "$CACHE_PSW" | docker login --username "$CACHE_USR" --password-stdin "http://$REGISTRY_CACHE"'
+ sh "docker buildx create ${buildxArgs.join(' ')}"
+ docker.build(baseImage, buildArgs.join(' '))
+ }
+ }
+}
+
diff --git a/package.json b/package.json
index f903a5ab6..70725c5e9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "salix-front",
- "version": "25.18.0",
+ "version": "25.22.0",
"description": "Salix frontend",
"productName": "Salix",
"author": "Verdnatura",
diff --git a/quasar.config.js.temporary.compiled.1744020058024.mjs b/quasar.config.js.temporary.compiled.1744020058024.mjs
deleted file mode 100644
index 54ecb84d9..000000000
--- a/quasar.config.js.temporary.compiled.1744020058024.mjs
+++ /dev/null
@@ -1,227 +0,0 @@
-/* eslint-disable */
-/**
- * THIS FILE IS GENERATED AUTOMATICALLY.
- * 1. DO NOT edit this file directly as it won't do anything.
- * 2. EDIT the original quasar.config file INSTEAD.
- * 3. DO NOT git commit this file. It should be ignored.
- *
- * This file is still here because there was an error in
- * the original quasar.config file and this allows you to
- * investigate the Node.js stack error.
- *
- * After you fix the original file, this file will be
- * deleted automatically.
- **/
-
-
-// quasar.config.js
-import { configure } from "quasar/wrappers";
-import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
-import path from "path";
-var __quasar_inject_dirname__ = "/home/jsegarra/Projects/salix-front";
-var target = `http://${process.env.CI ? "back" : "localhost"}:3000`;
-var quasar_config_default = configure(function() {
- return {
- eslint: {
- // fix: true,
- // include = [],
- // exclude = [],
- // rawOptions = {},
- warnings: true,
- errors: true
- },
- // https://v2.quasar.dev/quasar-cli/prefetch-feature
- // preFetch: true,
- // app boot file (/src/boot)
- // --> boot files are part of "main.js"
- // https://v2.quasar.dev/quasar-cli/boot-files
- boot: ["i18n", "axios", "vnDate", "validations", "quasar", "quasar.defaults"],
- // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
- css: ["app.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",
- "material-icons-outlined",
- "material-symbols-outlined"
- ],
- // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
- build: {
- target: {
- browser: ["es2022", "edge88", "firefox78", "chrome87", "safari13.1"],
- node: "node20"
- },
- vueRouterMode: "hash",
- // available values: 'hash', 'history'
- // vueRouterBase,
- // vueDevtools,
- // vueOptionsAPI: false,
- // rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
- // publicPath: '/',
- // analyze: true,
- // env: {},
- rawDefine: {
- "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV)
- },
- // ignorePublicFolder: true,
- // minify: false,
- // polyfillModulePreload: true,
- // distDir
- extendViteConf(viteConf) {
- delete viteConf.build.polyfillModulePreload;
- viteConf.build.modulePreload = {
- polyfill: false
- };
- },
- // viteVuePluginOptions: {},
- alias: {
- composables: path.join(__quasar_inject_dirname__, "./src/composables"),
- filters: path.join(__quasar_inject_dirname__, "./src/filters")
- },
- vitePlugins: [
- [
- VueI18nPlugin({
- strictMessage: false,
- runtimeOnly: false,
- include: [
- path.resolve(__quasar_inject_dirname__, "./src/i18n/locale/**"),
- path.resolve(__quasar_inject_dirname__, "./src/pages/**/locale/**")
- ]
- })
- ]
- ]
- },
- // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
- devServer: {
- server: {
- type: "http"
- },
- proxy: {
- "/api": {
- target,
- logLevel: "debug",
- changeOrigin: true,
- secure: false
- }
- },
- open: false,
- allowedHosts: [
- "front",
- // Agrega este nombre de host
- "localhost"
- // Opcional, para pruebas locales
- ]
- },
- // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
- framework: {
- config: {
- config: {
- dark: "auto"
- }
- },
- lang: "en-GB",
- // 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"],
- all: "auto",
- autoImportComponentCase: "pascal"
- },
- // animations: 'all', // --- includes all animations
- // https://v2.quasar.dev/options/animations
- animations: [],
- // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#property-sourcefiles
- // sourceFiles: {
- // rootComponent: 'src/App.vue',
- // router: 'src/router/index',
- // store: 'src/store/index',
- // registerServiceWorker: 'src-pwa/register-service-worker',
- // serviceWorker: 'src-pwa/custom-service-worker',
- // pwaManifestFile: 'src-pwa/manifest.json',
- // electronMain: 'src-electron/electron-main',
- // electronPreload: 'src-electron/electron-preload'
- // },
- // https://v2.quasar.dev/quasar-cli/developing-ssr/configuring-ssr
- ssr: {
- // ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name!
- // will mess up SSR
- // extendSSRWebserverConf (esbuildConf) {},
- // extendPackageJson (json) {},
- pwa: false,
- // manualStoreHydration: true,
- // manualPostHydrationTrigger: true,
- prodPort: 3e3,
- // The default port that the production server should use
- // (gets superseded if process.env.PORT is specified at runtime)
- middlewares: [
- "render"
- // keep this as last one
- ]
- },
- // https://v2.quasar.dev/quasar-cli/developing-pwa/configuring-pwa
- pwa: {
- workboxMode: "generateSW",
- // or 'injectManifest'
- injectPwaMetaTags: true,
- swFilename: "sw.js",
- manifestFilename: "manifest.json",
- useCredentialsForManifestTag: false
- // useFilenameHashes: true,
- // extendGenerateSWOptions (cfg) {}
- // extendInjectManifestOptions (cfg) {},
- // extendManifestJson (json) {}
- // extendPWACustomSWConf (esbuildConf) {}
- },
- // Full list of options: https://v2.quasar.dev/quasar-cli/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/developing-capacitor-apps/configuring-capacitor
- capacitor: {
- hideSplashscreen: true
- },
- // Full list of options: https://v2.quasar.dev/quasar-cli/developing-electron-apps/configuring-electron
- electron: {
- // extendElectronMainConf (esbuildConf)
- // extendElectronPreloadConf (esbuildConf)
- inspectPort: 5858,
- 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: "salix-frontend"
- }
- },
- // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
- bex: {
- contentScripts: ["my-content-script"]
- // extendBexScriptsConf (esbuildConf) {}
- // extendBexManifestJson (json) {}
- }
- };
-});
-export {
- quasar_config_default as default
-};
diff --git a/src/boot/quasar.js b/src/boot/quasar.js
index a4f2ba0fe..7fb814c1f 100644
--- a/src/boot/quasar.js
+++ b/src/boot/quasar.js
@@ -7,7 +7,7 @@ import { QLayout } from 'quasar';
import mainShortcutMixin from './mainShortcutMixin';
import { useCau } from 'src/composables/useCau';
-export default boot(({ app }) => {
+export default boot(({ app, router }) => {
QForm.mixins = [qFormMixin];
QLayout.mixins = [mainShortcutMixin];
@@ -22,6 +22,14 @@ export default boot(({ app }) => {
}
switch (response?.status) {
+ case 401:
+ if (!router.currentRoute.value.name.toLowerCase().includes('login')) {
+ message = 'errors.sessionExpired';
+ } else message = 'login.loginError';
+ break;
+ case 403:
+ if (!message || message.toLowerCase() === 'access denied')
+ message = 'errors.accessDenied';
case 422:
if (error.name == 'ValidationError')
message += ` "${responseError.details.context}.${Object.keys(
diff --git a/src/components/FormModel.vue b/src/components/FormModel.vue
index 43a0d63f0..29f5f1a5a 100644
--- a/src/components/FormModel.vue
+++ b/src/components/FormModel.vue
@@ -102,6 +102,10 @@ const $props = defineProps({
type: Boolean,
default: false,
},
+ customMethod: {
+ type: String,
+ default: null,
+ },
});
const emit = defineEmits(['onFetch', 'onDataSaved', 'submit']);
const modelValue = computed(
@@ -237,7 +241,9 @@ async function save() {
const url =
$props.urlCreate || $props.urlUpdate || $props.url || arrayData.store.url;
const response = await Promise.resolve(
- $props.saveFn ? $props.saveFn(body) : axios[method](url, body),
+ $props.saveFn
+ ? $props.saveFn(body)
+ : axios[$props.customMethod ?? method](url, body),
);
if ($props.urlCreate) notify('globals.dataCreated', 'positive');
diff --git a/src/components/VnTable/VnColumn.vue b/src/components/VnTable/VnColumn.vue
index 3ce62c5de..05c764d73 100644
--- a/src/components/VnTable/VnColumn.vue
+++ b/src/components/VnTable/VnColumn.vue
@@ -181,6 +181,10 @@ const col = computed(() => {
newColumn.component = 'checkbox';
if ($props.default && !newColumn.component) newColumn.component = $props.default;
+ if (typeof newColumn.component !== 'string') {
+ newColumn.attrs = { ...newColumn.component?.attrs, autofocus: $props.autofocus };
+ newColumn.event = { ...newColumn.component?.event, ...$props?.eventHandlers };
+ }
return newColumn;
});
diff --git a/src/components/VnTable/VnContextMenu.vue b/src/components/VnTable/VnContextMenu.vue
new file mode 100644
index 000000000..c20c213f5
--- /dev/null
+++ b/src/components/VnTable/VnContextMenu.vue
@@ -0,0 +1,136 @@
+
+
+
{{ card.subName }}
@@ -57,11 +57,12 @@ const card = toRef(props, 'item');