- inizio di modifiche all'editor di Pagine Web

This commit is contained in:
Surya Paolo
2025-09-05 01:05:36 +02:00
parent 574f389200
commit 63d0f865fd
55 changed files with 5356 additions and 3600 deletions

4
.env
View File

@@ -1,6 +1,6 @@
VITE_APP_VERSION="1.2.68" VITE_APP_VERSION="1.2.69"
VITE_LANG_DEFAULT="it" VITE_LANG_DEFAULT="it"
VITE_PAO_APP_ID="KKPPAA5KJK435J3KSS9F9D8S9F8SD98F9SDF" VITE_PAO_APP_ID="KKPPAA5KJK435J3KSS9F9D8S9F8SD98F9SDF"
VITE_SERVICE_WORKER_FILE="sw-1.2.68.js" VITE_SERVICE_WORKER_FILE="sw-1.2.69.js"
VITE_PROJECT_ID_MAIN="5cc0a13fe5c9d156728f400a" VITE_PROJECT_ID_MAIN="5cc0a13fe5c9d156728f400a"
VITE_VUE_ROUTER_MODE="history" VITE_VUE_ROUTER_MODE="history"

View File

@@ -10,7 +10,7 @@
<meta name="description" content="<%= productDescription %>"> <meta name="description" content="<%= productDescription %>">
<meta name="format-detection" content="telephone=no"> <meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no"> <meta name="msapplication-tap-highlight" content="no">
<meta name="version" content="1.2.68"> <meta name="version" content="1.2.69">
<meta name="viewport" <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<% } %>"> 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<% } %>">

View File

@@ -1,6 +1,6 @@
{ {
"name": "nuovomondo", "name": "nuovomondo",
"version": "1.2.68", "version": "1.2.69",
"description": "Nuovo Mondo", "description": "Nuovo Mondo",
"productName": "Nuovo Mondo", "productName": "Nuovo Mondo",
"author": "Surya", "author": "Surya",
@@ -9,11 +9,11 @@
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "PORT=8083 APP_VERSION='1.2.68' quasar dev", "dev": "APP_VERSION='1.2.69' PORT=8083 quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev", "dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build", "build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwa": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwatest": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit", "type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch", "type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa", "buildspa": "quasar build -m spa",
@@ -21,117 +21,117 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt", "fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.68' quasar dev -m pwa", "pwa": "NODE_ENV=development PORT=8094 APP_VERSION='1.2.69' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.68' quasar dev", "spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.69' quasar dev",
"debug": "quasar dev --mode debug", "debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0", "test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js", "generate-sw": "workbox generateSW workbox-config.js",
"postinstall": "quasar prepare" "postinstall": "quasar prepare"
}, },
"dependencies": { "dependencies": {
"@cubejs-client/core": "^1.2.26", "@cubejs-client/core": "^1.3.21",
"@quasar/extras": "^1.16.17", "@quasar/extras": "^1.17.0",
"@quasar/quasar-ui-qcalendar": "^4.1.2", "@quasar/quasar-ui-qcalendar": "^4.1.2",
"@types/jsbarcode": "^3.11.4", "@types/jsbarcode": "^3.11.4",
"@types/leaflet": "^1.9.17", "@types/leaflet": "^1.9.18",
"@vue/compat": "^3.5.13", "@vue/compat": "^3.5.16",
"@vue/compiler-sfc": "^3.5.13", "@vue/compiler-sfc": "^3.5.16",
"@vuelidate/core": "^2.0.3", "@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4", "@vuelidate/validators": "^2.0.4",
"acorn": "^8.14.1", "acorn": "^8.15.0",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"apexcharts": "^4.7.0",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"axios": "^1.8.4", "axios": "^1.9.0",
"bcryptjs": "^3.0.2", "bcryptjs": "^3.0.2",
"chart.js": "^4.4.8", "chart.js": "^4.4.9",
"core-js": "^3.41.0", "core-js": "^3.43.0",
"crypto-browserify": "^3.12.1", "crypto-browserify": "^3.12.1",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"echarts": "5.6.0", "echarts": "5.6.0",
"eslint-plugin-n": "^17.16.2", "eslint-plugin-n": "^17.19.0",
"eslint-plugin-quasar": "^1.1.0", "eslint-plugin-quasar": "^1.1.0",
"graphql": "^16.10.0", "gsap": "^3.13.0",
"graphql-tag": "^2.12.6",
"gsap": "^3.12.7",
"html2pdf.js": "^0.10.3", "html2pdf.js": "^0.10.3",
"jquery": "^3.7.1", "jquery": "^3.7.1",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"jsbarcode": "^3.11.6", "jsbarcode": "^3.12.1",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"leaflet-routing-machine": "^3.2.12", "leaflet-routing-machine": "^3.2.12",
"leaflet.markercluster": "^1.5.3", "leaflet.markercluster": "^1.5.3",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mongoose-paginate-v2": "^1.9.1",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^3.0.1", "pinia": "^3.0.3",
"qrcode-vue3": "^1.7.1",
"quasar": "^2.18.1", "quasar": "^2.18.1",
"quasar-extras": "^2.0.9", "quasar-extras": "^2.0.9",
"register-service-worker": "^1.7.2", "register-service-worker": "^1.7.2",
"scrollreveal": "^4.0.9", "scrollreveal": "^4.0.9",
"typescript-eslint": "^8.27.0", "typescript-eslint": "^8.34.0",
"vee-validate": "^4.15.0", "vee-validate": "^4.15.1",
"vue": "^3.5.13", "vue": "^3.5.16",
"vue-class-component": "^8.0.0-rc.1", "vue-class-component": "^8.0.0-rc.1",
"vue-country-code": "^1.1.3", "vue-country-code": "^1.1.3",
"vue-echarts": "^7.0.3", "vue-echarts": "^7.0.3",
"vue-i18n": "^11.1.2", "vue-i18n": "^11.1.5",
"vue-idb": "^0.2.0", "vue-idb": "^0.2.0",
"vue-image-zoomer": "^2.4.4",
"vue-property-decorator": "^10.0.0-rc.3", "vue-property-decorator": "^10.0.0-rc.3",
"vue-router": "^4.5.0", "vue-router": "^4.5.1",
"vue-scroll-reveal": "^2.1.0", "vue-scroll-reveal": "^2.1.0",
"vue-social-sharing": "^4.0.0-alpha4", "vue-social-sharing": "^4.0.0-alpha4",
"vue-svgicon": "^4.0.0-alpha.3", "vue-svgicon": "^4.0.0-alpha.3",
"vue-timeago3": "^2.3.2", "vue-timeago3": "^2.3.2",
"vue2-dragula": "^2.5.5", "vue2-dragula": "^2.5.5",
"vue3-pdf-app": "^1.0.3", "vue3-apexcharts": "^1.8.0",
"vue3-qr-reader": "^1.0.0", "vue3-qr-reader": "^1.0.0",
"vuedraggable": "^4.1.0",
"vuex": "^4.1.0", "vuex": "^4.1.0",
"vuex-router-sync": "^6.0.0-rc.1", "vuex-router-sync": "^6.0.0-rc.1",
"workbox-core": "^7.3.0", "workbox-core": "^7.3.0",
"workbox-precaching": "^7.3.0", "workbox-precaching": "^7.3.0",
"workbox-routing": "^7.3.0", "workbox-routing": "^7.3.0",
"workbox-strategies": "^7.3.0", "workbox-strategies": "^7.3.0",
"workbox-window": "^7.3.0" "workbox-window": "^7.3.0",
"xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.23.0", "@eslint/js": "^9.28.0",
"@intlify/unplugin-vue-i18n": "^6.0.5", "@intlify/unplugin-vue-i18n": "^6.0.8",
"@quasar/app-vite": "^2.1.4", "@quasar/app-vite": "^2.2.1",
"@types/google.maps": "^3.58.1", "@types/google.maps": "^3.58.1",
"@types/jest": "^29.5.14", "@types/jest": "^29.5.14",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/node": "^22.13.11", "@types/node": "^24.0.1",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/vue-tel-input": "^2.1.7", "@types/vue-tel-input": "^2.1.7",
"@types/vuelidate": "^0.7.22", "@types/vuelidate": "^0.7.22",
"@vue/devtools": "^7.7.2", "@vue/devtools": "^7.7.6",
"@vue/eslint-config-prettier": "^10.2.0", "@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.5.0", "@vue/eslint-config-typescript": "^14.5.0",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"eslint": "9", "eslint": "9",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-vue": "^10.0.0", "eslint-plugin-vue": "^10.2.0",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"globals": "^16.0.0", "globals": "^16.2.0",
"http-proxy-middleware": "^3.0.3", "http-proxy-middleware": "^3.0.5",
"jest": "^29.7.0", "jest": "^30.0.0",
"json-loader": "^0.5.7", "json-loader": "^0.5.7",
"nodemon": "^3.1.9", "nodemon": "^3.1.10",
"npm-check-updates": "^17.1.16", "npm-check-updates": "^18.0.1",
"parcel": "^2.14.1", "parcel": "^2.15.2",
"postcss": "^8.5.3", "postcss": "^8.5.5",
"postcss-loader": "^8.1.1", "postcss-loader": "^8.1.1",
"prettier": "3", "prettier": "3",
"strip-ansi": "=7.1.0", "strip-ansi": "=7.1.0",
"ts-jest": "^29.2.6", "ts-jest": "^29.4.0",
"typescript": "5.7.3", "typescript": "5.8.3",
"vite-plugin-checker": "^0.9.1", "vite-plugin-checker": "^0.9.3",
"vue-cli-plugin-element-ui": "^1.1.4", "vue-cli-plugin-element-ui": "^1.1.4",
"vue-eslint-parser": "^10.1.1", "vue-eslint-parser": "^10.1.3",
"vue-tsc": "^2.2.8", "vue-tsc": "^2.2.10",
"vueify": "^9.4.1", "vueify": "^9.4.1",
"workbox-build": "^7.3.0" "workbox-build": "^7.3.0"
}, },

View File

@@ -238,7 +238,7 @@ export default defineConfig((ctx) => {
port: parseInt(process.env.PORT, 10), port: parseInt(process.env.PORT, 10),
vueDevtools: false, // automatically opening remote Vue Devtools vueDevtools: false, // automatically opening remote Vue Devtools
open: false, // opens browser window automatically open: false, // opens browser window automatically
hot: true, // Disable hot module replacement hot: true, // Enable hot module replacement
headers: { headers: {
'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*', 'Access-Control-Allow-Headers': '*',

View File

@@ -1,6 +1,6 @@
{ {
"name": "cnm", "name": "cnm",
"version": "1.2.68", "version": "1.2.69",
"description": "Comunita Nuovo Mondo", "description": "Comunita Nuovo Mondo",
"productName": "ComunitaNuovoMondo", "productName": "ComunitaNuovoMondo",
"author": "Surya", "author": "Surya",
@@ -9,7 +9,7 @@
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "PORT=8083 APP_VERSION='1.2.68' quasar dev", "dev": "PORT=8083 APP_VERSION='1.2.69' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev", "dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build", "build": "quasar build",
"buildpwa": "NODE_ENV=production quasar build -m pwa", "buildpwa": "NODE_ENV=production quasar build -m pwa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt", "fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.68' quasar dev -m pwa", "pwa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.69' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.68' quasar dev", "spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.69' quasar dev",
"debug": "quasar dev --mode debug", "debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0", "test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js", "generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -47,7 +47,7 @@ function getDynamicPages(site: ISites): IListRoutes[] {
const baseroutes: IListRoutes[] = [ const baseroutes: IListRoutes[] = [
{ {
active: true, active: true,
order: 5, order: -1,
path: '/', path: '/',
materialIcon: 'home', materialIcon: 'home',
name: 'pages.home', name: 'pages.home',
@@ -321,3 +321,5 @@ export const static_data = {
preLoadImages, preLoadImages,
preloadedimages, preloadedimages,
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "freeplanet", "name": "freeplanet",
"version": "1.2.68", "version": "1.2.69",
"description": "freeplanet", "description": "freeplanet",
"productName": "freeplanet", "productName": "freeplanet",
"author": "Surya", "author": "Surya",
@@ -9,11 +9,11 @@
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "PORT=8087 APP_VERSION='1.2.68' quasar dev", "dev": "PORT=8087 APP_VERSION='1.2.69' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev", "dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build", "build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwa": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwatest": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit", "type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch", "type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa", "buildspa": "quasar build -m spa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt", "fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8097 APP_VERSION='1.2.68' quasar dev -m pwa", "pwa": "NODE_ENV=development PORT=8097 APP_VERSION='1.2.69' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8087 APP_VERSION='1.2.68' quasar dev", "spa": "NODE_ENV=development PORT=8087 APP_VERSION='1.2.69' quasar dev",
"debug": "quasar dev --mode debug", "debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0", "test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js", "generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "gruppomacro", "name": "gruppomacro",
"version": "1.2.68", "version": "1.2.69",
"productName": "Gruppo Macro", "productName": "Gruppo Macro",
"description": "Il Gruppo Editoriale Macro, attivo dal 1987, è leader europeo nella pubblicazione di libri per il benessere e la consapevolezza. Con oltre 1.500 titoli, promuove una visione armonica del mondo, offrendo opere di autori internazionali e italiani come Gregg Braden, Bruce Lipton, Joe Dispenza, Louise Hay, Eckhart Tolle e molti altri. Scopri un'editoria che abbraccia il corpo, la mente, lo spirito e l'ecologia.", "description": "Il Gruppo Editoriale Macro, attivo dal 1987, è leader europeo nella pubblicazione di libri per il benessere e la consapevolezza. Con oltre 1.500 titoli, promuove una visione armonica del mondo, offrendo opere di autori internazionali e italiani come Gregg Braden, Bruce Lipton, Joe Dispenza, Louise Hay, Eckhart Tolle e molti altri. Scopri un'editoria che abbraccia il corpo, la mente, lo spirito e l'ecologia.",
"author": "Surya", "author": "Surya",
@@ -9,20 +9,20 @@
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "PORT=8089 APP_VERSION='1.2.68' quasar dev", "dev": "PORT=8089 APP_VERSION='1.2.69' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev", "dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build", "build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwa": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwatest": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit", "type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch", "type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "APP_VERSION='1.2.68' quasar build -m spa", "buildspa": "APP_VERSION='1.2.69' quasar build -m spa",
"lint": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\"", "lint": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\"",
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt", "fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.68' quasar dev -m pwa", "pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.69' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8089 APP_VERSION='1.2.68' quasar dev", "spa": "NODE_ENV=development PORT=8089 APP_VERSION='1.2.69' quasar dev",
"debug": "quasar dev --mode debug", "debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0", "test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js", "generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "kolibrilab", "name": "kolibrilab",
"version": "1.2.68", "version": "1.2.69",
"description": "kolibrilab", "description": "kolibrilab",
"productName": "kolibrilab", "productName": "kolibrilab",
"author": "Surya Paolo", "author": "Surya Paolo",
@@ -8,11 +8,11 @@
"keywords": [], "keywords": [],
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"dev": "PORT=8083 APP_VERSION='1.2.68' quasar dev", "dev": "PORT=8083 APP_VERSION='1.2.69' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev", "dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build", "build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwa": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwatest": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit", "type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch", "type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa", "buildspa": "quasar build -m spa",
@@ -20,8 +20,8 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint --ext .ts,.vue --ignore-path .gitignore ./ --fix > file.out.txt", "fix": "eslint --ext .ts,.vue --ignore-path .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.68' quasar dev -m pwa", "pwa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.69' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.68' quasar dev", "spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.69' quasar dev",
"spanorefresh": "NODE_ENV=development NODE_OPTIONS=--max_old_space_size=4096 DEBUG=v8:* quasar dev -m spa", "spanorefresh": "NODE_ENV=development NODE_OPTIONS=--max_old_space_size=4096 DEBUG=v8:* quasar dev -m spa",
"test": "echo \"No test specified\" && exit 0", "test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js" "generate-sw": "workbox generateSW workbox-config.js"

View File

@@ -1,11 +1,11 @@
VITE_APP_ID="10" VITE_APP_ID="13"
VITE_APP_URL="https://test.nuovomondo.app" VITE_APP_URL="https://riso.app"
VITE_MONGODB_HOST="https://testapi.nuovomondo.app" VITE_MONGODB_HOST="https://api.riso.app"
VITE_LOGO_REG='cmn-logo-full.png' VITE_LOGO_REG='riso-logo-full.png'
VITE_PUBLICKEY_PUSH="BGXRf1TgcqocqD6J7qnRgCG7AvM2lxAoW7peb7UEzB4SxBb6DxGRdJ0UvD9ewnrB9KrSrh0-aDCODXBm7sZ1DDs" VITE_PUBLICKEY_PUSH="BGXRf1TgcqocqD6J7qnRgCG7AvM2lxAoW7peb7UEzB4SxBb6DxGRdJ0UvD9ewnrB9KrSrh0-aDCODXBm7sZ1DDs"
VITE_DEBUG="1" VITE_DEBUG="0"
VITE_VUE_APP_ISTEST=1 VITE_VUE_APP_ISTEST="0"
DIRECTORY_LOCAL="myprojplanet_vite" DIRECTORY_LOCAL=myprojplanet_vite
DIRECTORY_SERVER="/var/www/nodejs_test.riso_server" DIRECTORY_SERVER=/var/www/nodejs_riso_server
SERVERDIR_WEBSITE="/var/www/test.nuovomondo.app" SERVERDIR_WEBSITE="/var/www/riso.app"
SERVERPW_WEBSITE="pwdadmin@1AOK" SERVERPW_WEBSITE="pwdadmin@1AOK"

View File

@@ -1,6 +1,6 @@
{ {
"name": "nuovomondo", "name": "nuovomondo",
"version": "1.2.68", "version": "1.2.69",
"description": "Nuovo Mondo", "description": "Nuovo Mondo",
"productName": "Nuovo Mondo", "productName": "Nuovo Mondo",
"author": "Surya", "author": "Surya",
@@ -9,11 +9,11 @@
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "PORT=8083 APP_VERSION='1.2.68' quasar dev", "dev": "APP_VERSION='1.2.69' PORT=8083 quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev", "dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build", "build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwa": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwatest": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit", "type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch", "type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa", "buildspa": "quasar build -m spa",
@@ -21,74 +21,74 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt", "fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.68' quasar dev -m pwa", "pwa": "NODE_ENV=development PORT=8094 APP_VERSION='1.2.69' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.68' quasar dev", "spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.69' quasar dev",
"debug": "quasar dev --mode debug", "debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0", "test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js", "generate-sw": "workbox generateSW workbox-config.js",
"postinstall": "quasar prepare" "postinstall": "quasar prepare"
}, },
"dependencies": { "dependencies": {
"@cubejs-client/core": "^1.2.26", "@cubejs-client/core": "^1.3.21",
"@quasar/extras": "^1.16.17", "@quasar/extras": "^1.17.0",
"@quasar/quasar-ui-qcalendar": "^4.1.2", "@quasar/quasar-ui-qcalendar": "^4.1.2",
"@types/jsbarcode": "^3.11.4", "@types/jsbarcode": "^3.11.4",
"@types/leaflet": "^1.9.17", "@types/leaflet": "^1.9.18",
"@vue/compat": "^3.5.13", "@vue/compat": "^3.5.16",
"@vue/compiler-sfc": "^3.5.13", "@vue/compiler-sfc": "^3.5.16",
"@vuelidate/core": "^2.0.3", "@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4", "@vuelidate/validators": "^2.0.4",
"acorn": "^8.14.1", "acorn": "^8.15.0",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"apexcharts": "^4.7.0",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"axios": "^1.8.4", "axios": "^1.9.0",
"bcryptjs": "^3.0.2", "bcryptjs": "^3.0.2",
"chart.js": "^4.4.8", "chart.js": "^4.4.9",
"core-js": "^3.41.0", "core-js": "^3.43.0",
"crypto-browserify": "^3.12.1", "crypto-browserify": "^3.12.1",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"echarts": "5.6.0", "echarts": "5.6.0",
"eslint-plugin-n": "^17.16.2", "eslint-plugin-n": "^17.19.0",
"eslint-plugin-quasar": "^1.1.0", "eslint-plugin-quasar": "^1.1.0",
"graphql": "^16.10.0", "gsap": "^3.13.0",
"graphql-tag": "^2.12.6",
"gsap": "^3.12.7",
"html2pdf.js": "^0.10.3", "html2pdf.js": "^0.10.3",
"jquery": "^3.7.1", "jquery": "^3.7.1",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"jsbarcode": "^3.11.6", "jsbarcode": "^3.12.1",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"leaflet-routing-machine": "^3.2.12", "leaflet-routing-machine": "^3.2.12",
"leaflet.markercluster": "^1.5.3", "leaflet.markercluster": "^1.5.3",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mongoose-paginate-v2": "^1.9.1",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^3.0.1", "pinia": "^3.0.3",
"qrcode-vue3": "^1.7.1",
"quasar": "^2.18.1", "quasar": "^2.18.1",
"quasar-extras": "^2.0.9", "quasar-extras": "^2.0.9",
"register-service-worker": "^1.7.2", "register-service-worker": "^1.7.2",
"scrollreveal": "^4.0.9", "scrollreveal": "^4.0.9",
"typescript-eslint": "^8.27.0", "typescript-eslint": "^8.34.0",
"vee-validate": "^4.15.0", "vee-validate": "^4.15.1",
"vue": "^3.5.13", "vue": "^3.5.16",
"vue-class-component": "^8.0.0-rc.1", "vue-class-component": "^8.0.0-rc.1",
"vue-country-code": "^1.1.3", "vue-country-code": "^1.1.3",
"vue-echarts": "^7.0.3", "vue-echarts": "^7.0.3",
"vue-i18n": "^11.1.2", "vue-i18n": "^11.1.5",
"vue-idb": "^0.2.0", "vue-idb": "^0.2.0",
"vue-image-zoomer": "^2.4.4",
"vue-property-decorator": "^10.0.0-rc.3", "vue-property-decorator": "^10.0.0-rc.3",
"vue-router": "^4.5.0", "vue-router": "^4.5.1",
"vue-scroll-reveal": "^2.1.0", "vue-scroll-reveal": "^2.1.0",
"vue-social-sharing": "^4.0.0-alpha4", "vue-social-sharing": "^4.0.0-alpha4",
"vue-svgicon": "^4.0.0-alpha.3", "vue-svgicon": "^4.0.0-alpha.3",
"vue-timeago3": "^2.3.2", "vue-timeago3": "^2.3.2",
"vue2-dragula": "^2.5.5", "vue2-dragula": "^2.5.5",
"vue3-pdf-app": "^1.0.3", "vue3-apexcharts": "^1.8.0",
"vue3-qr-reader": "^1.0.0", "vue3-qr-reader": "^1.0.0",
"vuedraggable": "^4.1.0",
"vuex": "^4.1.0", "vuex": "^4.1.0",
"xlsx": "^0.18.5",
"vuex-router-sync": "^6.0.0-rc.1", "vuex-router-sync": "^6.0.0-rc.1",
"workbox-core": "^7.3.0", "workbox-core": "^7.3.0",
"workbox-precaching": "^7.3.0", "workbox-precaching": "^7.3.0",
@@ -97,41 +97,41 @@
"workbox-window": "^7.3.0" "workbox-window": "^7.3.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.23.0", "@eslint/js": "^9.28.0",
"@intlify/unplugin-vue-i18n": "^6.0.5", "@intlify/unplugin-vue-i18n": "^6.0.8",
"@quasar/app-vite": "^2.1.4", "@quasar/app-vite": "^2.2.1",
"@types/google.maps": "^3.58.1", "@types/google.maps": "^3.58.1",
"@types/jest": "^29.5.14", "@types/jest": "^29.5.14",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/node": "^22.13.11", "@types/node": "^24.0.1",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/vue-tel-input": "^2.1.7", "@types/vue-tel-input": "^2.1.7",
"@types/vuelidate": "^0.7.22", "@types/vuelidate": "^0.7.22",
"@vue/devtools": "^7.7.2", "@vue/devtools": "^7.7.6",
"@vue/eslint-config-prettier": "^10.2.0", "@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.5.0", "@vue/eslint-config-typescript": "^14.5.0",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"eslint": "9", "eslint": "9",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-vue": "^10.0.0", "eslint-plugin-vue": "^10.2.0",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"globals": "^16.0.0", "globals": "^16.2.0",
"http-proxy-middleware": "^3.0.3", "http-proxy-middleware": "^3.0.5",
"jest": "^29.7.0", "jest": "^30.0.0",
"json-loader": "^0.5.7", "json-loader": "^0.5.7",
"nodemon": "^3.1.9", "nodemon": "^3.1.10",
"npm-check-updates": "^17.1.16", "npm-check-updates": "^18.0.1",
"parcel": "^2.14.1", "parcel": "^2.15.2",
"postcss": "^8.5.3", "postcss": "^8.5.5",
"postcss-loader": "^8.1.1", "postcss-loader": "^8.1.1",
"prettier": "3", "prettier": "3",
"strip-ansi": "=7.1.0", "strip-ansi": "=7.1.0",
"ts-jest": "^29.2.6", "ts-jest": "^29.4.0",
"typescript": "5.7.3", "typescript": "5.8.3",
"vite-plugin-checker": "^0.9.1", "vite-plugin-checker": "^0.9.3",
"vue-cli-plugin-element-ui": "^1.1.4", "vue-cli-plugin-element-ui": "^1.1.4",
"vue-eslint-parser": "^10.1.1", "vue-eslint-parser": "^10.1.3",
"vue-tsc": "^2.2.8", "vue-tsc": "^2.2.10",
"vueify": "^9.4.1", "vueify": "^9.4.1",
"workbox-build": "^7.3.0" "workbox-build": "^7.3.0"
}, },

View File

@@ -1,6 +1,6 @@
{ {
"name": "nutriben", "name": "nutriben",
"version": "1.2.68", "version": "1.2.69",
"description": "Nutriben", "description": "Nutriben",
"productName": "Nutriben", "productName": "Nutriben",
"author": "Surya", "author": "Surya",
@@ -9,20 +9,20 @@
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "PORT=8093 APP_VERSION='1.2.68' quasar dev", "dev": "PORT=8093 APP_VERSION='1.2.69' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev", "dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build", "build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwa": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwatest": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit", "type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch", "type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "APP_VERSION='1.2.68' quasar build -m spa", "buildspa": "APP_VERSION='1.2.69' quasar build -m spa",
"lint": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\"", "lint": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\"",
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt", "fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.68' quasar dev -m pwa", "pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.69' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.68' quasar dev", "spa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.69' quasar dev",
"debug": "quasar dev --mode debug", "debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0", "test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js", "generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "piuchebuono", "name": "piuchebuono",
"version": "1.2.68", "version": "1.2.69",
"description": "PiuCheBuono", "description": "PiuCheBuono",
"productName": "PiuCheBuono", "productName": "PiuCheBuono",
"author": "Surya", "author": "Surya",
@@ -9,11 +9,11 @@
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "PORT=8085 APP_VERSION='1.2.68' quasar dev", "dev": "PORT=8085 APP_VERSION='1.2.69' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev", "dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build", "build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwa": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwatest": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit", "type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch", "type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa", "buildspa": "quasar build -m spa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt", "fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8085 APP_VERSION='1.2.68' quasar dev -m pwa", "pwa": "NODE_ENV=development PORT=8085 APP_VERSION='1.2.69' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8085 APP_VERSION='1.2.68' quasar dev", "spa": "NODE_ENV=development PORT=8085 APP_VERSION='1.2.69' quasar dev",
"debug": "quasar dev --mode debug", "debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0", "test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js", "generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "riso", "name": "riso",
"version": "1.2.68", "version": "1.2.69",
"productName": "Riso 💚 - Rete Italiana Scambi Orizzontali", "productName": "Riso 💚 - Rete Italiana Scambi Orizzontali",
"description": "Progetto RISO (Rete Italiana Scambi Orizzontali) promuove una rete di comunità locali che favoriscono scambi di beni, servizi e ospitalità. Con l'App RISO, sviluppata per facilitare il baratto, il dono e l'uso di monete alternative come i RIS, il progetto crea legami autentici basati sulla fiducia e sostenibilità. Partecipa agli scambi e costruisci una comunità più consapevole e autosufficiente.", "description": "Progetto RISO (Rete Italiana Scambi Orizzontali) promuove una rete di comunità locali che favoriscono scambi di beni, servizi e ospitalità. Con l'App RISO, sviluppata per facilitare il baratto, il dono e l'uso di monete alternative come i RIS, il progetto crea legami autentici basati sulla fiducia e sostenibilità. Partecipa agli scambi e costruisci una comunità più consapevole e autosufficiente.",
"author": "Surya", "author": "Surya",
@@ -9,11 +9,11 @@
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "APP_VERSION='1.2.68' PORT=8084 quasar dev", "dev": "APP_VERSION='1.2.69' PORT=8084 quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev", "dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build", "build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwa": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa", "buildpwatest": "NODE_ENV=production APP_VERSION='1.2.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit", "type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch", "type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa", "buildspa": "quasar build -m spa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt", "lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt", "fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8094 APP_VERSION='1.2.68' quasar dev -m pwa", "pwa": "NODE_ENV=development PORT=8094 APP_VERSION='1.2.69' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8084 APP_VERSION='1.2.68' quasar dev", "spa": "NODE_ENV=development PORT=8084 APP_VERSION='1.2.69' quasar dev",
"debug": "quasar dev --mode debug", "debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0", "test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js", "generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1 +1 @@
TERMINA DI LAVORARE SU nuovomondo.app: (Sovrascrivo!) TERMINA DI LAVORARE SU riso.app: (Sovrascrivo!)

View File

@@ -3,7 +3,7 @@
/* global workbox */ /* global workbox */
/* global cfgenv */ /* global cfgenv */
const VITE_APP_VERSION = '1.2.68'; const VITE_APP_VERSION = '1.2.69';
// Costanti di configurazione // Costanti di configurazione
const DYNAMIC_CACHE = 'dynamic-cache-v2'; const DYNAMIC_CACHE = 'dynamic-cache-v2';

View File

@@ -174,6 +174,9 @@ export const shared_consts = {
SEARCHPRODUCT: 430, SEARCHPRODUCT: 430,
RACCOLTE_CATALOGHI: 450, RACCOLTE_CATALOGHI: 450,
STAT_PAGES: 460, STAT_PAGES: 460,
SECTION: 1000,
ROW: 1100,
COLUMN: 1200,
}, },
QUERYTYPE_MYGROUP: 1, QUERYTYPE_MYGROUP: 1,
@@ -2008,6 +2011,22 @@ export const shared_consts = {
], ],
TypesElem: [ TypesElem: [
{
value: 1000, //ELEMTYPE.SECTION,
label: 'Sezione',
icon: 'fas fa-th-large',
},
{
value: 1100, //ELEMTYPE.ROW,
label: 'Riga',
icon: 'fas fa-th-large',
},
{
value: 1200, // ELEMTYPE.COLUMN,
label: 'Colonna',
icon: 'fas fa-th-large',
},
{ {
value: 20, value: 20,
label: 'Testo', label: 'Testo',

View File

@@ -11,6 +11,7 @@ import { CMyTeacher } from '@src/components/CMyTeacher'
// @ts-ignore // @ts-ignore
import MixinOperator from '../../mixins/mixin-operator' import MixinOperator from '../../mixins/mixin-operator'
import MixinUsers from '../../mixins/mixin-users' import MixinUsers from '../../mixins/mixin-users'
import { useI18n } from 'vue-i18n';
export default defineComponent({ export default defineComponent({
name: 'CCardDiscipline', name: 'CCardDiscipline',

View File

@@ -357,7 +357,7 @@ export default defineComponent({
recOrderCart.value = rissconto.mycart recOrderCart.value = rissconto.mycart
}; };
} }
} catch (error) { } catch (error: any) {
console.log('error ApplicaSconto', error); console.log('error ApplicaSconto', error);
tools.showNegativeNotif($q, `Sconto Non Applicato! ${error?.message || ''}`); tools.showNegativeNotif($q, `Sconto Non Applicato! ${error?.message || ''}`);
codice_sconto.value = ''; codice_sconto.value = '';

View File

@@ -0,0 +1,19 @@
<template>
<div class="column">
<slot></slot>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'CColumn'
});
</script>
<style scoped>
.column {
flex: 1;
}
</style>

View File

@@ -426,6 +426,7 @@ export default defineComponent({
function isIMG() { function isIMG() {
return props.filetype === shared_consts.FILETYPE.IMG; return props.filetype === shared_consts.FILETYPE.IMG;
} }
/*
const uploadFactory = async (files: readonly File[]) => { const uploadFactory = async (files: readonly File[]) => {
const userStore = useUserStore(); const userStore = useUserStore();
const url = getUrl(); const url = getUrl();
@@ -451,11 +452,11 @@ export default defineComponent({
// usa la tua logica centralizzata // usa la tua logica centralizzata
Api.checkTokenScaduto( Api.checkTokenScaduto(
status, status,
/*evitaloop*/ false, false,
url, url,
'POST', 'POST',
null, null,
/*setAuthToken*/ true true
); );
if (ret !== null) { if (ret !== null) {
// token aggiornato -> ritenta UNA volta // token aggiornato -> ritenta UNA volta
@@ -475,7 +476,7 @@ export default defineComponent({
throw err2; throw err2;
} }
} }
}; }; */
onMounted(created); onMounted(created);
@@ -514,7 +515,6 @@ export default defineComponent({
isIMG, isIMG,
isPDF, isPDF,
upl, upl,
uploadFactory,
t, t,
}; };
}, },

View File

@@ -1,44 +1,35 @@
<template> <template>
<div> <div class="map-container">
<div v-if="arrprovince" id="map" :style="`height:${myheight()}px; width:99%`"> <div id="map" style="height: 500px;"></div>
</div>
<!--
<div :style="`height:${myheight()}px; width:99%`">
<l-map
v-model="zoom"
v-model:zoom="zoom"
:center="[42.71, 12.934]"
@move="log('move')"
@click="getCoordinates"
>
<l-tile-layer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
></l-tile-layer>
<l-control-layers />
<span v-for="provincia in arrprovince" :key="provincia.nome">
<l-marker-cluster :options="clusterOptions">
<l-marker
v-if="provincia.userCount > 0"
:lat-lng="[provincia.lat, provincia.long]"
>
<l-popup
>{{ provincia.descr }}:
{{ provincia.userCount }} utenti</l-popup
>
</l-marker>
</l-marker-cluster>
</span>
</l-map>
<button @click="changeIcon">New kitten icon</button>
</div>
-->
</div> </div>
</template> </template>
<script lang="ts" src="./CMapUsers.ts">
<script lang="ts">
import { defineComponent, onMounted } from 'vue'
import { tools } from '@tools'
import { useGlobalStore } from 'app/src/store'
export default defineComponent({
name: 'CMapUsers',
setup() {
const globalStore = useGlobalStore()
onMounted(() => {
// Initialize map logic here
/*tools.initUserMap('map', {
center: { lat: 45.4642, lng: 9.1900 }, // Default to Milan coordinates
zoom: 6
})*/
})
return { tools, globalStore }
}
})
</script> </script>
<style lang="scss" scoped> <style scoped>
@import './CMapUsers.scss'; .map-container {
width: 100%;
height: 100%;
}
</style> </style>

View File

@@ -628,6 +628,15 @@ export default defineComponent({
); );
} }
function isLayoutContainer() {
const t = myel.value?.type;
return (
t === shared_consts.ELEMTYPE.SECTION ||
t === shared_consts.ELEMTYPE.ROW ||
t === shared_consts.ELEMTYPE.COLUMN
);
}
/*function updateElem(myvalue: any) { /*function updateElem(myvalue: any) {
console.log('updateElem', myvalue) console.log('updateElem', myvalue)
if (myel.value.type === shared_consts.ELEMTYPE.IMGTITLE) { if (myel.value.type === shared_consts.ELEMTYPE.IMGTITLE) {

View File

@@ -1,87 +1,122 @@
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { import { computed, defineComponent, onMounted, ref, toRef, watch, nextTick } from 'vue';
computed,
defineComponent, onMounted, ref, toRef, watch, nextTick,
} from 'vue'
import type { IOptCatalogo, ICoordGPS, IMyElem, ISocial } from '@src/model'; import type { IOptCatalogo, ICoordGPS, IMyElem, ISocial } from '@src/model';
import { IMyCard, IMyPage, IOperators } from '@src/model' import { IMyCard, IMyPage, IOperators } from '@src/model';
import { useGlobalStore } from '@store/globalStore' import { useGlobalStore } from '@store/globalStore';
import { CImgTitle } from '../CImgTitle/index' import { CImgTitle } from '../CImgTitle/index';
import { CImgPoster } from '@src/components/CImgPoster' import { CImgPoster } from '@src/components/CImgPoster';
import { CTitle } from '@src/components/CTitle/index' import CSection from '@src/components/CSection/CSection.vue';
import { CGridOriz } from '@src/components/CGridOriz/index' import CRow from '@src/components/CRow/CRow.vue';
import { ChatBot } from '@src/components/ChatBot/index' import CColumn from '@src/components/CColumn/CColumn.vue';
import { CCatalogList } from '@src/components/CCatalogList/index' import { CTitle } from '@src/components/CTitle/index';
import { CRaccoltaCataloghi } from '@src/components/CRaccoltaCataloghi/index' import { CGridOriz } from '@src/components/CGridOriz/index';
import { tools } from '@tools' import { ChatBot } from '@src/components/ChatBot/index';
import { shared_consts } from '@src/common/shared_vuejs' import { CCatalogList } from '@src/components/CCatalogList/index';
import { LandingFooter } from '@src/components/LandingFooter' import { CRaccoltaCataloghi } from '@src/components/CRaccoltaCataloghi/index';
import { CMyActivities } from '@src/components/CMyActivities' import { tools } from '@tools';
import { CECommerce } from '@src/components/CECommerce' import { shared_consts } from '@src/common/shared_vuejs';
import { CStatMacro } from '@src/components/CStatMacro' import { LandingFooter } from '@src/components/LandingFooter';
import { CSearchProduct } from '@src/components/CSearchProduct' import { CMyActivities } from '@src/components/CMyActivities';
import { CPageViewStats } from '@src/components/CPageViewStats' import { CECommerce } from '@src/components/CECommerce';
import { CQRCode } from '@src/components/CQRCode' import { CStatMacro } from '@src/components/CStatMacro';
import { CAITools } from '@src/components/CAITools' import { CSearchProduct } from '@src/components/CSearchProduct';
import { CCatalogo } from '@src/components/CCatalogo' import { CPageViewStats } from '@src/components/CPageViewStats';
import { CRaccolta } from '@src/components/CRaccolta' import { CQRCode } from '@src/components/CQRCode';
import { CAITools } from '@src/components/CAITools';
import { CCatalogo } from '@src/components/CCatalogo';
import { CRaccolta } from '@src/components/CRaccolta';
// import { CMapMarker } from '@src/components/CMapMarker.off' // import { CMapMarker } from '@src/components/CMapMarker.off'
import { CMapUsers } from '@src/components/CMapUsers' import { CMapUsers } from '@src/components/CMapUsers';
import { CMapGetCoordinates } from '@src/components/CMapGetCoordinates' import { CMapGetCoordinates } from '@src/components/CMapGetCoordinates';
import { CMapEditAddressByCoord } from '@src/components/CMapEditAddressByCoord' import { CMapEditAddressByCoord } from '@src/components/CMapEditAddressByCoord';
import { CMapComuni } from '@src/components/CMapComuni' import { CMapComuni } from '@src/components/CMapComuni';
import { COpenStreetMap } from '@src/components/COpenStreetMap' import { COpenStreetMap } from '@src/components/COpenStreetMap';
import { CCardCarousel } from '@src/components/CCardCarousel' import { CCardCarousel } from '@src/components/CCardCarousel';
import { CMyPage } from '@src/components/CMyPage' import { CMyPage } from '@src/components/CMyPage';
import { CMyPageIntro } from '@src/components/CMyPageIntro' import { CMyPageIntro } from '@src/components/CMyPageIntro';
import { CEventsCalendar } from '@src/components/CEventsCalendar' import { CEventsCalendar } from '@src/components/CEventsCalendar';
import { CMyEditor } from '@src/components/CMyEditor' import { CMyEditor } from '@src/components/CMyEditor';
import { CMyFieldRec } from '@src/components/CMyFieldRec' import { CMyFieldRec } from '@src/components/CMyFieldRec';
import { CSelectColor } from '@src/components/CSelectColor' import { CSelectColor } from '@src/components/CSelectColor';
import { CMainView } from '@src/components/CMainView' import { CMainView } from '@src/components/CMainView';
import { CMyProfileTutorial } from '@src/components/CMyProfileTutorial' import { CMyProfileTutorial } from '@src/components/CMyProfileTutorial';
import { CSendRISTo } from '@src/components/CSendRISTo' import { CSendRISTo } from '@src/components/CSendRISTo';
import { CDashboard } from '@src/components/CDashboard' import { CDashboard } from '@src/components/CDashboard';
import { CDashGroup } from '@src/components/CDashGroup' import { CDashGroup } from '@src/components/CDashGroup';
import { CMovements } from '@src/components/CMovements' import { CMovements } from '@src/components/CMovements';
import { CCheckAppRunning } from '@src/components/CCheckAppRunning' import { CCheckAppRunning } from '@src/components/CCheckAppRunning';
import { CStatusReg } from '@src/components/CStatusReg' import { CStatusReg } from '@src/components/CStatusReg';
import { CTitleBanner } from '@src/components/CTitleBanner' import { CTitleBanner } from '@src/components/CTitleBanner';
import { CCheckIfIsLogged } from '@src/components/CCheckIfIsLogged' import { CCheckIfIsLogged } from '@src/components/CCheckIfIsLogged';
import { CSelectFontSize } from '@src/components/CSelectFontSize' import { CSelectFontSize } from '@src/components/CSelectFontSize';
import { CNotifAtTop } from '@src/components/CNotifAtTop' import { CNotifAtTop } from '@src/components/CNotifAtTop';
import { CPresentazione } from '@src/components/CPresentazione' import { CPresentazione } from '@src/components/CPresentazione';
import { CRegistration } from '@src/components/CRegistration' import { CRegistration } from '@src/components/CRegistration';
import { CShareSocial } from '@src/components/CShareSocial' import { CShareSocial } from '@src/components/CShareSocial';
import { CVisuVideoPromoAndPDF } from '@src/components/CVisuVideoPromoAndPDF' import { CVisuVideoPromoAndPDF } from '@src/components/CVisuVideoPromoAndPDF';
import MixinMetaTags from '@src/mixins/mixin-metatags' import MixinMetaTags from '@src/mixins/mixin-metatags';
import MixinBase from '@src/mixins/mixin-base' import MixinBase from '@src/mixins/mixin-base';
import { useQuasar } from 'quasar' import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router';
import { LatLng } from 'leaflet' import { LatLng } from 'leaflet';
import { costanti } from '@costanti'
import { costanti } from '@costanti';
export default defineComponent({ export default defineComponent({
name: 'CMyElem', name: 'CMyElem',
components: { components: {
CImgTitle, CTitle, LandingFooter, CEventsCalendar, CImgTitle,
CCardCarousel, COpenStreetMap, CMyPage, CMyPageIntro, CMyEditor, CMyFieldRec, CTitle,
CSelectColor, CSelectFontSize, CImgPoster, LandingFooter,
CCheckIfIsLogged, CStatusReg, CDashboard, CMainView, CNotifAtTop, CEventsCalendar,
CPresentazione, CMyActivities, CCardCarousel,
CMyProfileTutorial, CSendRISTo, COpenStreetMap,
CTitleBanner, CShareSocial, CCheckAppRunning, CRegistration, CMyPage,
CVisuVideoPromoAndPDF, CECommerce, CCatalogo, CRaccolta, CAITools, CStatMacro, CMyPageIntro,
CMapComuni, CMapUsers, CMapGetCoordinates, CMapEditAddressByCoord, CMyEditor,
CDashGroup, CMovements, CGridOriz, CQRCode, CCatalogList, CMyFieldRec,
CSearchProduct, CRaccoltaCataloghi, CPageViewStats, CSelectColor,
CSelectFontSize,
CImgPoster,
CCheckIfIsLogged,
CStatusReg,
CDashboard,
CMainView,
CNotifAtTop,
CPresentazione,
CMyActivities,
CMyProfileTutorial,
CSendRISTo,
CTitleBanner,
CShareSocial,
CCheckAppRunning,
CRegistration,
CVisuVideoPromoAndPDF,
CECommerce,
CCatalogo,
CRaccolta,
CAITools,
CStatMacro,
CMapComuni,
CMapUsers,
CMapGetCoordinates,
CMapEditAddressByCoord,
CDashGroup,
CMovements,
CGridOriz,
CQRCode,
CCatalogList,
CSearchProduct,
CRaccoltaCataloghi,
CPageViewStats,
ChatBot, ChatBot,
CSection,
CRow,
CColumn,
// , //CMapMarker, // , //CMapMarker,
}, },
emits: ['selElemClick'], emits: ['selElemClick'],
@@ -116,205 +151,212 @@ export default defineComponent({
}, },
}, },
setup(props, { emit }) { setup(props, { emit }) {
const globalStore = useGlobalStore() const globalStore = useGlobalStore();
const { setmeta, getsrcbyimg } = MixinMetaTags() const { setmeta, getsrcbyimg } = MixinMetaTags();
const { setValDb, getValDb } = MixinBase() const { setValDb, getValDb } = MixinBase();
const $router = useRouter() const $router = useRouter();
const $q = useQuasar() const $q = useQuasar();
const { t } = useI18n() const { t } = useI18n();
const animare = ref(0) const animare = ref(0);
const animarecard = ref(0) const animarecard = ref(0);
const slide = ref(0) const slide = ref(0);
const slide2 = ref(0) const slide2 = ref(0);
const disableSave = ref(true) const disableSave = ref(true);
const enableEdit = ref(false) const enableEdit = ref(false);
const enableAdd = ref(true) const enableAdd = ref(true);
const visushare = ref(false) const visushare = ref(false);
const tabcatalogo = ref('griglia') const tabcatalogo = ref('griglia');
const social = ref(<ISocial>{}) const social = ref(<ISocial>{});
const neworder = ref(<number | undefined>0) const neworder = ref(<number | undefined>0);
const myel = ref(<IMyElem>{}) const myel = ref(<IMyElem>{});
const newtype = ref(<any>'') const newtype = ref(<any>'');
const isAppRunning = computed(() => globalStore.isAppRunning) const isAppRunning = computed(() => globalStore.isAppRunning);
const currentCardsPerSlide = computed(() => { const currentCardsPerSlide = computed(() => {
return myel.value.num2 ? myel.value.num2 : 2 // cardsPerSlide return myel.value.num2 ? myel.value.num2 : 2; // cardsPerSlide
}) });
// Raggruppa le card in base al numero di card per slide // Raggruppa le card in base al numero di card per slide
const cardGroups = computed(() => { const cardGroups = computed(() => {
const cards = myel.value.listcards || [] const cards = myel.value.listcards || [];
const groups = [] const groups = [];
for (let i = 0; i < cards.length; i += currentCardsPerSlide.value) { for (let i = 0; i < cards.length; i += currentCardsPerSlide.value) {
groups.push(cards.slice(i, i + currentCardsPerSlide.value)) groups.push(cards.slice(i, i + currentCardsPerSlide.value));
} }
return groups return groups;
}) });
const coordaddr = ref(<ICoordGPS>{ address: '', coordinates: [0, 0] }) const coordaddr = ref(<ICoordGPS>{ address: '', coordinates: [0, 0] });
const speedSafe = computed(() => (myel.value as any).speed ?? 0);
const carouselRef = ref(<any>null) const carouselRef = ref(<any>null);
const isAtStart = ref(true) const isAtStart = ref(true);
const isAtEnd = ref(false) const isAtEnd = ref(false);
const activeIndex = ref(0) const activeIndex = ref(0);
watch(() => myel.value.order, (value, oldval) => { watch(
mounted() () => myel.value.order,
}) (value, oldval) => {
mounted();
}
);
function getArrDisciplines() { function getArrDisciplines() {
return globalStore.disciplines.filter((rec: any) => rec.showinhome) return globalStore.disciplines.filter((rec: any) => rec.showinhome);
} }
function getheightgallery() { function getheightgallery() {
if (tools.isMobile()) if (tools.isMobile()) return '400px';
return '400px' else return '600px';
else
return '600px'
} }
function saveElem(exit?: boolean) { function saveElem(exit?: boolean) {
// Save Elem record // Save Elem record
const myelem = props.myelem const myelem = props.myelem;
myelem.order = neworder.value myelem.order = neworder.value;
globalStore.saveMyElem($q, t, myelem).then((ris) => { globalStore.saveMyElem($q, t, myelem).then((ris) => {
if (ris) { if (ris) {
// OK // OK
disableSave.value = true disableSave.value = true;
if (exit) if (exit) enableEdit.value = false;
enableEdit.value = false
} }
}) });
} }
function addNewElem(order?: number) { function addNewElem(order?: number) {
const newrec = globalStore.prepareAddNewElem(
const newrec = globalStore.prepareAddNewElem(order, $q, t, props.myelem, newtype.value) order,
$q,
t,
props.myelem,
newtype.value
);
} }
function dupElem(order?: number) { function dupElem(order?: number) {
const newrec = props.myelem;
const newrec = props.myelem newrec._id = undefined;
newrec.order = order ? order : newrec.order! + 10;
newrec._id = undefined globalStore.addNewElem($q, t, newrec);
newrec.order = order ? order : (newrec.order! + 10)
globalStore.addNewElem($q, t, newrec)
} }
function modifElem() { function modifElem() {
disableSave.value = false disableSave.value = false;
} }
const checkScrollPosition = () => { const checkScrollPosition = () => {
const container = carouselRef.value const container = carouselRef.value;
if (!container || !myel.value || !myel.value.listcards) return if (!container || !myel.value || !myel.value.listcards) return;
isAtStart.value = container.scrollLeft <= 0 isAtStart.value = container.scrollLeft <= 0;
isAtEnd.value = container.scrollLeft + container.clientWidth >= container.scrollWidth - 1 isAtEnd.value =
container.scrollLeft + container.clientWidth >= container.scrollWidth - 1;
const cardWidth = container.scrollWidth / myel.value.listcards.length const cardWidth = container.scrollWidth / myel.value.listcards.length;
activeIndex.value = Math.round(container.scrollLeft / cardWidth) activeIndex.value = Math.round(container.scrollLeft / cardWidth);
} };
function mounted() { function mounted() {
myel.value = props.myelem myel.value = props.myelem;
neworder.value = props.myelem.order neworder.value = props.myelem.order;
if (props.myelem) if (props.myelem) newtype.value = props.myelem.type;
newtype.value = props.myelem.type
nextTick(() => { nextTick(() => {
checkScrollPosition() checkScrollPosition();
carouselRef.value?.addEventListener('scroll', checkScrollPosition) carouselRef.value?.addEventListener('scroll', checkScrollPosition);
}) });
} }
function clickOnElem() { function clickOnElem() {
if (props.editOn) { if (props.editOn) {
enableEdit.value = true enableEdit.value = true;
// console.log('selElemClick', props.myelem) // console.log('selElemClick', props.myelem)
emit('selElemClick', props.myelem) emit('selElemClick', props.myelem);
} }
} }
function getClass() { function getClass() {
let mycl = '' let mycl = '';
if (props.myelem.align === shared_consts.ALIGNTYPE.CEHTER) { if (props.myelem.align === shared_consts.ALIGNTYPE.CEHTER) {
mycl += ' align_center' mycl += ' align_center';
} else if (props.myelem.align === shared_consts.ALIGNTYPE.RIGHT) { } else if (props.myelem.align === shared_consts.ALIGNTYPE.RIGHT) {
mycl += ' align_right' mycl += ' align_right';
} else if (props.myelem.align === shared_consts.ALIGNTYPE.LEFT) { } else if (props.myelem.align === shared_consts.ALIGNTYPE.LEFT) {
mycl += ' align_left' mycl += ' align_left';
} }
if (props.myelem.class2) if (props.myelem.class2) mycl += ' ' + props.myelem.class2;
mycl += ' ' + props.myelem.class2
if (props.selElem && props.editOn) { if (props.selElem && props.editOn) {
if (props.myelem._id === props.selElem._id) if (props.myelem._id === props.selElem._id) mycl += ' selectedElem';
mycl += ' selectedElem'
} }
return mycl return mycl;
} }
function showFit() { function showFit() {
if (props.myelem && props.myelem.type) if (props.myelem && props.myelem.type)
return [shared_consts.ELEMTYPE.TEXT].includes(props.myelem.type) return [shared_consts.ELEMTYPE.TEXT].includes(props.myelem.type);
else else return false;
return false
} }
function PagLogin() { function PagLogin() {
$router.replace('/signin') $router.replace('/signin');
} }
async function clickshare() { async function clickshare() {
tools.addToTemporaryLinkReg() tools.addToTemporaryLinkReg();
const mytext = await tools.sendMsgTelegramCmd( const mytext = await tools.sendMsgTelegramCmd(
$q, $q,
t, t,
shared_consts.MsgTeleg.SHARE_MSGREG, shared_consts.MsgTeleg.SHARE_MSGREG,
true true
) );
if (false) { if (false) {
social.value.description = mytext social.value.description = mytext;
visushare.value = true visushare.value = true;
} }
} }
// Classe per le colonne delle card // Classe per le colonne delle card
function cardColumnClass() { function cardColumnClass() {
const width = 12 / currentCardsPerSlide.value const width = 12 / currentCardsPerSlide.value;
return `col-${width}` return `col-${width}`;
} }
function updateCatalogoEmit(updatedCatalogo: IOptCatalogo) { function updateCatalogoEmit(updatedCatalogo?: IOptCatalogo) {
console.log('CMyElem: updateCatalogoEmit') if (!updatedCatalogo) return;
myel.value.catalogo = updatedCatalogo console.log('CMyElem: updateCatalogoEmit');
myel.value.catalogo = updatedCatalogo;
}
function naviga(path?: string): void {
if (path) {
$router.push(path);
} else {
// default fallback route
$router.push('/');
}
} }
function naviga(path: string) { onMounted(mounted);
$router.push(path)
}
onMounted(mounted)
return { return {
tools, tools,
@@ -359,8 +401,8 @@ export default defineComponent({
tabcatalogo, tabcatalogo,
costanti, costanti,
naviga, naviga,
speedSafe,
t, t,
} };
}, },
});
})

View File

@@ -10,6 +10,37 @@
" "
> >
<div v-if="myel.type"> <div v-if="myel.type">
<div v-if="myel.children && myel.children.length">
<template v-for="(section, sidx) in myel.children">
<CSection
v-if="section.type === shared_consts.ELEMTYPE.SECTION"
:key="'sec' + sidx"
>
<template
v-for="(row, ridx) in section.rows || section.children || []"
:key="'row' + ridx"
>
<CRow
v-if="row.type === shared_consts.ELEMTYPE.ROW"
:key="'r' + ridx"
>
<template
v-for="(col, cidx) in row.columns || row.children || []"
:key="'col' + cidx"
>
<CColumn
v-if="col"
:key="'col' + cidx"
>
<div v-if="col.container">{{ col.container }}</div>
<div v-else-if="col.title">{{ col.title }}</div>
</CColumn>
</template>
</CRow>
</template>
</CSection>
</template>
</div>
<q-btn <q-btn
v-if="editOn" v-if="editOn"
class="btn-edit-floating" class="btn-edit-floating"
@@ -228,7 +259,7 @@
:title="myel.container" :title="myel.container"
:myheight="myel.heightimg" :myheight="myel.heightimg"
:vertalign="myel.vertalign" :vertalign="myel.vertalign"
:speed="myel.speed" :speed="speedSafe"
:elemsText="myel.elemsText" :elemsText="myel.elemsText"
:logo="tools.getImgFileByFilename(myel, myel.img)" :logo="tools.getImgFileByFilename(myel, myel.img)"
:logoheight="myel.height ? myel.height.toString() : '100'" :logoheight="myel.height ? myel.height.toString() : '100'"
@@ -1100,6 +1131,7 @@
<div v-else-if="myel.type === shared_consts.ELEMTYPE.FOOTER"> <div v-else-if="myel.type === shared_consts.ELEMTYPE.FOOTER">
<LandingFooter /> <LandingFooter />
</div> </div>
<div v-if="editOn"> <div v-if="editOn">
<div class="q-ma-md"></div> <div class="q-ma-md"></div>
</div> </div>

View File

@@ -57,7 +57,6 @@ export default defineComponent({
const load = async (): Promise<void> => { const load = async (): Promise<void> => {
// console.log('load', mypath.value) // console.log('load', mypath.value)
if (mypath.value !== '') rec.value = await globalStore.loadPage('/' + mypath.value, 'cmypage') if (mypath.value !== '') rec.value = await globalStore.loadPage('/' + mypath.value, 'cmypage')
} }
watch(() => props.mypath, async (to: string, from: string) => { watch(() => props.mypath, async (to: string, from: string) => {

View File

@@ -78,7 +78,7 @@ export default defineComponent({
const { t } = useI18n(); const { t } = useI18n();
const globalStore = useGlobalStore(); const globalStore = useGlobalStore();
const $router = useRouter(); const $router = useRouter();
const $route = useRoute() const $route = useRoute();
const mywidthEditor = ref(400); const mywidthEditor = ref(400);
@@ -125,6 +125,12 @@ export default defineComponent({
async function load() { async function load() {
console.log('load', mypathin.value, 'idapp', tools.getEnv('VITE_APP_ID')); console.log('load', mypathin.value, 'idapp', tools.getEnv('VITE_APP_ID'));
const query = $router.currentRoute.value.query
if (query.edit === '1') {
globalStore.editOn = true;
}
if (mypathin.value !== '') { if (mypathin.value !== '') {
onloading.value = true; onloading.value = true;
await globalStore.loadPage('/' + mypathin.value, 'cmypageelem').then((ris) => { await globalStore.loadPage('/' + mypathin.value, 'cmypageelem').then((ris) => {

View File

@@ -0,0 +1,20 @@
<template>
<div class="row-container" style="display:flex; flex-direction: row;">
<slot></slot>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'CRow'
});
</script>
<style scoped>
.row-container {
display: flex;
flex-direction: row;
}
</style>

View File

@@ -0,0 +1,19 @@
<template>
<div class="section">
<slot></slot>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'CSection'
});
</script>
<style scoped>
.section {
padding: 8px 0;
}
</style>

View File

@@ -170,7 +170,7 @@ export default defineComponent({
function removeFromCard() { function removeFromCard() {
$q.dialog({ $q.dialog({
title: order.value.product.productInfo.name, title: order.value.product?.productInfo?.name,
message: 'Sicuro di voler rimuovere il prodotto dal carrello?', message: 'Sicuro di voler rimuovere il prodotto dal carrello?',
ok: { ok: {
label: 'Rimuovi', label: 'Rimuovi',
@@ -222,7 +222,7 @@ export default defineComponent({
function mounted() { function mounted() {
endload.value = false; endload.value = false;
weight.value = props.order.product?.productInfo.weight; weight.value = props.order.product?.productInfo?.weight;
price.value = props.order.price; price.value = props.order.price;
if (props.order.quantity !== 0) { if (props.order.quantity !== 0) {
orderQuantity.value = props.order.quantity; orderQuantity.value = props.order.quantity;

View File

@@ -1,10 +1,4 @@
import { defineComponent, ref, computed, onMounted, watch, onBeforeUnmount } from 'vue' import { defineComponent, ref, computed, watch } from 'vue'
import { useUserStore } from '@store/UserStore'
import { useQuasar } from 'quasar'
import { useI18n } from 'vue-i18n'
import { tools } from '@tools'
import { useRouter } from 'vue-router'
export default defineComponent({ export default defineComponent({
name: 'IconPicker', name: 'IconPicker',
@@ -12,49 +6,75 @@ export default defineComponent({
modelValue: { type: String, default: '' }, modelValue: { type: String, default: '' },
icons: { icons: {
type: Array as () => string[], type: Array as () => string[],
default: () => ([ // SOLO Font Awesome 5 (free)
// Estendi pure questo set default: () => [
'fas fa-house', 'fas fa-home',
'fas fa-book', 'fas fa-book',
'fas fa-star', 'fas fa-star',
'fas fa-heart', 'fas fa-heart',
'fas fa-user', 'fas fa-user',
'fas fa-gear', 'fas fa-cog',
'fas fa-circle-info', 'fas fa-info-circle',
'fas fa-newspaper', 'far fa-newspaper',
'fas fa-list', 'fas fa-list',
'fas fa-tags', 'fas fa-tags',
'fas fa-chart-line', 'fas fa-chart-line',
'fas fa-briefcase', 'fas fa-briefcase',
'fas fa-envelope', 'fas fa-envelope',
'fas fa-phone', 'fas fa-phone',
'fas fa-earth-europe', 'fas fa-globe-europe'
]) ]
} }
}, },
emits: ['update:modelValue', 'change'], emits: ['update:modelValue', 'change'],
setup (props, { emit }) { setup (props, { emit }) {
const keyword = ref('') const local = ref(props.modelValue) // testo inserito/valore corrente
const local = ref(props.modelValue) const dialog = ref(false) // mostra/nasconde il picker
const keyword = ref('') // filtro dentro il dialog
watch(() => props.modelValue, v => { local.value = v }) watch(() => props.modelValue, v => { local.value = v })
const filteredIcons = computed(() => { const filteredIcons = computed(() => {
const k = keyword.value.trim().toLowerCase() const k = keyword.value.trim().toLowerCase()
if (!k) return props.icons if (!k) return props.icons
return props.icons.filter(i => i.toLowerCase().includes(k)) return props.icons.filter(i =>
i.toLowerCase().includes(k) ||
// match anche sul nome “breve” (es: 'home')
i.toLowerCase().split(' ').some(cls => cls.startsWith('fa-') && cls.includes(k))
)
}) })
function select (val: string) { function select (val: string) {
local.value = val // applica la stringa così comè; nessun fallback
emit('update:modelValue', val) emit('update:modelValue', val || '')
emit('change', val) emit('change', val || '')
} }
function onKeyword () { function choose (ic: string) {
// solo aggiorna la lista; il pulsante "Usa testo" applica local.value = ic || ''
select(local.value)
dialog.value = false
} }
return { keyword, local, filteredIcons, select, onKeyword } function clear () {
local.value = ''
select('')
}
function openPicker () {
keyword.value = ''
dialog.value = true
}
return {
local,
dialog,
keyword,
filteredIcons,
select,
choose,
clear,
openPicker
}
} }
}) })

View File

@@ -1,67 +1,86 @@
<template> <template>
<div class="q-gutter-sm"> <div class="q-gutter-sm">
<div class="row items-center q-col-gutter-sm"> <div class="row items-center q-col-gutter-sm">
<div class="col"> <div class="col-12">
<q-input <q-input
v-model="keyword" v-model="local"
dense dense
clearable clearable
label="Cerca icona (es: fas fa-house)" label="Icona"
@update:model-value="onKeyword"
> >
<template #prepend> <template #prepend>
<q-icon :name="modelValue || 'fa-regular fa-face-smile'" /> <q-icon v-if="local" :name="local" />
</template> </template>
</q-input> </q-input>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<q-btn <q-btn
dense dense
outline outline
:disable="!keyword"
label="Usa testo" label="Usa testo"
@click="select(keyword)" @click="select(local)"
/>
</div>
<div class="col-auto">
<q-btn
dense
color="primary"
outline
label="Scegli icona"
@click="openPicker"
/> />
</div> </div>
</div> </div>
<q-separator spaced /> <!-- La griglia icone appare solo nel dialog -->
<q-dialog v-model="dialog">
<q-card style="min-width: 640px; max-width: 90vw;">
<q-toolbar>
<q-toolbar-title>Seleziona icona</q-toolbar-title>
<q-btn flat round dense icon="fas fa-times" v-close-popup />
</q-toolbar>
<div class="row q-col-gutter-sm"> <div class="q-pa-md">
<div <q-input
v-for="ic in filteredIcons" v-model="keyword"
:key="ic" dense
class="col-3 col-sm-2 col-md-1" clearable
> autofocus
<q-btn label="Cerca (es: home, user, info...)"
outline />
class="full-width"
:icon="ic"
@click="select(ic)"
:color="ic === modelValue ? 'primary' : 'grey-7'"
>
<q-tooltip>{{ ic }}</q-tooltip>
</q-btn>
</div>
</div>
<div class="q-mt-md"> <div class="row q-col-gutter-sm q-mt-sm">
<q-input <div
v-model="local" v-for="ic in filteredIcons"
dense :key="ic"
label="Valore attuale icona" class="col-3 col-sm-2 col-md-1"
@update:model-value="select(local)" >
> <q-btn
<template #append> outline
<q-icon :name="local || 'fa-regular fa-circle-question'" /> class="full-width"
</template> :icon="ic"
</q-input> @click="choose(ic)"
</div> >
<q-tooltip>{{ ic }}</q-tooltip>
</q-btn>
</div>
</div>
<div class="row q-mt-md q-gutter-sm">
<q-btn outline label="Rimuovi icona" color="negative" @click="choose('')" />
<q-space />
<q-btn color="primary" label="Chiudi" v-close-popup />
</div>
</div>
</q-card>
</q-dialog>
</div> </div>
</template> </template>
<script lang="ts" src="./IconPicker.ts"></script> <script lang="ts" src="./IconPicker.ts"></script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import './IconPicker.scss'; /* opzionale: spaziatura minima sulle celle */
</style> </style>

View File

@@ -0,0 +1,50 @@
import { defineComponent, ref, computed, onMounted, watch, onBeforeUnmount } from 'vue';
import { IMyPage } from 'app/src/model';
type PageWithKey = IMyPage & { __key?: string };
export default defineComponent({
name: 'MenuPageItem',
props: {
item: { type: Object as () => PageWithKey, required: true },
selected: { type: Boolean, default: false },
active: { type: Boolean, default: false }, // v-model:active
variant: { type: String as () => 'menu' | 'off', default: 'menu' },
showGrip: { type: Boolean, default: true },
draggableHandleClass: { type: String, default: 'drag-handle' },
depth: { type: Number, default: 0 },
},
emits: ['select', 'edit', 'delete', 'open', 'update:active', 'update:item'],
setup(props, { emit }) {
function displayPath(path?: string) {
if (!path) return '-';
return path.startsWith('/') ? path : '/' + path;
}
function emitSelect() {
emit('select', props.item.__key);
}
function emitEdit() {
emit('edit', props.item.__key);
}
function emitDelete() {
emit('delete', props.item.__key);
}
function emitOpen() {
emit('open', props.item.__key);
}
const indentSpacerStyle = computed(() => {
const px = Math.min(props.depth, 6) * 16; // max 6 livelli x 16px
return { width: `${px}px`, minWidth: `${px}px` };
});
return {
displayPath,
emitSelect,
emitEdit,
emitDelete,
emitOpen,
indentSpacerStyle,
};
},
});

View File

@@ -0,0 +1,144 @@
<template>
<q-item
clickable
:active="selected"
@click="emitSelect"
>
<q-item-section
v-if="showGrip"
avatar
>
<q-btn
flat
round
dense
:class="draggableHandleClass"
icon="fas fa-grip-vertical"
@click.stop
/>
</q-item-section>
<q-item-section
v-if="depth > 0"
avatar
class="q-pr-none"
>
<div :style="indentSpacerStyle" />
</q-item-section>
<!--<q-item-section side>
<q-toggle
:model-value="active"
:color="active ? 'green' : 'grey'"
@update:model-value="val => $emit('update:active', val)"
@click.stop
/>
</q-item-section>-->
<q-item-section avatar>
<q-icon :name="item.icon || 'far fa-file-alt'" />
</q-item-section>
<q-item-section side>
<q-item-label caption>{{ item.order }}</q-item-label>
</q-item-section>
<q-item-section>
<q-item-label :class="{ 'text-grey-7': !active }">{{
item.title || '(senza titolo)'
}}</q-item-label>
<q-item-label caption>{{ displayPath(item.path) }}</q-item-label>
</q-item-section>
<q-item-section
v-if="active"
side
class="float-right"
>
<q-icon
name="fas fa-circle"
color="green"
size="xs"
/>
</q-item-section>
<q-item-section
v-if="item.only_admin"
side
class="float-right"
>
<q-icon
name="fas fa-circle"
color="red"
size="xs"
/>
</q-item-section>
<q-item-section side>
<slot name="actions">
<div
class="column q-gutter-xs"
v-if="true"
>
<q-btn
dense
round
color="primary"
icon="fas fa-ellipsis-v"
class="q-mr-xs"
@click.stop
>
<q-menu>
<q-list style="min-width: 140px">
<q-item
clickable
v-close-popup
@click="emitOpen"
>
<q-item-section side><q-icon name="fas fa-edit" /></q-item-section>
<q-item-section>Modifica</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="emitEdit"
>
<q-item-section side><q-icon name="fas fa-cog" /></q-item-section>
<q-item-section>Impostazioni</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="emitDelete"
>
<q-item-section side
><q-icon
name="fas fa-trash"
color="red"
/></q-item-section>
<q-item-section>Elimina</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
<div
class="column q-gutter-xs"
v-else
>
<q-btn
dense
round
color="negative"
icon="fas fa-trash"
@click.stop="emitDelete"
/>
</div>
</slot>
</q-item-section>
</q-item>
</template>
<script lang="ts" src="./MenuPageItem.ts"></script>
<style lang="scss" scoped>
@import './MenuPageItem.scss';
</style>

View File

@@ -0,0 +1 @@
export {default as MenuPageItem} from './MenuPageItem.vue'

View File

@@ -1,141 +1,167 @@
import { import {
defineComponent, defineComponent, ref, computed, watch, reactive, toRaw, nextTick
ref, } from 'vue'
computed, import { useQuasar } from 'quasar'
onMounted, import IconPicker from '../IconPicker/IconPicker.vue'
watch, import { IMyPage } from 'app/src/model'
onBeforeUnmount, import { useGlobalStore } from 'app/src/store'
toRaw, import { storeToRefs } from 'pinia'
nextTick, import { useI18n } from 'vue-i18n'
} from 'vue'; import { costanti } from '@costanti'
import { useUserStore } from '@store/UserStore'; import { CMyFieldRec } from '@src/components/CMyFieldRec'
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import { tools } from '@tools';
import { useRouter } from 'vue-router';
import { reactive } from 'vue';
import { IMyPage } from 'app/src/model';
import IconPicker from '../IconPicker/IconPicker.vue';
import { useGlobalStore } from 'app/src/store';
import { storeToRefs } from 'pinia';
export default defineComponent({ export default defineComponent({
name: 'PageEditor', name: 'PageEditor',
components: { IconPicker }, components: { IconPicker, CMyFieldRec },
props: { props: {
modelValue: { modelValue: { type: Object as () => IMyPage, required: true },
type: Object as () => IMyPage, nuovaPagina: { type: Boolean, required: true } // <-- modalità "bozza"
required: true,
},
}, },
emits: ['update:modelValue', 'apply'], emits: ['update:modelValue', 'apply', 'hide'],
setup(props, { emit }) { setup (props, { emit }) {
const $q = useQuasar(); const $q = useQuasar()
const globalStore = useGlobalStore();
const { mypage } = storeToRefs(globalStore);
// DRaft locale const { t } = useI18n()
const draft = reactive<IMyPage>({ ...props.modelValue }); const globalStore = useGlobalStore()
const { mypage } = storeToRefs(globalStore)
// Draft locale indipendente dal parent (specie in nuovaPagina)
const draft = reactive<IMyPage>({ ...props.modelValue })
// UI helper: path mostrato con "/" iniziale // UI helper: path mostrato con "/" iniziale
const ui = reactive({ const ui = reactive({ pathText: toUiPath(draft.path) })
pathText: toUiPath(draft.path),
});
const saving = ref(false) const saving = ref(false)
const syncingFromProps = ref(false) // <-- FLAG anti-loop const syncingFromProps = ref(false) // anti-loop
// --- Watch input esterno: ricarica draft e UI path // --- Sync IN: quando cambia il valore del parent, aggiorna solo il draft
// --- Watch input esterno: ricarica draft e UI path (NO scritture nello store qui!)
watch( watch(
() => props.modelValue, () => props.modelValue,
async (v) => { async v => {
syncingFromProps.value = true; syncingFromProps.value = true
Object.assign(draft, v || {}); Object.assign(draft, v || {})
ui.pathText = toUiPath(draft.path); ui.pathText = toUiPath(draft.path)
await nextTick(); await nextTick()
syncingFromProps.value = false; syncingFromProps.value = false
}, },
{ deep: false } { deep: false }
); )
// --- Ogni modifica del draft: aggiorna store.mypage e emetti update:modelValue (solo se modifica nasce da UI) // --- Modifiche live: SE NON è nuovaPagina, aggiorna store e v-model del parent
watch( watch(
draft, draft,
(val) => { (val) => {
if (syncingFromProps.value) return; // evita ricorsione if (syncingFromProps.value) return
upsertIntoStore(val, mypage.value); if (props.nuovaPagina) return // <-- blocca ogni propagazione durante "nuova pagina"
emit('update:modelValue', { ...val }); upsertIntoStore(val, mypage.value)
emit('update:modelValue', { ...val })
}, },
{ deep: true } { deep: true }
); )
// --- Helpers path // --- Helpers path
function toUiPath(storePath?: string) { function toUiPath (storePath?: string) {
const p = (storePath || '').trim(); const p = (storePath || '').trim()
if (!p) return '/'; if (!p) return '/'
return p.startsWith('/') ? p : `/${p}`; return p.startsWith('/') ? p : `/${p}`
} }
function toStorePath(uiPath?: string) { function toStorePath (uiPath?: string) {
const p = (uiPath || '').trim(); const p = (uiPath || '').trim()
if (!p) return ''; if (!p) return ''
return p.startsWith('/') ? p.slice(1) : p; return p.startsWith('/') ? p.slice(1) : p
}
function normalizeAndApplyPath () {
// normalizza: niente spazi → trattini
let p = (ui.pathText || '/').trim()
p = p.replace(/\s+/g, '-')
if (!p.startsWith('/')) p = '/' + p
ui.pathText = p
draft.path = toStorePath(p) // NB: scrive sul draft (watch sopra gestisce la propagazione)
} }
function normalizeAndApplyPath() { function pathRule (v: string) {
// normalizza: niente spazi, minuscole, trattini if (!v) return 'Percorso richiesto'
let p = (ui.pathText || '/').trim(); if (!v.startsWith('/')) return 'Deve iniziare con /'
p = p.replace(/\s+/g, '-'); if (/\s/.test(v)) return 'Nessuno spazio nel path'
if (!p.startsWith('/')) p = '/' + p; return true
ui.pathText = p;
draft.path = toStorePath(p);
} }
function pathRule(v: string) { // --- Upsert nello store.mypage (usato solo quando NON è nuovaPagina o dopo il save)
if (!v) return 'Percorso richiesto'; function upsertIntoStore (page: IMyPage, arr: IMyPage[]) {
if (!v.startsWith('/')) return 'Deve iniziare con /'; if (!page) return
if (/\s/.test(v)) return 'Nessuno spazio nel path'; const keyId = page._id
return true; const keyPath = page.path || ''
let idx = -1
if (keyId) idx = arr.findIndex(p => p._id === keyId)
if (idx < 0 && keyPath) idx = arr.findIndex(p => (p.path || '') === keyPath)
if (idx >= 0) arr[idx] = { ...arr[idx], ...toRaw(page) }
else arr.push({ ...toRaw(page) })
} }
// --- Upsert nello store.mypage // --- VALIDAZIONE + COMMIT per nuova pagina (o anche per edit espliciti)
function upsertIntoStore(page: IMyPage, arr: IMyPage[]) { async function checkAndSave (payloadDraft?: IMyPage) {
if (!page) return; const cur = payloadDraft || draft
// chiave di matching: prima _id, altrimenti path
const keyId = page._id; // validazioni base
const keyPath = page.path || ''; if (!cur.title?.trim()) {
let idx = -1; $q.notify({ message: 'Inserisci il titolo della pagina', type: 'warning' })
if (keyId) { return
idx = arr.findIndex((p) => p._id === keyId);
} }
if (idx < 0 && keyPath) {
idx = arr.findIndex((p) => (p.path || '') === keyPath); const pathText = (ui.pathText || '').trim()
if (!pathText) {
$q.notify({ message: 'Inserisci il percorso della pagina', type: 'warning' })
return
} }
if (idx >= 0) {
// merge preservando reattività const candidatePath = toStorePath(pathText).toLowerCase()
arr[idx] = { ...arr[idx], ...toRaw(page) };
} else { // unicità PATH (ignora se stesso quando editing)
arr.push({ ...toRaw(page) }); const existPath = globalStore.mypage.find(
(r) => (r.path || '').toLowerCase() === candidatePath && r._id !== cur._id
)
if (existPath) {
$q.notify({ message: 'Esiste già unaltra pagina con questo percorso', type: 'warning' })
return
} }
// unicità TITOLO (ignora se stesso quando editing)
const candidateTitle = (cur.title || '').toLowerCase()
const existName = globalStore.mypage.find(
(r) => (r.title || '').toLowerCase() === candidateTitle && r._id !== cur._id
)
if (existName) {
$q.notify({ message: 'Il nome della pagina esiste già', type: 'warning' })
return
}
await save() // esegue commit vero
emit('hide') // chiudi il dialog (se usi dialog)
} }
async function save () {
// --- Salvataggio esplicito (commit). Qui propaghiamo SEMPRE al parent/store.
async function save () {
try { try {
saving.value = true saving.value = true
normalizeAndApplyPath() // assicura path coerente normalizeAndApplyPath() // assicura path coerente
const payload: IMyPage = { ...toRaw(draft), path: draft.path || '' } const payload: IMyPage = { ...toRaw(draft), path: draft.path || '' }
const saved = await globalStore.savePage(payload) const saved = await globalStore.savePage(payload)
if (saved && typeof saved === 'object') { if (saved && typeof saved === 'object') {
syncingFromProps.value = true syncingFromProps.value = true
Object.assign(draft, saved) Object.assign(draft, saved)
upsertIntoStore(draft, mypage.value) upsertIntoStore(draft, mypage.value) // ora è lecito anche per nuovaPagina
await nextTick() await nextTick()
syncingFromProps.value = false syncingFromProps.value = false
} }
// IMPORTANTISSIMO: in nuovaPagina non abbiamo mai emesso prima → emettiamo ora
emit('update:modelValue', { ...draft })
emit('apply', { ...draft }) emit('apply', { ...draft })
$q.notify({ type: 'positive', message: 'Pagina salvata' }) $q.notify({ type: 'positive', message: 'Pagina salvata' })
} catch (err: any) { } catch (err) {
console.error(err) console.error(err)
$q.notify({ type: 'negative', message: 'Errore nel salvataggio' }) $q.notify({ type: 'negative', message: 'Errore nel salvataggio' })
} finally { } finally {
@@ -143,7 +169,7 @@ async function save () {
} }
} }
// --- Ricarica da sorgente // --- Ricarica (per editing). In modalità nuovaPagina non propaghiamo al parent.
async function reloadFromStore () { async function reloadFromStore () {
try { try {
const absolute = ui.pathText || '/' const absolute = ui.pathText || '/'
@@ -153,8 +179,7 @@ async function save () {
Object.assign(draft, page) Object.assign(draft, page)
ui.pathText = toUiPath(draft.path) ui.pathText = toUiPath(draft.path)
upsertIntoStore(draft, mypage.value) upsertIntoStore(draft, mypage.value)
console.log('page', draft) if (!props.nuovaPagina) emit('update:modelValue', { ...draft }) // <-- no propagate in nuovaPagina
emit('update:modelValue', { ...draft })
await nextTick() await nextTick()
syncingFromProps.value = false syncingFromProps.value = false
$q.notify({ type: 'info', message: 'Pagina ricaricata' }) $q.notify({ type: 'info', message: 'Pagina ricaricata' })
@@ -167,16 +192,18 @@ async function save () {
} }
} }
function resetDraft() { function resetDraft () {
console.log('resetDraft')
syncingFromProps.value = true syncingFromProps.value = true
Object.assign(draft, props.modelValue || {}) Object.assign(draft, props.modelValue || {})
ui.pathText = toUiPath(draft.path) ui.pathText = toUiPath(draft.path)
// aggiorna i componenti if (!props.nuovaPagina) emit('update:modelValue', { ...draft }) // <-- no propagate in nuovaPagina
emit('update:modelValue', { ...draft })
nextTick(() => { syncingFromProps.value = false }) nextTick(() => { syncingFromProps.value = false })
} }
function modifElem() {
}
const absolutePath = computed(() => toUiPath(draft.path)) const absolutePath = computed(() => toUiPath(draft.path))
return { return {
@@ -189,6 +216,10 @@ async function save () {
reloadFromStore, reloadFromStore,
resetDraft, resetDraft,
absolutePath, absolutePath,
}; checkAndSave,
}, t,
}); costanti,
modifElem,
}
}
})

View File

@@ -1,5 +1,9 @@
<template> <template>
<q-card flat bordered class="q-pa-md"> <q-card
flat
bordered
class="q-pa-md"
>
<div class="row q-col-gutter-md"> <div class="row q-col-gutter-md">
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<q-input <q-input
@@ -20,53 +24,99 @@
v-model="draft.title" v-model="draft.title"
label="Titolo" label="Titolo"
dense dense
:rules="[v => !!v || 'Titolo richiesto']" :rules="[(v) => !!v || 'Titolo richiesto']"
/> />
</div> </div>
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<icon-picker v-model="draft.icon" /> <q-input
v-model.number="draft.order"
label="Ordine"
dense
type="number"
:rules="[
(v) => v !== null || 'Ordine richiesto',
(v) => v >= 0 || 'Ordine deve essere positivo',
]"
/>
</div> </div>
SottoMenu:
<CMyFieldRec
title="SottoMenu:"
table="pages"
:id="draft._id"
:rec="draft"
field="sottoMenu"
@update:model-value="modifElem"
:canEdit="true"
:canModify="true"
:nosaveToDb="true"
:fieldtype="costanti.FieldType.multiselect"
>
</CMyFieldRec>
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<q-input v-model="draft.iconsize" label="Dimensione icona (es: 24px)" dense /> <icon-picker v-model="draft.icon" />
</div> </div>
<div class="col-12"> <div class="col-12">
<q-separator spaced /> <q-separator spaced />
<div class="row items-center q-col-gutter-md"> <div class="row items-center q-col-gutter-md">
<div class="col-auto"> <div class="col-auto">
<q-toggle v-model="draft.active" label="Attivo" /> <q-toggle
v-model="draft.active"
label="Attivo"
/>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<q-toggle v-model="draft.inmenu" label="Presente nel menu" /> <q-toggle
v-model="draft.inmenu"
label="Presente nel menu"
/>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<q-toggle v-model="draft.onlyif_logged" label="Solo se loggati" /> <q-toggle
v-model="draft.onlyif_logged"
:label="t('pages.onlyif_logged')"
/>
</div>
<div class="col-auto">
<q-toggle
v-model="draft.only_admin"
:label="t('pages.only_admin')"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row q-col-gutter-sm q-mt-md"> <q-card-actions
<div class="col-auto"> align="center"
<q-btn color="primary" label="Salva" :loading="saving" @click="save" /> class="q-pa-md q-gutter-md"
</div> >
<div class="col-auto"> <q-btn
<q-btn outline label="Ricarica" @click="reloadFromStore" /> color="primary"
</div> label="Salva"
<div class="col-auto"> :loading="saving"
<q-btn color="grey-7" label="Chiudi" @click="$emit('close', draft.path)" /> @click="checkAndSave(draft)"
</div> />
<div class="col-auto"> <q-btn
<q-btn flat color="grey-7" label="Reset draft" @click="resetDraft" /> outline
</div> label="Ricarica"
</div> @click="reloadFromStore"
/>
<q-btn
color="grey-7"
label="Chiudi"
v-close-popup
/>
<!--<q-btn flat color="grey-7" label="Reset draft" @click="resetDraft" />-->
</q-card-actions>
</q-card> </q-card>
</template> </template>
<script lang="ts" src="./PageEditor.ts"> <script lang="ts" src="./PageEditor.ts"></script>
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import './PageEditor.scss'; @import './PageEditor.scss';

View File

@@ -1,160 +1,542 @@
import { defineComponent, ref, computed, onMounted, watch, onBeforeUnmount } from 'vue'; import { defineComponent, ref, computed, watch } from 'vue';
import { IMyPage } from 'app/src/model'; import type { IMyPage } from 'app/src/model';
import { PageEditor } from '../PageEditor'; import PageEditor from '@src/components/PageEditor/PageEditor.vue';
import MenuPageItem from '@src/components/MenuPageItem/MenuPageItem.vue';
import { useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
import { useGlobalStore } from 'app/src/store';
import draggable from 'vuedraggable';
type Bucket = 'menu' | 'off';
type PageWithKey = IMyPage & { __key?: string };
type PageRow = PageWithKey & { __depth: number }; // 0 = voce di menu, 1 = sottomenu
const byOrder = (a: IMyPage, b: IMyPage) => (a.order ?? 0) - (b.order ?? 0);
const norm = (p?: string) => (p || '').trim().replace(/^\//, '').toLowerCase();
let uidSeed = 1;
function ensureKeys(arr: PageWithKey[]) {
for (const p of arr) {
if (!p.__key) p.__key = p._id || `tmp_${uidSeed++}`;
}
}
export default defineComponent({ export default defineComponent({
name: 'PagesConfigurator', name: 'PagesConfigurator',
components: { PageEditor }, components: { PageEditor, MenuPageItem, draggable },
props: { props: {
modelValue: { modelValue: { type: Array as () => IMyPage[], required: true },
type: Array as () => IMyPage[],
required: true,
},
}, },
emits: ['update:modelValue', 'save', 'change-order'], emits: ['update:modelValue', 'save', 'change-order'],
setup(props, { emit }) { setup(props, { emit }) {
const pages = ref<IMyPage[]>(props.modelValue ? [...props.modelValue] : []); const $q = useQuasar();
const selectedIndex = ref<number>(-1); const $router = useRouter();
const globalStore = useGlobalStore();
const visualizzaEditor = ref(false);
const nuovaPagina = ref(false);
const ORDER_STEP = 10;
const MIN_GAP = 1;
function getPageByKey(key?: string) {
return key ? pages.value.find((p) => p.__key === key) : undefined;
}
function getOrderOfRow(row?: PageRow) {
const p = row ? getPageByKey(row.__key) : undefined;
return typeof p?.order === 'number' ? (p!.order as number) : undefined;
}
/**
* Assegna l'order SOLO all'elemento spostato (e, se serve, a una piccola finestra attorno)
* usando un ordinamento "sparso": media fra i vicini, oppure reseed locale.
* Ritorna i delta {id, order} da salvare.
*/
function sparseAssignOrder(
rows: PageRow[],
movedIndex: number
): { id: string; order: number }[] {
const deltas: { id: string; order: number }[] = [];
const curRow = rows[movedIndex];
if (!curRow?.__key) return deltas;
const cur = getPageByKey(curRow.__key);
if (!cur) return deltas;
const prevOrder = getOrderOfRow(rows[movedIndex - 1]);
const nextOrder = getOrderOfRow(rows[movedIndex + 1]);
const pushDelta = (p: PageWithKey, val: number) => {
if (p.order !== val) {
p.order = val;
if (p._id) deltas.push({ id: p._id, order: val });
}
};
// Caso 1: abbiamo spazio tra prev e next → usa media
if (
prevOrder !== undefined &&
nextOrder !== undefined &&
nextOrder - prevOrder > MIN_GAP
) {
const mid = prevOrder + Math.floor((nextOrder - prevOrder) / 2);
pushDelta(cur, mid);
return deltas;
}
// Caso 2: in testa → prima del first
if (prevOrder === undefined && nextOrder !== undefined) {
pushDelta(cur, nextOrder - ORDER_STEP);
return deltas;
}
// Caso 3: in coda → dopo l'ultimo
if (prevOrder !== undefined && nextOrder === undefined) {
pushDelta(cur, prevOrder + ORDER_STEP);
return deltas;
}
// Caso 4: nessuno spazio (o ordini uguali) → reseed locale (finestra stretta)
const start = Math.max(0, movedIndex - 3);
const end = Math.min(rows.length - 1, movedIndex + 3);
// base = order dellelemento appena prima della finestra (se esiste), altrimenti 0
let base = getOrderOfRow(rows[start - 1]) ?? 0;
for (let i = start; i <= end; i++) {
const r = rows[i];
const p = getPageByKey(r.__key!);
if (!p) continue;
base += ORDER_STEP;
pushDelta(p, base);
}
return deltas;
}
/** order per append in fondo a "menu" */
function computeAppendOrderForMenu(): number {
let max = 0;
for (const p of pages.value)
if (p.inmenu && typeof p.order === 'number') max = Math.max(max, p.order!);
return max + ORDER_STEP;
}
/** order per append in fondo a "off" (dopo tutti) */
function computeAppendOrderForOff(): number {
let max = 0;
for (const p of pages.value)
if (typeof p.order === 'number') max = Math.max(max, p.order!);
return max + ORDER_STEP;
}
// ---- STATE BASE --------------------------------------------------------
const pages = ref<PageWithKey[]>(
props.modelValue ? props.modelValue.map((p) => ({ ...p })) : []
);
ensureKeys(pages.value);
pages.value.sort(byOrder)
const selectedKey = ref<string | null>(null);
// Liste derivate per UI
const menuRows = ref<PageRow[]>([]); // lista piatta (top + figli) con depth
const offList = ref<PageWithKey[]>([]); // voci fuori menu (inmenu=false)
const applyingRows = ref(false); // guard per evitare rientri
// ---- BUILDERS (no side-effects) ---------------------------------------
function rebuildMenuRows() {
const mapByPath = new Map<string, PageWithKey>();
for (const p of pages.value) mapByPath.set(norm(p.path), p);
const tops = pages.value
.filter((p) => p.inmenu && !p.submenu)
.sort(byOrder) as PageWithKey[];
const rows: PageRow[] = [];
const usedChildKeys = new Set<string>();
for (const parent of tops) {
rows.push({ ...(parent as any), __depth: 0 });
const arr = Array.isArray(parent.sottoMenu) ? parent.sottoMenu : [];
for (const childPath of arr) {
const child = mapByPath.get(norm(childPath));
if (child && child.inmenu !== false && child.submenu === true) {
rows.push({ ...(child as any), __depth: 1 });
if (child.__key) usedChildKeys.add(child.__key);
}
}
}
// Orfani: sottomenu==true ma non referenziati da alcun parent
const orphans = (pages.value as PageWithKey[])
.filter((p) => p.inmenu && p.submenu && p.__key && !usedChildKeys.has(p.__key))
.sort(byOrder);
for (const ch of orphans) {
rows.push({ ...(ch as any), __depth: 0 }); // fallback: top-level
}
menuRows.value = rows;
}
function rebuildOffList() {
offList.value = pages.value.filter((p) => !p.inmenu).sort(byOrder);
}
function rebuildAllViews() {
rebuildMenuRows();
rebuildOffList();
globalStore.aggiornaMenu($router);
}
// ⬇️ Sostituisci completamente la funzione esistente
function applyMenuRows(
newRows: PageRow[],
movedIndex?: number
): { id: string; order: number }[] {
// 1) svuota i sottoMenu dei parent (ricostruiremo i link)
for (const p of pages.value) {
if (p.inmenu && !p.submenu) p.sottoMenu = [];
}
// 2) mappa chiave->page
const key2page = new Map<string, PageWithKey>();
for (const p of pages.value) if (p.__key) key2page.set(p.__key, p);
// 3) ricostruisci SOLO la struttura (inmenu/submenu e sottoMenu dei parent)
let currentParent: PageWithKey | null = null;
for (const row of newRows) {
const page = row.__key ? key2page.get(row.__key) : undefined;
if (!page) continue;
if (row.__depth <= 0 || !currentParent) {
// top-level
page.inmenu = true;
page.submenu = false;
currentParent = page;
if (!Array.isArray(page.sottoMenu)) page.sottoMenu = [];
} else {
// child
page.inmenu = true;
page.submenu = true;
page.mainMenu = true;
const pathStr = page.path || '';
if (currentParent) {
if (!Array.isArray(currentParent.sottoMenu)) currentParent.sottoMenu = [];
const exists = currentParent.sottoMenu.some((p) => norm(p) === norm(pathStr));
if (!exists) currentParent.sottoMenu.push(pathStr);
}
}
}
// 4) assegna order in modalità "sparsa" SOLO per lelemento spostato (e finestra vicina)
if (typeof movedIndex === 'number') {
return sparseAssignOrder(newRows, movedIndex);
}
return [];
}
// ⬇️ Sostituisci completamente la funzione esistente
function applyOffList(
newOff: PageWithKey[],
movedIndex?: number
): { id: string; order: number }[] {
// 1) togli riferimenti dai sottoMenu dei parent
const offPaths = new Set(newOff.map((x) => norm(x.path)));
for (const parent of pages.value) {
if (parent.inmenu && !parent.submenu && Array.isArray(parent.sottoMenu)) {
parent.sottoMenu = parent.sottoMenu.filter((p) => !offPaths.has(norm(p)));
}
}
// 2) marca inmenu/submenu=false per tutti gli "off" presenti
const offKeys = new Set(newOff.map((x) => x.__key));
for (const p of pages.value) {
if (p.__key && offKeys.has(p.__key)) {
p.inmenu = false;
p.submenu = false;
p.mainMenu = false;
}
}
// 3) assegna ordine "sparso" al solo elemento spostato
const deltas: { id: string; order: number }[] = [];
if (typeof movedIndex === 'number') {
const prev = newOff[movedIndex - 1];
const next = newOff[movedIndex + 1];
const cur = newOff[movedIndex];
if (cur?.__key) {
const curP = getPageByKey(cur.__key);
const prevO = prev ? getPageByKey(prev.__key!)?.order : undefined;
const nextO = next ? getPageByKey(next.__key!)?.order : undefined;
const pushDelta = (p: PageWithKey, val: number) => {
if (p.order !== val) {
p.order = val;
if (p._id) deltas.push({ id: p._id, order: val });
}
};
if (prevO !== undefined && nextO !== undefined && nextO - prevO > MIN_GAP) {
pushDelta(curP!, prevO + Math.floor((nextO - prevO) / 2));
} else if (prevO !== undefined && nextO === undefined) {
pushDelta(curP!, prevO + ORDER_STEP);
} else if (prevO === undefined && nextO !== undefined) {
pushDelta(curP!, nextO - ORDER_STEP);
} else {
// reseed locale nell'offList
const start = Math.max(0, movedIndex - 3);
const end = Math.min(newOff.length - 1, movedIndex + 3);
let base =
start > 0 ? (getPageByKey(newOff[start - 1].__key!)?.order ?? 0) : 0;
for (let i = start; i <= end; i++) {
const r = newOff[i];
const p = getPageByKey(r.__key!);
if (!p) continue;
base += ORDER_STEP;
pushDelta(p, base);
}
}
}
}
return deltas;
}
// ---- WATCHERS ----------------------------------------------------------
watch( watch(
() => props.modelValue, () => props.modelValue,
(v) => { (v) => {
pages.value = v ? [...v] : []; pages.value = (v || []).map((p) => ({ ...p }));
if (selectedIndex.value >= pages.value.length) selectedIndex.value = -1; ensureKeys(pages.value);
} pages.value.sort(byOrder)
rebuildAllViews();
if (!pages.value.find((p) => p.__key === selectedKey.value))
selectedKey.value = null;
},
{ deep: true }
); );
const localPagesSorted = computed(() => { // ricostruisci la vista quando pages cambia (evita durante apply)
return [...pages.value].sort((a, b) => (a.order || 0) - (b.order || 0)); watch(
}); () => pages.value,
() => {
if (applyingRows.value) return;
rebuildAllViews();
},
{ deep: true, immediate: true }
);
function originalIndex(sortedIdx: number) { // ---- SELEZIONE / UTILS -------------------------------------------------
// mappa lindice sortato allindice reale in pages const currentIdx = computed(() =>
const item = localPagesSorted.value[sortedIdx]; pages.value.findIndex((p) => p.__key === selectedKey.value)
return pages.value.indexOf(item); );
function select(key?: string) {
selectedKey.value = key || null;
} }
const current = computed(() => pages.value[selectedIndex.value]); function displayPath(path?: string) {
if (!path) return '-';
return path.startsWith('/') ? path : '/' + path;
}
// Removed automatic order reindexing to preserve original order values // ---- AZIONI UI ---------------------------------------------------------
function addSubmenu() {
const p = pages.value.find((x) => x.__key === selectedKey.value);
if (!p) return;
// Duplicate outer function removed, keeping the inner implementation if (!Array.isArray(p.sottoMenu)) p.sottoMenu = [];
function addPage() { if (p.submenu !== true) p.submenu = true;
const newOrder = if (p.mainMenu !== true) p.mainMenu = true;
pages.value.reduce((max: number, p: IMyPage) => Math.max(max, p.order ?? 0), 0) +
1; // placeholder path
const np: IMyPage = { const base = '/nuova-voce';
let name = base;
let i = 1;
while (p.sottoMenu.includes(name)) {
i++;
name = `${base}-${i}`;
}
p.sottoMenu.push(name);
emit(
'update:modelValue',
pages.value.map((x) => ({ ...x }))
);
rebuildAllViews();
}
function addPage(bucket: Bucket) {
visualizzaEditor.value = true; // ⬅️ aggiungi
nuovaPagina.value = true; // ⬅️ aggiungi
const np: PageWithKey = {
title: '', title: '',
path: '/nuova-pagina-' + (Math.floor(Math.random() * 8999) + 1000).toString(), path: '/nuova-pagina',
icon: 'fa-regular fa-file-lines', icon: 'far fa-file-alt',
iconsize: '24px', iconsize: '24px',
active: true, active: true,
inmenu: true, inmenu: bucket === 'menu',
submenu: false,
onlyif_logged: false, onlyif_logged: false,
order: newOrder, order:
bucket === 'menu' ? computeAppendOrderForMenu() : computeAppendOrderForOff(),
__key: `tmp_${uidSeed++}`,
}; };
pages.value.push(np); pages.value.push(np);
selectedIndex.value = pages.value.length - 1; emit(
emit('update:modelValue', [...pages.value]); 'update:modelValue',
pages.value.map((p) => ({ ...p }))
);
rebuildAllViews();
selectedKey.value = np.__key!;
} }
function removePage(idx: number) { function removeAt(bucket: Bucket, idx: number) {
if (idx < 0) return; const target = bucket === 'menu' ? menuRows.value[idx] : offList.value[idx];
const page = pages.value[idx]; if (!target) return;
if (!page) return;
pages.value.splice(idx, 1);
if (selectedIndex.value === idx) selectedIndex.value = -1;
emit('update:modelValue', [...pages.value]);
// Add cleanup logic for page deletion
if (page._id) {
// Mark for deletion in backend
void deletePageFromServer(page);
}
}
async function deletePageFromServer(page: IMyPage) { $q.dialog({
if (!page._id) return; title: 'Conferma cancellazione',
message: `Sei sicuro di voler cancellare la pagina "${target.title || target.path}"?`,
cancel: true,
persistent: true,
}).onOk(async () => {
// rimuovi il record da pages
const key = target.__key;
const pathN = norm(target.path);
const i = pages.value.findIndex((p) => p.__key === key);
if (i >= 0) pages.value.splice(i, 1);
try { // pulisci eventuali riferimenti nei sottoMenu dei parent
const response = await fetch(`/api/pages/${page._id}`, { for (const parent of pages.value) {
method: 'DELETE', if (parent.inmenu && !parent.submenu && Array.isArray(parent.sottoMenu)) {
headers: { parent.sottoMenu = parent.sottoMenu.filter((p) => norm(p) !== pathN);
'Content-Type': 'application/json', }
},
});
if (!response.ok) {
throw new Error('Failed to delete page from server');
} }
} catch (error) {
console.error('Error deleting page:', error); emit(
// Revert changes if deletion fails 'update:modelValue',
pages.value.splice(originalIndex, 0, page); pages.value.map((p) => ({ ...p }))
emit('update:modelValue', [...pages.value]); );
rebuildAllViews();
if (selectedKey.value === key) selectedKey.value = null;
// opzionale: elimina anche lato server
try {
await globalStore.deletePage($q, target._id || '');
} catch {}
});
}
function move(bucket: Bucket, idx: number, delta: number) {
if (bucket === 'menu') {
const list = menuRows.value.slice();
const to = idx + delta;
if (to < 0 || to >= list.length) return;
const [it] = list.splice(idx, 1);
list.splice(to, 0, it);
menuRows.value = list;
onMenuDragChange({ moved: { newIndex: to } }); // ⬅️ usa handler con indice
selectedKey.value = it.__key!;
} else {
const list = offList.value.slice();
const to = idx + delta;
if (to < 0 || to >= list.length) return;
const [it] = list.splice(idx, 1);
list.splice(to, 0, it);
offList.value = list;
onOffDragChange({ moved: { newIndex: to } }); // ⬅️ idem
selectedKey.value = it.__key!;
} }
} }
function select(idx: number) { // ⬇️ Sostituisci la tua onMenuDragChange
selectedIndex.value = idx; function onMenuDragChange(evt?: any) {
} const movedIndex: number | undefined = evt?.moved?.newIndex;
applyingRows.value = true;
let deltas: { id: string; order: number }[] = [];
try {
deltas = applyMenuRows(menuRows.value, movedIndex);
} finally {
applyingRows.value = false;
rebuildAllViews();
}
function swap(i: number, j: number) { emit(
const a = pages.value[i]; 'update:modelValue',
const b = pages.value[j]; pages.value.map((p) => ({ ...p }))
if (!a || !b) return; );
const ao = a.order ?? i; if (deltas.length) emit('change-order', deltas);
const bo = b.order ?? j; if (typeof globalStore.aggiornaMenu === 'function') {
a.order = bo; try {
b.order = ao; globalStore.aggiornaMenu($router);
emit('update:modelValue', [...pages.value]); } catch {}
}
} }
// ⬇️ Sostituisci la tua onOffDragChange
function onOffDragChange(evt?: any) {
const movedIndex: number | undefined = evt?.moved?.newIndex;
const deltas = applyOffList(offList.value, movedIndex);
function moveUp(idx: number) { rebuildAllViews();
if (idx <= 0) return; emit(
const prev = idx - 1; 'update:modelValue',
const a = pages.value[idx]; pages.value.map((p) => ({ ...p }))
const b = pages.value[prev]; );
if (!a || !b) return; if (deltas.length) emit('change-order', deltas);
const ao = a.order ?? idx; if (typeof globalStore.aggiornaMenu === 'function') {
const bo = b.order ?? prev; try {
a.order = bo; globalStore.aggiornaMenu($router);
b.order = ao; } catch {}
emit('update:modelValue', [...pages.value]); }
} }
function moveDown(idx: number) {
if (idx >= pages.value.length - 1) return;
const next = idx + 1;
const a = pages.value[idx];
const b = pages.value[next];
if (!a || !b) return;
const ao = a.order ?? idx;
const bo = b.order ?? next;
a.order = bo;
b.order = ao;
emit('update:modelValue', [...pages.value]);
}
function onApply() { function onApply() {
emit('update:modelValue', [...pages.value]); emit(
emit('save', current.value); 'update:modelValue',
} pages.value.map((p) => ({ ...p }))
function onClose() { );
selectedIndex.value = -1; const cur = currentIdx.value >= 0 ? pages.value[currentIdx.value] : undefined;
emit('save', cur);
rebuildAllViews();
visualizzaEditor.value = false; // ⬅️ aggiungi
nuovaPagina.value = false; // ⬅️ aggiungi
} }
function editAt(idx: number) {
const key = (menuRows.value[idx] || offList.value[idx])?.__key;
selectedKey.value = key || selectedKey.value;
visualizzaEditor.value = true; // ⬅️ aggiungi
nuovaPagina.value = false; // ⬅️ aggiungi
}
function openKey(key?: string) {
const p = pages.value.find((x) => x.__key === key);
if (!p) return;
$router.push(`/${p.path}?edit=1`);
}
// ---- EXPOSE ------------------------------------------------------------
return { return {
pages, pages,
selectedIndex, menuRows,
localPagesSorted, offList,
current, selectedKey,
currentIdx,
// actions
select, select,
addPage, addPage,
removePage, addSubmenu,
moveUp, removeAt,
moveDown, move,
originalIndex, onMenuDragChange,
onOffDragChange,
onApply, onApply,
onClose, displayPath,
editAt,
openKey,
visualizzaEditor, // ⬅️ aggiungi
nuovaPagina, // ⬅️ aggiungi
}; };
}, },
}); });

View File

@@ -1,67 +1,122 @@
<template> <template>
<div class="row q-col-gutter-md"> <div class="row q-col-gutter-md">
<!-- Lista pagine --> <!-- COLONNA: Nel menu -->
<div class="col-12 col-md-5"> <div class="col-12 col-md-6">
<q-card flat bordered> <q-card
flat
bordered
>
<q-toolbar> <q-toolbar>
<q-toolbar-title>Pagine</q-toolbar-title> <q-toolbar-title>Menu</q-toolbar-title>
<q-btn dense icon="fas fa-plus" label="Aggiungi" @click="addPage" /> <q-badge
color="primary"
:label="menuRows.length"
/>
<q-space />
<q-btn
dense
icon="fas fa-plus"
label="Nuovo"
@click="addPage('menu')"
/>
<q-btn
dense
icon="fas fa-sitemap"
label="SottoMenu"
:disable="!selectedKey"
@click="addSubmenu()"
/>
</q-toolbar> </q-toolbar>
<q-list separator> <draggable
<q-item v-model="menuRows"
v-for="(p, idx) in localPagesSorted" item-key="__key"
:key="p._id || idx" group="pages"
clickable handle=".drag-handle"
:active="selectedIndex === originalIndex(idx)" :animation="180"
@click="select(originalIndex(idx))" ghost-class="bg-grey-2"
> @change="onMenuDragChange($event)"
<q-item-section avatar> >
<q-icon :name="p.icon || 'fa-regular fa-file-lines'" /> <template #item="{ element, index }">
</q-item-section> <MenuPageItem
:item="element"
<q-item-section> :selected="selectedKey === element.__key"
<q-item-label>{{ p.title || '(senza titolo)' }}</q-item-label> v-model:active="element.active"
<q-item-label caption>{{ p.path || '-' }}</q-item-label> :depth="element.__depth"
</q-item-section> variant="menu"
@select="select(element.__key)"
<q-item-section side top> @edit="editAt(index)"
<div class="row items-center q-gutter-xs"> @delete="removeAt('menu', index)"
<q-badge :color="p.active ? 'positive' : 'grey'">{{ p.active ? 'attiva' : 'spenta' }}</q-badge> @open="openKey(element.__key)"
<q-badge :color="p.inmenu ? 'primary' : 'grey'">menu</q-badge> />
</div> </template>
</q-item-section> </draggable>
<q-item-section side>
<div class="column q-gutter-xs">
<!--<q-btn dense round icon="fas fa-chevron-up" @click.stop="moveUp(originalIndex(idx))" />
<q-btn dense round icon="fas fa-chevron-down" @click.stop="moveDown(originalIndex(idx))" />-->
<q-btn dense round color="negative" icon="fas fa-trash" @click.stop="removePage(originalIndex(idx))" />
</div>
</q-item-section>
</q-item>
</q-list>
</q-card> </q-card>
</div> </div>
<!-- Editor pagina selezionata --> <!-- COLONNA: Fuori menu -->
<div class="col-12 col-md-7"> <div class="col-12 col-md-6">
<q-card
flat
bordered
>
<q-toolbar>
<q-toolbar-title>Pagine</q-toolbar-title>
<q-badge
color="grey-7"
:label="offList.length"
/>
<q-space />
<q-btn
dense
icon="fas fa-plus"
label="Aggiungi"
@click="addPage('off')"
/>
</q-toolbar>
<draggable
v-model="offList"
item-key="__key"
group="pages"
handle=".drag-handle"
:animation="180"
ghost-class="bg-grey-2"
@change="onOffDragChange($event)"
>
<template #item="{ element, index }">
<MenuPageItem
:item="element"
:selected="selectedKey === element.__key"
v-model:active="element.active"
variant="off"
:depth="0"
@select="select(element.__key)"
@delete="removeAt('off', index)"
@open="openKey(element.__key)"
/>
</template>
</draggable>
</q-card>
</div>
<!-- Editor -->
<q-dialog
v-model="visualizzaEditor"
persistent
>
<page-editor <page-editor
v-if="current" v-if="currentIdx !== -1"
v-model="pages[selectedIndex]" v-model="pages[currentIdx]"
@apply="onApply" @apply="onApply"
@close="onClose" @hide="visualizzaEditor = false"
:nuovaPagina="nuovaPagina"
/> />
<q-card v-else flat bordered class="q-pa-lg flex flex-center text-grey"> </q-dialog>
Seleziona o aggiungi una pagina.
</q-card>
</div>
</div> </div>
</template> </template>
<script lang="ts" src="./PagesConfigurator.ts"> <script lang="ts" src="./PagesConfigurator.ts"></script>
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import './PagesConfigurator.scss'; @import './PagesConfigurator.scss';
</style> </style>

18
src/composables/useDnd.js Normal file
View File

@@ -0,0 +1,18 @@
import { ref } from 'vue';
export const useDnd = () => {
const dragging = ref(false);
const draggedElement = ref(null);
const startDrag = (element) => {
dragging.value = true;
draggedElement.value = element;
};
const endDrag = () => {
dragging.value = false;
draggedElement.value = null;
};
return { dragging, draggedElement, startDrag, endDrag };
};

View File

@@ -0,0 +1,29 @@
import { ref } from 'vue';
export const useHistory = () => {
const history = ref([]);
const currentIndex = ref(-1);
const addToHistory = (state) => {
history.value.push(state);
currentIndex.value = history.value.length - 1;
};
const undo = () => {
if (currentIndex.value > 0) {
currentIndex.value--;
return history.value[currentIndex.value];
}
return null;
};
const redo = () => {
if (currentIndex.value < history.value.length - 1) {
currentIndex.value++;
return history.value[currentIndex.value];
}
return null;
};
return { history, addToHistory, undo, redo };
};

View File

@@ -1,65 +1,86 @@
import type { IListRoutes } from '@src/model' import type { IListRoutes } from '@src/model';
import { useGlobalStore } from '@store/globalStore' import { useGlobalStore } from '@store/globalStore';
import { tools } from '@tools' import { tools } from '@tools';
import { computed, defineComponent, ref, watch } from 'vue' import { computed, defineComponent, ref, watch } from 'vue';
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router';
import { static_data } from '@src/db/static_data' import { static_data } from '@src/db/static_data';
import { useUserStore } from '@store/UserStore' import { useUserStore } from '@store/UserStore';
import { CMenuItem } from '../../components/CMenuItem' import { CMenuItem } from '../../components/CMenuItem';
export default defineComponent({ export default defineComponent({
name: 'MenuOne', name: 'MenuOne',
components: { CMenuItem }, components: { CMenuItem },
setup(props) { setup(props) {
const route = useRoute() const route = useRoute();
const userStore = useUserStore() const userStore = useUserStore();
const globalStore = useGlobalStore() const globalStore = useGlobalStore();
const finishLoading = computed(() => globalStore.finishLoading) const finishLoading = computed(() => globalStore.finishLoading);
const updateMenu = computed(() => globalStore.updateMenu);
const path = computed(() => route.path) const path = computed(() => route.path);
const getroutes = computed(() => static_data.routes) const getroutes = computed(() => static_data.routes);
const myroutes = ref(<IListRoutes[]>[]) const myroutes = ref(<IListRoutes[]>[]);
const getmenu = computed(() => globalStore.getmenu) const getmenu = computed(() => globalStore.getmenu);
const islogged = computed(() => userStore.isLogged) const islogged = computed(() => userStore.isLogged);
const clBase = ref('my-menu') const clBase = ref('my-menu');
function setParentVisibilityBasedOnRoute(parent: any) { function setParentVisibilityBasedOnRoute(parent: any) {
parent.routes.forEach((item: any) => { parent.routes.forEach((item: any) => {
if (path.value === item.path) { if (path.value === item.path) {
parent.show = true parent.show = true;
} }
}) });
} }
function updatemenu() { function updatemenu() {
const mymenu = globalStore.getmenu console.log('*** updatemenu');
const mymenu = globalStore.getmenu;
Object.keys(mymenu).forEach((parentName: any) => { Object.keys(mymenu).forEach((parentName: any) => {
// @ts-ignore // @ts-ignore
setParentVisibilityBasedOnRoute(mymenu[parentName]) setParentVisibilityBasedOnRoute(mymenu[parentName]);
}) });
myroutes.value = [] myroutes.value = [];
myroutes.value = static_data.routes myroutes.value = static_data.routes;
globalStore.updateMenu = false;
} }
watch(() => islogged.value, (to, from) => { watch(
updatemenu() () => islogged.value,
}) (to, from) => {
updatemenu();
}
);
watch(() => finishLoading.value, (to, from) => { watch(
updatemenu() () => finishLoading.value,
}) (to, from) => {
updatemenu();
}
);
watch(() => path, (to, from) => { watch(
updatemenu() () => updateMenu.value,
}) (newValue, oldValue) => {
if (newValue) updatemenu();
},
{ deep: true }
);
watch(
() => path,
(to, from) => {
updatemenu();
}
);
/* function replaceUnderlineToSpace(text: string) { /* function replaceUnderlineToSpace(text: string) {
while (text.indexOf('_') !== -1) { while (text.indexOf('_') !== -1) {
@@ -69,61 +90,56 @@ export default defineComponent({
} */ } */
function getroute(elem: IListRoutes) { function getroute(elem: IListRoutes) {
let link = '' let link = '';
if (elem.idelem) { if (elem.idelem) {
link = tools.getUrlByTipoProj(elem.urlroute ? elem.urlroute : '') + elem.idelem link = tools.getUrlByTipoProj(elem.urlroute ? elem.urlroute : '') + elem.idelem;
} }
if (!link) if (!link) link = elem.path;
link = elem.path
// console.log('getroute LINK=', link) // console.log('getroute LINK=', link)
return link return link;
} }
function getmymenuclass(elem: IListRoutes) { function getmymenuclass(elem: IListRoutes) {
let menu: string = clBase.value let menu: string = clBase.value;
if (elem.color) { if (elem.color) {
menu += ` ${elem.color}` menu += ` ${elem.color}`;
} else { } else {
if (elem.onlyAdmin) menu += ' isAdmin' if (elem.onlyAdmin) menu += ' isAdmin';
if (elem.onlyManager) menu += ' isManager' if (elem.onlyManager) menu += ' isManager';
if (elem.onlySocioResidente) menu += ' isSocioResidente' if (elem.onlySocioResidente) menu += ' isSocioResidente';
if (elem.onlyConsiglio) menu += ' isConsiglio' if (elem.onlyConsiglio) menu += ' isConsiglio';
if (elem.onlyDepartment) menu += ' isDepartment' if (elem.onlyDepartment) menu += ' isDepartment';
if (elem.onlyFacilitatore) menu += ' isFacilitatore' if (elem.onlyFacilitatore) menu += ' isFacilitatore';
if (elem.onlyEditor) menu += ' isEditor' if (elem.onlyEditor) menu += ' isEditor';
if (elem.onlyCommerciale) menu += ' isCommerciale' if (elem.onlyCommerciale) menu += ' isCommerciale';
if (elem.onlyCollaboratore) menu += ' isCollaboratore' if (elem.onlyCollaboratore) menu += ' isCollaboratore';
if (elem.onlyGrafico) menu += ' isGrafico' if (elem.onlyGrafico) menu += ' isGrafico';
} }
if (elem.extraclass) menu += ` ${elem.extraclass}` if (elem.extraclass) menu += ` ${elem.extraclass}`;
// console.log('menu', menu) // console.log('menu', menu)
return menu return menu;
} }
function getimgiconclass(elem: IListRoutes) { function getimgiconclass(elem: IListRoutes) {
if (elem.extraclass) if (elem.extraclass) return elem.extraclass;
return elem.extraclass else return 'imgicon';
else
return 'imgicon'
} }
function getimgiconclass2(elem: IListRoutes) { function getimgiconclass2(elem: IListRoutes) {
if (elem.extraclass) if (elem.extraclass) return elem.extraclass;
return elem.extraclass else return 'clBase';
else
return 'clBase'
} }
function getmenuByPath(path: string) { function getmenuByPath(path: string) {
const mymenufind = static_data.routes.find((menu: any) => menu.path === '/' + path) const mymenufind = static_data.routes.find((menu: any) => menu.path === '/' + path);
return mymenufind return mymenufind;
} }
myroutes.value = static_data.routes myroutes.value = static_data.routes;
return { return {
getmenu, getmenu,
@@ -138,6 +154,6 @@ export default defineComponent({
getimgiconclass2, getimgiconclass2,
getmenuByPath, getmenuByPath,
clBase, clBase,
} };
}, },
}) });

View File

@@ -194,6 +194,8 @@ export interface IMyElem {
elemsText?: IElemText[] elemsText?: IElemText[]
titleBanner?: string titleBanner?: string
classBanner?: string classBanner?: string
color?: string
children?: any[]
} }
export interface IElemText { export interface IElemText {
@@ -236,6 +238,7 @@ export interface IMyPage {
elemsText?: IElemText[] elemsText?: IElemText[]
onlyif_logged?: boolean onlyif_logged?: boolean
only_residenti?: boolean only_residenti?: boolean
only_admin?: boolean
only_consiglio?: boolean only_consiglio?: boolean
only_collab?: boolean only_collab?: boolean
submenu?: boolean submenu?: boolean
@@ -479,6 +482,7 @@ export interface ISelector {
} }
export interface IGlobalState { export interface IGlobalState {
finishLoading: boolean finishLoading: boolean
updateMenu: boolean
showHeader?: boolean showHeader?: boolean
inStampa?: boolean inStampa?: boolean
conta: number conta: number

View File

@@ -1,103 +1,153 @@
import { defineComponent, ref, watch, onMounted } from 'vue' import { defineComponent, ref, watch, onMounted } from 'vue';
import { useQuasar } from 'quasar' import { useQuasar } from 'quasar';
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia';
import { IMyPage } from 'app/src/model' import { IMyPage } from 'app/src/model';
import { useGlobalStore } from 'app/src/store' import { useGlobalStore } from 'app/src/store';
import { PagesConfigurator } from '@src/components/PagesConfigurator' import { PagesConfigurator } from '@src/components/PagesConfigurator';
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
export default defineComponent({ export default defineComponent({
name: 'PagesAdmin', name: 'PagesAdmin',
components: { PagesConfigurator }, components: { PagesConfigurator },
setup () { setup() {
const $q = useQuasar() const $q = useQuasar();
const { t } = useI18n() const { t } = useI18n();
const globalStore = useGlobalStore() const globalStore = useGlobalStore();
const { mypage } = storeToRefs(globalStore) const { mypage } = storeToRefs(globalStore);
const $router = useRouter()
// Copia locale per lediting tramite v-model // Copia locale per lediting tramite v-model
const pagesLocal = ref<IMyPage[]>([]) const pagesLocal = ref<IMyPage[]>([]);
const savingAll = ref(false) const savingAll = ref(false);
// Mantieni pagesLocal <-> store allineati // Mantieni pagesLocal <-> store allineati
const syncFromStore = () => { const syncFromStore = () => {
pagesLocal.value = (mypage.value || []).map(p => ({ ...p })) pagesLocal.value = (mypage.value || []).map((p) => ({ ...p }));
} };
watch(mypage, syncFromStore, { deep: true }) watch(mypage, syncFromStore, { deep: true });
onMounted(async () => { onMounted(async () => {
// Se hai una funzione per caricare tutte le pagine, chiamala qui // Se hai una funzione per caricare tutte le pagine, chiamala qui
// es. await globalStore.loadAllPages() // es. await globalStore.loadAllPages()
syncFromStore() syncFromStore();
}) });
// Salva singola pagina (chiamato da PagesConfigurator @save) const lastOrderSnapshot = ref<Map<string, number>>(new Map());
async function saveOne (page: IMyPage) { const savingOrders = ref(false);
try {
const saved = await globalStore.savePage({ ...page }) // crea/ricostruisce lo snapshot (solo pagine con _id)
if (saved && typeof saved === 'object') { function initOrderSnapshot(arr: IMyPage[]) {
// rimpiazza nel local e nello store (lo store viene aggiornato da savePage) const m = new Map<string, number>();
const idx = pagesLocal.value.findIndex(p => for (const p of arr) {
(p._id && p._id === saved._id) || (p.path && p.path === saved.path) if (p._id) m.set(p._id, p.order ?? 0);
)
if (idx >= 0) pagesLocal.value[idx] = { ...saved }
$q.notify({ type: 'positive', message: `Salvata: ${saved.title || saved.path}` })
} else {
$q.notify({ type: 'positive', message: 'Salvataggio completato' })
}
} catch (err) {
console.error(err)
$q.notify({ type: 'negative', message: 'Errore nel salvataggio della pagina' })
} }
lastOrderSnapshot.value = m;
} }
// call una volta dopo aver caricato le pagine
onMounted(() => {
initOrderSnapshot(pagesLocal.value); // pagesLocal è il tuo array locale già usato
});
// quando salvi una pagina singola e ottieni l'_id, aggiorna lo snapshot
async function saveOne(p: IMyPage) {
try {
const saved = await globalStore.savePage({ ...p });
if (saved && saved._id) {
// aggiorna la copia locale
const idx = pagesLocal.value.findIndex(
(x) => (x._id && x._id === saved._id) || (x.path && x.path === saved.path)
);
if (idx >= 0) pagesLocal.value[idx] = { ...saved };
// aggiorna lo snapshot dell'ordinamento persistito
lastOrderSnapshot.value.set(
saved._id,
saved.order ?? pagesLocal.value[idx]?.order ?? 0
);
$q.notify({
type: 'positive',
message: `Salvata: ${saved.title || saved.path}`,
});
}
} catch (e) {
console.error(e);
$q.notify({ type: 'negative', message: 'Errore nel salvataggio della pagina' });
}
}
// Salva lordinamento (fallback: batch di savePage per aggiornare order) // Salva lordinamento (fallback: batch di savePage per aggiornare order)
async function saveOrder (orders: { id?: string; order: number }[]) { async function saveOrder(orders: { id?: string; order: number }[]) {
try { try {
// Applica l'order nella copia locale if (savingOrders.value) return;
for (const { id, order } of orders) { savingOrders.value = true;
const p = pagesLocal.value.find(x => x._id === id) ||
pagesLocal.value.find(x => !id && typeof x.order === 'number' && x.order === order) // 1) calcola solo i cambi VERI su pagine già persistite (_id presente)
if (p) p.order = order const changedExisting = orders.filter((o) => {
if (!o.id) return false; // ignora nuovi senza id
const prev = lastOrderSnapshot.value.get(o.id);
return prev === undefined ? false : prev !== o.order;
});
// 2) se non è cambiato nulla di "persistito", esci silenziosamente
if (changedExisting.length === 0) {
// console.debug('[saveOrder] nessun cambiamento persistente')
return;
} }
// Fallback: se non hai un endpoint /reorder, salvi singolarmente
// fallback: salva solo le pagine cambiate
await Promise.all( await Promise.all(
pagesLocal.value.map(p => globalStore.savePage({ ...p })) changedExisting.map(({ id, order }) => {
) const p = pagesLocal.value.find((x) => x._id === id);
$q.notify({ type: 'positive', message: 'Ordinamento salvato' }) if (!p) return Promise.resolve();
// se il tuo savePage accetta payload parziale, meglio passare solo {_id, order}
// altrimenti manda l'oggetto intero con order aggiornato
return globalStore.savePage({ ...p, order });
})
);
// 4) aggiorna lo snapshot solo per gli elementi realmente salvati
for (const { id, order } of changedExisting) {
if (id) lastOrderSnapshot.value.set(id, order);
}
$q.notify({ type: 'positive', message: 'Ordinamento aggiornato' });
globalStore.aggiornaMenu($router);
} catch (err) { } catch (err) {
console.error(err) console.error(err);
$q.notify({ type: 'negative', message: 'Errore nel salvataggio dellordinamento' }) $q.notify({
type: 'negative',
message: 'Errore nel salvataggio dellordinamento',
});
} finally {
savingOrders.value = false;
} }
} }
// Ricarica lelenco (se disponibile una API globale; altrimenti rilegge dallo store) // Ricarica lelenco (se disponibile una API globale; altrimenti rilegge dallo store)
async function reloadAll () { async function reloadAll() {
try { try {
// Se esiste: await globalStore.loadAllPages() // Se esiste: await globalStore.loadAllPages()
syncFromStore() syncFromStore();
$q.notify({ type: 'info', message: 'Elenco ricaricato' }) $q.notify({ type: 'info', message: 'Elenco ricaricato' });
} catch (err) { } catch (err) {
console.error(err) console.error(err);
$q.notify({ type: 'negative', message: 'Errore nel ricaricare lelenco' }) $q.notify({ type: 'negative', message: 'Errore nel ricaricare lelenco' });
} }
} }
// Salva tutto (batch) // Salva tutto (batch)
async function saveAll () { async function saveAll() {
try { try {
savingAll.value = true savingAll.value = true;
await Promise.all( await Promise.all(pagesLocal.value.map((p) => globalStore.savePage({ ...p })));
pagesLocal.value.map(p => globalStore.savePage({ ...p })) $q.notify({ type: 'positive', message: 'Tutte le pagine salvate' });
)
$q.notify({ type: 'positive', message: 'Tutte le pagine salvate' })
} catch (err) { } catch (err) {
console.error(err) console.error(err);
$q.notify({ type: 'negative', message: 'Errore nel salvataggio massivo' }) $q.notify({ type: 'negative', message: 'Errore nel salvataggio massivo' });
} finally { } finally {
savingAll.value = false savingAll.value = false;
} }
} }
@@ -109,6 +159,6 @@ export default defineComponent({
saveAll, saveAll,
savingAll, savingAll,
t, t,
} };
} },
}) });

View File

@@ -1,7 +1,7 @@
<template> <template>
<q-page class="q-pa-md"> <q-page class="q-pa-md">
<q-toolbar class="q-mb-md"> <q-toolbar class="q-mb-md">
<q-toolbar-title>Configuratore Pagine</q-toolbar-title> <q-toolbar-title class="text-center">Configura Pagine</q-toolbar-title>
<q-space /> <q-space />
<q-btn <q-btn
flat flat

View File

@@ -0,0 +1,615 @@
import { CMyPage } from '../../../components/CMyPage/index'
import { shared_consts } from '@src/common/shared_vuejs'
import { tools } from '@src/store/Modules/tools'
import { defineComponent, ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useUserStore } from '@store/UserStore'
import { useGlobalStore } from '@store/globalStore'
import { useQuasar } from 'quasar'
import type { IParamsQuery } from 'model'
import { toolsext } from '@store/Modules/toolsext'
import { StringDecoder } from 'string_decoder'
export default defineComponent({
name: 'importaprodotti',
components: { CMyPage },
setup(props, { emit }) {
const $q = useQuasar()
const { t } = useI18n()
const userStore = useUserStore()
const globalStore = useGlobalStore()
const arrSector = ref(<any[]>[])
const arrSectorGood = ref(<any[]>[])
const arrSkill = ref(<any[]>[])
const arrGood = ref(<any[]>[])
const importasulserver = ref(false)
const skipfirstrow = ref(true)
const incaricamento = ref(false)
const checkAggiornaQta = ref(false)
const cosafare = ref(shared_consts.Cmd.PRODUCTS_V2)
const inputfile = ref('')
const risultato = ref('')
const risraw = ref('')
const caricaDatiToggle = ref(false)
const ListaCmd = ref(
[
{
label: 'Importa Prodotti',
value: shared_consts.Cmd.PRODUCTS
},
{
label: 'Importa Prodotti V2',
value: shared_consts.Cmd.PRODUCTS_V2
},
]
)
function caricadati() {
if (!caricaDatiToggle.value) {
arrSector.value = []
arrSectorGood.value = []
arrSkill.value = []
arrGood.value = []
return
}
const sortBy = 'descr'
const descending = 1
const myobj: any = {}
if (descending)
myobj[sortBy] = -1
else
myobj[sortBy] = 1
const params: IParamsQuery = {
newvers: true,
table: '',
startRow: 0,
endRow: 10000,
filter: '',
filterand: '',
filtersearch: '',
filtersearch2: '',
filtercustom: '',
filterextra: '',
filterextra2: '',
filter_gte: '',
sortBy: myobj,
descending,
userId: '',
noaut: false,
}
if (true) {
params.table = toolsext.TABSECTORS
globalStore.loadTable(params).then((data) => {
arrSector.value = data.rows
})
params.table = 'sectorgoods'
globalStore.loadTable(params).then((data) => {
arrSectorGood.value = data.rows
})
params.table = 'skills'
globalStore.loadTable(params).then((data) => {
arrSkill.value = data.rows
})
params.table = 'goods'
globalStore.loadTable(params).then((data) => {
arrGood.value = data.rows
})
}
}
function created() {
// se idapp = 18
if (tools.getIdApp() === tools.IDAPP_MACRO) {
cosafare.value = shared_consts.Cmd.MACRO_CATALOGO_JSON
}
inputfile.value = ''
if (caricaDatiToggle.value) {
caricadati()
}
}
function createSector(cat: string, cmd: number) {
let arr = []
if (cmd === shared_consts.Cmd.CAT_GOODS_TXT) {
arr = arrSectorGood.value
} else {
arr = arrSector.value
}
const myid = arr.length + 1
arr.push({ _id: myid, descr: cat })
return myid
}
function findidSector(cat: string, cmd: number) {
let arr = []
if (cmd === shared_consts.Cmd.CAT_GOODS_TXT) {
arr = arrSectorGood.value
} else {
arr = arrSector.value
}
const rec = arr.find((rec) => rec.descr === cat)
if (rec) {
return rec._id
}
return 0;
}
function findidSkill(cat: string, cmd: number) {
let arr = []
if (cmd === shared_consts.Cmd.CAT_GOODS_TXT) {
arr = arrGood.value
} else {
arr = arrSkill.value
}
const rec = arr.find((rec) => rec.descr === cat)
if (rec) {
return rec._id
}
return 0;
}
function createSkill(cat: string, cmd: number) {
let arr = []
if (cmd === shared_consts.Cmd.CAT_GOODS_TXT) {
arr = arrGood.value
} else {
arr = arrSkill.value
}
const myid = arr.length + 1
arr.push({ _id: myid, descr: cat })
return myid
}
function importCmdTxt(cmd: number, testo: string) {
const delim = '\n';
const righe = 1;
let indrec = 0;
let myarr = tools.CSVToArray(testo, delim)
let sector = ''
let skill = ''
let sotto_cat = ''
let idSector = 0
let idSkill = 0
let strskills = '';
let strsubskills = '';
let strsectors = '';
let indrecsub = 1;
myarr = myarr[0]
let arrstr = []
// debugger;
for (let i = 0; i < myarr.length; i = i + righe) {
arrstr = myarr[i].split(',')
sector = arrstr[0]
skill = arrstr[1]
// sotto_cat = arrstr[2]
// sotto_cat = myarr[i].replace('\'', '\\\'')
// sector = myarr[i+2]
if (skill)
skill = skill.replace('\'', '\\\'')
if (sector)
sector = sector.replace('\'', '\\\'')
if (sector) {
idSector = findidSector(sector, cmd)
if (!idSector) {
idSector = createSector(sector, cmd)
// sectors
strsectors += '{ \n'
strsectors += ' _id:' + idSector + ','
strsectors += ' descr:\'' + sector + '\','
strsectors += '}, \n'
}
if (skill !== '') {
idSkill = findidSkill(skill, cmd)
if (!idSkill) {
idSkill = createSkill(skill, cmd)
// skills
strskills += '{ \n'
strskills += ' _id:' + idSkill + ','
if (cmd === shared_consts.Cmd.CAT_GOODS_TXT) {
strskills += ' idSectorGood: [' + idSector + '],'
} else if (cmd === shared_consts.Cmd.CAT_SKILL_TXT) {
strskills += ' idSector: [' + idSector + '],'
}
strskills += ' descr:\'' + skill + '\','
strskills += '}, \n'
}
}
/*
if (sotto_cat !== '') {
// subskills
strsubskills += '{ \n'
strsubskills += ' idSkill: ' + idSkill + ','
strsubskills += ' descr:\'' + sotto_cat + '\','
strsubskills += '}, \n'
}
*/
}
indrecsub++
}
let ris = 'module.exports = {\n' +
' list: [' + strsectors + ']'
ris += '<br><br><br><br>'
ris += 'module.exports = {\n' +
' list: [' + strskills + ']'
ris += '<br><br><br><br>'
/*ris += 'module.exports = {\n' +
' list: [' + strsubskills + ']'
*/
return ris
}
function importMacroCatalogoJson(cmd: number, testo: string) {
return testo
}
function importNoSpazi(cmd: number, testo: string) {
const delim = '\n';
const righe = 3;
let indrec = 0;
let myarr = tools.CSVToArray(testo, delim)
let sector = ''
let sotto_cat = ''
myarr = myarr[0]
let txt = ''
// debugger;
for (let i = 0; i < myarr.length; i = i + righe) {
sotto_cat = myarr[i].replace('\'', '\\\'')
sector = myarr[i + 2]
txt += sotto_cat + ',' + sector + '<br>'
}
return txt
}
function importCmd(cmd: number, testo: string) {
let risultato = '(nessuno)'
let delim = ','
if (cmd === shared_consts.Cmd.PROVINCE) {
delim = ','
} else if ((cmd === shared_consts.Cmd.COMUNI) || (cmd === shared_consts.Cmd.CITIES_SERVER)) {
delim = ';'
} else if (cmd === shared_consts.Cmd.CAT_SKILL_TXT) {
return importCmdTxt(cmd, testo);
} else if (cmd === shared_consts.Cmd.CAT_GOODS_TXT) {
return importCmdTxt(cmd, testo);
} else if (cmd === shared_consts.Cmd.CAT_NO_SPAZI) {
return importNoSpazi(cmd, testo);
} else if ((cmd === shared_consts.Cmd.MACRO_CATALOGO_JSON || cmd === shared_consts.Cmd.MACRO_RANKING)) {
return importMacroCatalogoJson(cmd, testo);
} else if (cmd === shared_consts.Cmd.MACRO_DESCRELINKSITOWEB) {
// console.log('TESTO PRIMA:', testo)
const testoJSON = tools.convertXMLStringToJSON(testo)
const testoJSONtoPrint = JSON.stringify(testoJSON, null, 2)
// console.log(testoJSONtoPrint)
return importMacroCatalogoJson(cmd, testoJSONtoPrint)
}
function addfield(col: number, field: string, rec: any, opt: any) {
let risultato = ''
try {
let valstr = opt.strinput ? opt.strinput : rec[col]
if (opt.isnumero) {
if (valstr === '')
valstr = '0';
valstr = valstr.replace(',', '.');
}
if (opt.iseuro) {
valstr = tools.convertPriceEurToValue(valstr)
}
valstr = tools.removeescape_e_acapo(valstr)
// valstr = tools.addslashes(valstr)
if (!opt.primo)
risultato += ', '
risultato += '"' + field + '":"' + valstr + '"'
return risultato;
} catch (e) {
console.error('err', e);
}
}
const myarr = tools.CSVToArray(testo, delim)
let strris = ''
let ind = 1
let primo = true
let arrCols: any = []
if (skipfirstrow.value) {
arrCols = myarr[0]
}
console.log('arrCols', arrCols)
if (cmd === shared_consts.Cmd.PRODUCTS_V2) {
skipfirstrow.value = false
} else if ((cmd === shared_consts.Cmd.MACRO_CATALOGO_JSON) || (cmd === shared_consts.Cmd.MACRO_RANKING) || (cmd === shared_consts.Cmd.MACRO_DESCRELINKSITOWEB)) {
skipfirstrow.value = false
}
for (const rec of myarr) {
if (skipfirstrow.value) {
if (ind === 1) {
ind++;
continue;
}
}
let lab = tools.addslashes(rec[0])
let val = tools.addslashes(rec[1])
if (cmd === shared_consts.Cmd.PROVINCE) {
let reg = tools.addslashes(rec[1])
val = tools.addslashes(rec[2])
strris += '{ \n'
strris += ' _id:' + ind + ','
strris += ' reg:\'' + reg + '\','
strris += ' prov:\'' + val + '\','
strris += ' descr:\'' + lab + '\','
strris += '}, \n'
} else if (cmd === shared_consts.Cmd.COMUNI) {
strris += '{ \n'
strris += ' istat:\'' + tools.addslashes(rec[0]) + '\','
strris += ' comune:\'' + tools.addslashes(rec[1]) + '\','
strris += ' prov:\'' + tools.addslashes(rec[2]) + '\''
} else if (cmd === shared_consts.Cmd.PRODUCTS) {
if (!primo) {
strris += ', '
}
strris += '{ '
let col = 0;
strris += addfield(col, 'idapp', rec, { strinput: tools.appid(), primo: true });
strris += addfield(col, 'code', rec, {}); col++;
strris += addfield(col, 'name', rec, {}); col++;
strris += addfield(col, 'price', rec, { iseuro: true }); col++;
strris += addfield(col, 'stockQty', rec, { isnumero: true }); col++;
col++;
strris += addfield(col, 'weight', rec, { isnumero: true }); col++;
strris += addfield(col, 'unit', rec, {}); col++;
strris += addfield(col, 'link', rec, {}); col++;
strris += addfield(col, 'perc_iva', rec, {}); col++;
strris += addfield(col, 'price_acquistato', rec, { isnumero: true, iseuro: true }); col++;
strris += addfield(col, 'minBuyQty', rec, { isnumero: true }); col++;
strris += addfield(col, 'minStepQty', rec, { isnumero: true }); col++;
strris += addfield(col, 'sconto', rec, {}); col++;
strris += addfield(col, 'cat_name', rec, {}); col++;
strris += addfield(col, 'subcat_name', rec, {}); col++;
strris += addfield(col, 'producer_name', rec, {}); col++;
strris += addfield(col, 'provider_name', rec, {}); col++;
strris += addfield(col, 'magazzino_name', rec, {}); col++;
strris += addfield(col, 'qtyToReachForGas', rec, { isnumero: true }); col++;
strris += addfield(col, 'maxbookableGASQty', rec, { isnumero: true }); col++;
strris += addfield(col, 'sconto1', rec, {}); col++;
strris += addfield(col, 'sconto2', rec, {}); col++;
strris += addfield(col, 'gas_name', rec, {}); col++;
strris += addfield(col, 'note', rec, {}); col++;
strris += '} '
} else if (cmd === shared_consts.Cmd.PRODUCTS_V2) {
if (!primo) {
strris += ', '
}
strris += '{ '
let col = 0;
strris += addfield(col, 'idapp', rec, { strinput: tools.appid(), primo: true });
for (const mycol of arrCols) {
strris += addfield(col, mycol, rec, {}); col++;
}
strris += '} '
} else if (cmd === shared_consts.Cmd.INVENTARIO) {
if (!primo) {
strris += ', '
}
strris += '{ '
let col = 0;
strris += addfield(col, 'idapp', rec, { strinput: tools.appid(), primo: true });
for (const mycol of arrCols) {
strris += addfield(col, mycol, rec, {}); col++;
}
strris += '} '
} else if (cmd === shared_consts.Cmd.CITIES_SERVER) {
strris += '{ \n'
strris += ' _id :' + ind + ',\n'
strris += ' istat :\'' + rec[0] + '\'\n,'
strris += ' comune :\'' + tools.addslashes(rec[1]) + '\'\n,'
strris += ' prov :\'' + rec[2] + '\'\n,'
strris += ' reg :\'' + tools.addslashes(rec[3]) + '\'\n,'
strris += ' pref :\'' + tools.addslashes(rec[4]) + '\'\n,'
strris += ' cap :\'' + rec[5] + '\'\n,'
strris += ' abitanti :\'' + rec[6] + '\'\n,'
strris += ' country : \'IT\'\n'
strris += '}, \n'
}
ind += 1
primo = false
}
if (cmd === shared_consts.Cmd.CITIES_SERVER) {
userStore.importToServerCmd($q, t, cmd, null, true)
} else if ((cmd === shared_consts.Cmd.PRODUCTS) || (cmd === shared_consts.Cmd.PRODUCTS_V2)) {
let options = { aggiornaStockQty: checkAggiornaQta.value }
if (importasulserver.value)
userStore.importToServerCmd($q, t, cmd, { arrdata: JSON.stringify(strris, null, 2), options }, true)
}
risultato = strris
return risultato
}
function reset() {
risultato.value = ''
risraw.value = ''
inputfile.value = ''
}
function loadTextFromFile(ev: any) {
try {
console.log('ev', ev)
reset()
if (ev.target && ev.target.files) {
const file = ev.target.files[0]
const reader = new FileReader()
reader.onload = (e: any) => {
const testo = e.target.result
risultato.value = importCmd(cosafare.value, testo)
}
if (file)
reader.readAsText(file)
}
} catch (e) {
risultato.value = ''
}
}
function eseguiCmd() {
risultato.value = ''
userStore.importToServerCmd($q, t, cosafare.value, null)
}
function eseguiCmdProduct() {
let options = { aggiornaStockQty: checkAggiornaQta.value }
userStore.importToServerCmd($q, t, cosafare.value, { arrdata: risultato.value, options }, true)
risultato.value = ''
}
function eseguiCmdInventario() {
let options = { aggiornaStockQty: checkAggiornaQta.value }
userStore.importToServerCmd($q, t, cosafare.value, { arrdata: risultato.value, options }, true)
risultato.value = ''
}
function eseguiCmdCatalogoJson() {
let options = { aggiornaStockQty: checkAggiornaQta.value }
userStore.importToServerCmd($q, t, cosafare.value, { arrdata: risultato.value, options }, true)
risultato.value = ''
}
function createProvLink() {
let str = ''
const arr = globalStore.provinces
let regione = ''
let regid = ''
for (const prov of arr) {
if (prov.link_grp) {
if (prov.reg !== regid) {
const myreg = shared_consts.Regions.find((rec: any) => rec.value === prov.reg)
if (myreg) {
regid = myreg.value
regione = myreg.label
str += '<br><div class="text-subtitle1">' + regione + '</div>'
}
}
str += '<a class="prov" href="' + prov.link_grp + '" target="_blank">' + prov.descr + '</a><br>'
}
}
risultato.value = str
risraw.value = str
}
onMounted(created)
return {
inputfile,
shared_consts,
loadTextFromFile,
risultato,
cosafare,
ListaCmd,
eseguiCmd,
caricaDatiToggle,
caricadati,
createProvLink,
risraw,
importasulserver,
skipfirstrow,
eseguiCmdProduct,
checkAggiornaQta,
eseguiCmdInventario,
eseguiCmdCatalogoJson,
}
}
})

View File

@@ -0,0 +1,49 @@
<template>
<CMyPage
img=""
title="Importa Prodotti"
keywords=""
description=""
>
<div class="q-ma-sm">
<q-toggle
v-model="skipfirstrow"
label="Salta la prima riga"
></q-toggle>
<q-toggle
v-model="checkAggiornaQta"
label="Aggiorna Quantità in Magazzino"
></q-toggle>
<label class="text-reader">
<input
type="file"
@change="loadTextFromFile"
/>
</label>
<br />
<br />
<div class="row justify-center">
<q-btn
label="Importa Prodotti"
@click="eseguiCmdProduct"
></q-btn>
</div>
<br />
<div>{{ risraw }}</div>
<div
v-if="risultato"
v-html="risultato.substring(0, 1000)"
></div>
<br />
<br />
</div>
</CMyPage>
</template>
<script lang="ts" src="./importaprodotti.ts"></script>
<style lang="scss" scoped>
@import './importaprodotti.scss';
</style>

View File

@@ -273,22 +273,6 @@ function getRoutesAd(site: ISites) {
}, },
*/ */
{
active: site.confpages?.showMenuCoins,
path: '/admin/ris',
order: 60,
faIcon: 'fa fa-list-alt',
materialIcon: 'fas fa-coins',
name: 'otherpages.admin.monete',
routes2: routes_ris,
inmenu: false,
submenu: true,
level_parent: 0.5,
level_child: 0.5,
solotitle: true,
onlyAdmin: true,
onlyManager: true
},
/*{ /*{
active: false, active: false,
order: 10, order: 10,
@@ -398,22 +382,6 @@ function getRoutesAd(site: ISites) {
onlyManager: true, onlyManager: true,
onlyEditor: false onlyEditor: false
}, },
{
active: site.confpages && site.confpages?.sendNewsletter,
path: '/admin/newsletter',
order: 60,
faIcon: 'fa fa-list-alt',
materialIcon: 'fas fa-users',
name: 'otherpages.admin.newsletter',
routes2: routes_newsletter,
inmenu: false,
submenu: true,
level_parent: 0.5,
level_child: 0.5,
solotitle: true,
onlyAdmin: true,
onlyManager: true
},
{ {
active: true, active: true,
order: 35, order: 35,
@@ -471,6 +439,38 @@ function getRoutesAd(site: ISites) {
onlyAdmin: true, onlyAdmin: true,
onlyManager: true onlyManager: true
}, },
{
active: site.confpages?.showMenuCoins,
path: '/admin/ris',
order: 60,
faIcon: 'fa fa-list-alt',
materialIcon: 'fas fa-coins',
name: 'otherpages.admin.monete',
routes2: routes_ris,
inmenu: false,
submenu: true,
level_parent: 0.5,
level_child: 0.5,
solotitle: true,
onlyAdmin: true,
onlyManager: true
},
{
active: site.confpages && site.confpages?.sendNewsletter,
path: '/admin/newsletter',
order: 60,
faIcon: 'fa fa-list-alt',
materialIcon: 'fas fa-users',
name: 'otherpages.admin.newsletter',
routes2: routes_newsletter,
inmenu: false,
submenu: true,
level_parent: 0.5,
level_child: 0.5,
solotitle: true,
onlyAdmin: true,
onlyManager: true
},
] ]
const menuAdmins = [ const menuAdmins = [

View File

@@ -70,6 +70,19 @@ function getRoutesEcomm(site: ISites) {
onlyManager: true, onlyManager: true,
onlyEditor: true onlyEditor: true
}, },
{
active: true,
order: 1040,
path: '/products/importa',
materialIcon: 'fas fa-upload',
name: 'otherpages.manage.importaprodotti',
component: () => import('@src/rootgen/admin/importaprodotti/importaprodotti.vue'),
level_parent: 0.0,
level_child: 0.5,
inmenu: true,
submenu: true,
onlyAdmin: true
},
{ {
active: true, active: true,
order: 30, order: 30,

View File

@@ -194,6 +194,7 @@ const msg_it = {
nessuno: 'Nessuno', nessuno: 'Nessuno',
sendpushnotif: 'Invia Messaggi', sendpushnotif: 'Invia Messaggi',
importfile: 'Importa File', importfile: 'Importa File',
importaprodotti: 'Importa Prodotti',
convertPDF: 'Converti PDF', convertPDF: 'Converti PDF',
}, },
messages: { messages: {
@@ -316,7 +317,12 @@ const msg_it = {
comp: { comp: {
Conta: 'Conta', Conta: 'Conta',
}, },
pages: {
onlyif_logged: 'solo se Registrati',
only_admin: 'visibile solo agli Admin',
},
db: { db: {
deletedpage: 'Pagina Cancellata',
recupdated: 'Record Aggiornato', recupdated: 'Record Aggiornato',
records_imported: '{num} Record Importati', records_imported: '{num} Record Importati',
records_updated: '{num} Record Aggiornati', records_updated: '{num} Record Aggiornati',

View File

@@ -64,6 +64,7 @@ export const useGlobalStore = defineStore('GlobalStore', {
state: (): IGlobalState => ({ state: (): IGlobalState => ({
showHeader: true, showHeader: true,
inStampa: false, inStampa: false,
updateMenu: false,
finishLoading: false, finishLoading: false,
conta: 0, conta: 0,
wasAlreadySubscribed: false, wasAlreadySubscribed: false,
@@ -791,7 +792,7 @@ export const useGlobalStore = defineStore('GlobalStore', {
addDynamicPages($router: Router | null) { addDynamicPages($router: Router | null) {
// console.log('this.mypage', this.mypage) // console.log('this.mypage', this.mypage)
/// console.log('addDynamicPages.........') // console.log('addDynamicPages.........')
const arrpagesroute: IListRoutes[] = []; const arrpagesroute: IListRoutes[] = [];
for (const page of this.mypage) { for (const page of this.mypage) {
@@ -886,10 +887,6 @@ export const useGlobalStore = defineStore('GlobalStore', {
// Sort array // Sort array
static_data.routes = static_data.routes.sort((a, myb) => a.order - myb.order); static_data.routes = static_data.routes.sort((a, myb) => a.order - myb.order);
// console.log('static_data.routes', static_data.routes)
// console.log('$router', $router)
if ($router) { if ($router) {
static_data.routes.forEach((route: any) => { static_data.routes.forEach((route: any) => {
if (!$router.hasRoute(route.name) && !route.noroute) { if (!$router.hasRoute(route.name) && !route.noroute) {
@@ -933,6 +930,7 @@ export const useGlobalStore = defineStore('GlobalStore', {
} }
} }
} }
this.updateMenu = true;
}, },
async aggiornaMenu(router: Router) { async aggiornaMenu(router: Router) {
@@ -1446,7 +1444,7 @@ export const useGlobalStore = defineStore('GlobalStore', {
// console.log('mypage', mypage) // console.log('mypage', mypage)
// Controlla se l'ho già caricato // Controlla se l'ho già caricato
if ((!!mypage && (!!mypage.content || mypage.loadFirst || mypage.loaded)) && !forza) { if (!!mypage && (!!mypage.content || mypage.loadFirst || mypage.loaded) && !forza) {
return mypage; return mypage;
} }
@@ -1478,8 +1476,8 @@ export const useGlobalStore = defineStore('GlobalStore', {
return Api.SendReq('/savepage', 'POST', { page }) return Api.SendReq('/savepage', 'POST', { page })
.then((res) => { .then((res) => {
if (res && res.data && res.data.ris) { if (res && res.data && res.data.mypage) {
return res.data.ris; return res.data.mypage;
} else { } else {
return null; return null;
} }
@@ -1713,6 +1711,25 @@ export const useGlobalStore = defineStore('GlobalStore', {
}); });
}, },
async deletePage($q: any, idpage: string) {
console.log('deletePage', idpage);
return Api.SendReq('/api/mypage/' + idpage, 'DELETE', null)
.then((res) => {
if (res.status === 200) {
if (res.data.code === serv_constants.RIS_CODE_OK) {
tools.showPositiveNotif($q, t('db.deletedpage'));
return true;
}
}
return false;
})
.catch((error) => {
console.error(error);
return false;
});
},
async DeleteRec({ table, id }: { table: string; id: string }) { async DeleteRec({ table, id }: { table: string; id: string }) {
console.log('DeleteRec', table, id); console.log('DeleteRec', table, id);

View File

@@ -136,7 +136,7 @@
<div class="text-center q-py-sm prod_trov"> <div class="text-center q-py-sm prod_trov">
{{ {{
t('ecomm.prodotti_trovati', { t('ecomm.prodotti_trovati', {
qta: arrProducts.length, qta: getArrProducts.length,
qtatot: qtatot:
productStore.getNumProdTot() > 0 productStore.getNumProdTot() > 0
? ` ${t('ecomm.su')} ${productStore.getNumProdTot()}` ? ` ${t('ecomm.su')} ${productStore.getNumProdTot()}`

5438
yarn.lock

File diff suppressed because it is too large Load Diff