- 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_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_VUE_ROUTER_MODE="history"

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "cnm",
"version": "1.2.68",
"version": "1.2.69",
"description": "Comunita Nuovo Mondo",
"productName": "ComunitaNuovoMondo",
"author": "Surya",
@@ -9,7 +9,7 @@
"license": "MIT",
"type": "module",
"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",
"build": "quasar build",
"buildpwa": "NODE_ENV=production quasar build -m pwa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.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",
"pwa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.68' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.68' quasar dev",
"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.69' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "freeplanet",
"version": "1.2.68",
"version": "1.2.69",
"description": "freeplanet",
"productName": "freeplanet",
"author": "Surya",
@@ -9,11 +9,11 @@
"license": "MIT",
"type": "module",
"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",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa",
"buildpwatest": "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.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.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",
"pwa": "NODE_ENV=development PORT=8097 APP_VERSION='1.2.68' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8087 APP_VERSION='1.2.68' quasar dev",
"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.69' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{
"name": "gruppomacro",
"version": "1.2.68",
"version": "1.2.69",
"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.",
"author": "Surya",
@@ -9,20 +9,20 @@
"license": "MIT",
"type": "module",
"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",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa",
"buildpwatest": "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.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"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}\"",
"lintfile": "eslint --ext .js,.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",
"pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.68' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8089 APP_VERSION='1.2.68' quasar dev",
"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.69' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{
"name": "kolibrilab",
"version": "1.2.68",
"version": "1.2.69",
"description": "kolibrilab",
"productName": "kolibrilab",
"author": "Surya Paolo",
@@ -8,11 +8,11 @@
"keywords": [],
"license": "MIT",
"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",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa",
"buildpwatest": "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.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa",
@@ -20,8 +20,8 @@
"lintfile": "eslint --ext .js,.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",
"pwa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.68' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.68' quasar dev",
"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.69' quasar dev",
"spanorefresh": "NODE_ENV=development NODE_OPTIONS=--max_old_space_size=4096 DEBUG=v8:* quasar dev -m spa",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js"

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "nutriben",
"version": "1.2.68",
"version": "1.2.69",
"description": "Nutriben",
"productName": "Nutriben",
"author": "Surya",
@@ -9,20 +9,20 @@
"license": "MIT",
"type": "module",
"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",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa",
"buildpwatest": "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.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"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}\"",
"lintfile": "eslint --ext .js,.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",
"pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.68' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.68' quasar dev",
"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.69' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{
"name": "piuchebuono",
"version": "1.2.68",
"version": "1.2.69",
"description": "PiuCheBuono",
"productName": "PiuCheBuono",
"author": "Surya",
@@ -9,11 +9,11 @@
"license": "MIT",
"type": "module",
"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",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa",
"buildpwatest": "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.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.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",
"pwa": "NODE_ENV=development PORT=8085 APP_VERSION='1.2.68' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8085 APP_VERSION='1.2.68' quasar dev",
"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.69' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{
"name": "riso",
"version": "1.2.68",
"version": "1.2.69",
"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.",
"author": "Surya",
@@ -9,11 +9,11 @@
"license": "MIT",
"type": "module",
"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",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.68' quasar build -m pwa",
"buildpwatest": "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.69' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.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",
"pwa": "NODE_ENV=development PORT=8094 APP_VERSION='1.2.68' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8084 APP_VERSION='1.2.68' quasar dev",
"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.69' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"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 cfgenv */
const VITE_APP_VERSION = '1.2.68';
const VITE_APP_VERSION = '1.2.69';
// Costanti di configurazione
const DYNAMIC_CACHE = 'dynamic-cache-v2';

View File

@@ -174,6 +174,9 @@ export const shared_consts = {
SEARCHPRODUCT: 430,
RACCOLTE_CATALOGHI: 450,
STAT_PAGES: 460,
SECTION: 1000,
ROW: 1100,
COLUMN: 1200,
},
QUERYTYPE_MYGROUP: 1,
@@ -2008,6 +2011,22 @@ export const shared_consts = {
],
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,
label: 'Testo',

View File

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

View File

@@ -357,7 +357,7 @@ export default defineComponent({
recOrderCart.value = rissconto.mycart
};
}
} catch (error) {
} catch (error: any) {
console.log('error ApplicaSconto', error);
tools.showNegativeNotif($q, `Sconto Non Applicato! ${error?.message || ''}`);
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() {
return props.filetype === shared_consts.FILETYPE.IMG;
}
/*
const uploadFactory = async (files: readonly File[]) => {
const userStore = useUserStore();
const url = getUrl();
@@ -451,11 +452,11 @@ export default defineComponent({
// usa la tua logica centralizzata
Api.checkTokenScaduto(
status,
/*evitaloop*/ false,
false,
url,
'POST',
null,
/*setAuthToken*/ true
true
);
if (ret !== null) {
// token aggiornato -> ritenta UNA volta
@@ -475,7 +476,7 @@ export default defineComponent({
throw err2;
}
}
};
}; */
onMounted(created);
@@ -514,7 +515,6 @@ export default defineComponent({
isIMG,
isPDF,
upl,
uploadFactory,
t,
};
},

View File

@@ -1,44 +1,35 @@
<template>
<div>
<div v-if="arrprovince" id="map" :style="`height:${myheight()}px; width:99%`">
</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 class="map-container">
<div id="map" style="height: 500px;"></div>
</div>
</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>
<style lang="scss" scoped>
@import './CMapUsers.scss';
<style scoped>
.map-container {
width: 100%;
height: 100%;
}
</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) {
console.log('updateElem', myvalue)
if (myel.value.type === shared_consts.ELEMTYPE.IMGTITLE) {

View File

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

View File

@@ -10,6 +10,37 @@
"
>
<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
v-if="editOn"
class="btn-edit-floating"
@@ -228,7 +259,7 @@
:title="myel.container"
:myheight="myel.heightimg"
:vertalign="myel.vertalign"
:speed="myel.speed"
:speed="speedSafe"
:elemsText="myel.elemsText"
:logo="tools.getImgFileByFilename(myel, myel.img)"
:logoheight="myel.height ? myel.height.toString() : '100'"
@@ -1100,6 +1131,7 @@
<div v-else-if="myel.type === shared_consts.ELEMTYPE.FOOTER">
<LandingFooter />
</div>
<div v-if="editOn">
<div class="q-ma-md"></div>
</div>

View File

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

View File

@@ -78,7 +78,7 @@ export default defineComponent({
const { t } = useI18n();
const globalStore = useGlobalStore();
const $router = useRouter();
const $route = useRoute()
const $route = useRoute();
const mywidthEditor = ref(400);
@@ -125,6 +125,12 @@ export default defineComponent({
async function load() {
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 !== '') {
onloading.value = true;
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() {
$q.dialog({
title: order.value.product.productInfo.name,
title: order.value.product?.productInfo?.name,
message: 'Sicuro di voler rimuovere il prodotto dal carrello?',
ok: {
label: 'Rimuovi',
@@ -222,7 +222,7 @@ export default defineComponent({
function mounted() {
endload.value = false;
weight.value = props.order.product?.productInfo.weight;
weight.value = props.order.product?.productInfo?.weight;
price.value = props.order.price;
if (props.order.quantity !== 0) {
orderQuantity.value = props.order.quantity;

View File

@@ -1,10 +1,4 @@
import { defineComponent, ref, computed, onMounted, watch, onBeforeUnmount } 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'
import { defineComponent, ref, computed, watch } from 'vue'
export default defineComponent({
name: 'IconPicker',
@@ -12,49 +6,75 @@ export default defineComponent({
modelValue: { type: String, default: '' },
icons: {
type: Array as () => string[],
default: () => ([
// Estendi pure questo set
'fas fa-house',
// SOLO Font Awesome 5 (free)
default: () => [
'fas fa-home',
'fas fa-book',
'fas fa-star',
'fas fa-heart',
'fas fa-user',
'fas fa-gear',
'fas fa-circle-info',
'fas fa-newspaper',
'fas fa-cog',
'fas fa-info-circle',
'far fa-newspaper',
'fas fa-list',
'fas fa-tags',
'fas fa-chart-line',
'fas fa-briefcase',
'fas fa-envelope',
'fas fa-phone',
'fas fa-earth-europe',
])
'fas fa-globe-europe'
]
}
},
emits: ['update:modelValue', 'change'],
setup (props, { emit }) {
const keyword = ref('')
const local = ref(props.modelValue)
const local = ref(props.modelValue) // testo inserito/valore corrente
const dialog = ref(false) // mostra/nasconde il picker
const keyword = ref('') // filtro dentro il dialog
watch(() => props.modelValue, v => { local.value = v })
const filteredIcons = computed(() => {
const k = keyword.value.trim().toLowerCase()
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) {
local.value = val
emit('update:modelValue', val)
emit('change', val)
// applica la stringa così comè; nessun fallback
emit('update:modelValue', val || '')
emit('change', val || '')
}
function onKeyword () {
// solo aggiorna la lista; il pulsante "Usa testo" applica
function choose (ic: string) {
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,33 +1,57 @@
<template>
<div class="q-gutter-sm">
<div class="row items-center q-col-gutter-sm">
<div class="col">
<div class="col-12">
<q-input
v-model="keyword"
v-model="local"
dense
clearable
label="Cerca icona (es: fas fa-house)"
@update:model-value="onKeyword"
label="Icona"
>
<template #prepend>
<q-icon :name="modelValue || 'fa-regular fa-face-smile'" />
<q-icon v-if="local" :name="local" />
</template>
</q-input>
</div>
<div class="col-auto">
<q-btn
dense
outline
:disable="!keyword"
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>
<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">
<q-input
v-model="keyword"
dense
clearable
autofocus
label="Cerca (es: home, user, info...)"
/>
<div class="row q-col-gutter-sm q-mt-sm">
<div
v-for="ic in filteredIcons"
:key="ic"
@@ -37,31 +61,26 @@
outline
class="full-width"
:icon="ic"
@click="select(ic)"
:color="ic === modelValue ? 'primary' : 'grey-7'"
@click="choose(ic)"
>
<q-tooltip>{{ ic }}</q-tooltip>
</q-btn>
</div>
</div>
<div class="q-mt-md">
<q-input
v-model="local"
dense
label="Valore attuale icona"
@update:model-value="select(local)"
>
<template #append>
<q-icon :name="local || 'fa-regular fa-circle-question'" />
</template>
</q-input>
<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>
</template>
<script lang="ts" src="./IconPicker.ts"></script>
<style lang="scss" scoped>
@import './IconPicker.scss';
/* opzionale: spaziatura minima sulle celle */
</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 {
defineComponent,
ref,
computed,
onMounted,
watch,
onBeforeUnmount,
toRaw,
nextTick,
} from 'vue';
defineComponent, ref, computed, watch, reactive, toRaw, nextTick
} from 'vue'
import { useQuasar } from 'quasar'
import IconPicker from '../IconPicker/IconPicker.vue'
import { IMyPage } from 'app/src/model'
import { useGlobalStore } from 'app/src/store'
import { storeToRefs } from 'pinia'
import { useI18n } from 'vue-i18n'
import { costanti } from '@costanti'
import { useUserStore } from '@store/UserStore';
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';
import { CMyFieldRec } from '@src/components/CMyFieldRec'
export default defineComponent({
name: 'PageEditor',
components: { IconPicker },
components: { IconPicker, CMyFieldRec },
props: {
modelValue: {
type: Object as () => IMyPage,
required: true,
modelValue: { type: Object as () => IMyPage, required: true },
nuovaPagina: { type: Boolean, required: true } // <-- modalità "bozza"
},
},
emits: ['update:modelValue', 'apply'],
setup(props, { emit }) {
const $q = useQuasar();
const globalStore = useGlobalStore();
const { mypage } = storeToRefs(globalStore);
emits: ['update:modelValue', 'apply', 'hide'],
setup (props, { emit }) {
const $q = useQuasar()
// DRaft locale
const draft = reactive<IMyPage>({ ...props.modelValue });
const { t } = useI18n()
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
const ui = reactive({
pathText: toUiPath(draft.path),
});
const ui = reactive({ pathText: toUiPath(draft.path) })
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
// --- Watch input esterno: ricarica draft e UI path (NO scritture nello store qui!)
// --- Sync IN: quando cambia il valore del parent, aggiorna solo il draft
watch(
() => props.modelValue,
async (v) => {
syncingFromProps.value = true;
Object.assign(draft, v || {});
ui.pathText = toUiPath(draft.path);
await nextTick();
syncingFromProps.value = false;
async v => {
syncingFromProps.value = true
Object.assign(draft, v || {})
ui.pathText = toUiPath(draft.path)
await nextTick()
syncingFromProps.value = 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(
draft,
(val) => {
if (syncingFromProps.value) return; // evita ricorsione
upsertIntoStore(val, mypage.value);
emit('update:modelValue', { ...val });
if (syncingFromProps.value) return
if (props.nuovaPagina) return // <-- blocca ogni propagazione durante "nuova pagina"
upsertIntoStore(val, mypage.value)
emit('update:modelValue', { ...val })
},
{ deep: true }
);
)
// --- Helpers path
function toUiPath(storePath?: string) {
const p = (storePath || '').trim();
if (!p) return '/';
return p.startsWith('/') ? p : `/${p}`;
function toUiPath (storePath?: string) {
const p = (storePath || '').trim()
if (!p) return '/'
return p.startsWith('/') ? p : `/${p}`
}
function toStorePath(uiPath?: string) {
const p = (uiPath || '').trim();
if (!p) return '';
return p.startsWith('/') ? p.slice(1) : p;
function toStorePath (uiPath?: string) {
const p = (uiPath || '').trim()
if (!p) return ''
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() {
// normalizza: niente spazi, minuscole, trattini
let p = (ui.pathText || '/').trim();
p = p.replace(/\s+/g, '-');
if (!p.startsWith('/')) p = '/' + p;
ui.pathText = p;
draft.path = toStorePath(p);
function pathRule (v: string) {
if (!v) return 'Percorso richiesto'
if (!v.startsWith('/')) return 'Deve iniziare con /'
if (/\s/.test(v)) return 'Nessuno spazio nel path'
return true
}
function pathRule(v: string) {
if (!v) return 'Percorso richiesto';
if (!v.startsWith('/')) return 'Deve iniziare con /';
if (/\s/.test(v)) return 'Nessuno spazio nel path';
return true;
// --- Upsert nello store.mypage (usato solo quando NON è nuovaPagina o dopo il save)
function upsertIntoStore (page: IMyPage, arr: IMyPage[]) {
if (!page) return
const keyId = page._id
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
function upsertIntoStore(page: IMyPage, arr: IMyPage[]) {
if (!page) return;
// chiave di matching: prima _id, altrimenti path
const keyId = page._id;
const keyPath = page.path || '';
let idx = -1;
if (keyId) {
idx = arr.findIndex((p) => p._id === keyId);
// --- VALIDAZIONE + COMMIT per nuova pagina (o anche per edit espliciti)
async function checkAndSave (payloadDraft?: IMyPage) {
const cur = payloadDraft || draft
// validazioni base
if (!cur.title?.trim()) {
$q.notify({ message: 'Inserisci il titolo della pagina', type: 'warning' })
return
}
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à
arr[idx] = { ...arr[idx], ...toRaw(page) };
} else {
arr.push({ ...toRaw(page) });
const candidatePath = toStorePath(pathText).toLowerCase()
// unicità PATH (ignora se stesso quando editing)
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
}
async function save () {
await save() // esegue commit vero
emit('hide') // chiudi il dialog (se usi dialog)
}
// --- Salvataggio esplicito (commit). Qui propaghiamo SEMPRE al parent/store.
async function save () {
try {
saving.value = true
normalizeAndApplyPath() // assicura path coerente
const payload: IMyPage = { ...toRaw(draft), path: draft.path || '' }
const saved = await globalStore.savePage(payload)
if (saved && typeof saved === 'object') {
syncingFromProps.value = true
Object.assign(draft, saved)
upsertIntoStore(draft, mypage.value)
upsertIntoStore(draft, mypage.value) // ora è lecito anche per nuovaPagina
await nextTick()
syncingFromProps.value = false
}
// IMPORTANTISSIMO: in nuovaPagina non abbiamo mai emesso prima → emettiamo ora
emit('update:modelValue', { ...draft })
emit('apply', { ...draft })
$q.notify({ type: 'positive', message: 'Pagina salvata' })
} catch (err: any) {
} catch (err) {
console.error(err)
$q.notify({ type: 'negative', message: 'Errore nel salvataggio' })
} finally {
@@ -143,7 +169,7 @@ async function save () {
}
}
// --- Ricarica da sorgente
// --- Ricarica (per editing). In modalità nuovaPagina non propaghiamo al parent.
async function reloadFromStore () {
try {
const absolute = ui.pathText || '/'
@@ -153,8 +179,7 @@ async function save () {
Object.assign(draft, page)
ui.pathText = toUiPath(draft.path)
upsertIntoStore(draft, mypage.value)
console.log('page', draft)
emit('update:modelValue', { ...draft })
if (!props.nuovaPagina) emit('update:modelValue', { ...draft }) // <-- no propagate in nuovaPagina
await nextTick()
syncingFromProps.value = false
$q.notify({ type: 'info', message: 'Pagina ricaricata' })
@@ -167,16 +192,18 @@ async function save () {
}
}
function resetDraft() {
console.log('resetDraft')
function resetDraft () {
syncingFromProps.value = true
Object.assign(draft, props.modelValue || {})
ui.pathText = toUiPath(draft.path)
// aggiorna i componenti
emit('update:modelValue', { ...draft })
if (!props.nuovaPagina) emit('update:modelValue', { ...draft }) // <-- no propagate in nuovaPagina
nextTick(() => { syncingFromProps.value = false })
}
function modifElem() {
}
const absolutePath = computed(() => toUiPath(draft.path))
return {
@@ -189,6 +216,10 @@ async function save () {
reloadFromStore,
resetDraft,
absolutePath,
};
},
});
checkAndSave,
t,
costanti,
modifElem,
}
}
})

View File

@@ -1,5 +1,9 @@
<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="col-12 col-md-6">
<q-input
@@ -20,53 +24,99 @@
v-model="draft.title"
label="Titolo"
dense
:rules="[v => !!v || 'Titolo richiesto']"
:rules="[(v) => !!v || 'Titolo richiesto']"
/>
</div>
<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>
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">
<q-input v-model="draft.iconsize" label="Dimensione icona (es: 24px)" dense />
<icon-picker v-model="draft.icon" />
</div>
<div class="col-12">
<q-separator spaced />
<div class="row items-center q-col-gutter-md">
<div class="col-auto">
<q-toggle v-model="draft.active" label="Attivo" />
<q-toggle
v-model="draft.active"
label="Attivo"
/>
</div>
<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 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 class="row q-col-gutter-sm q-mt-md">
<div class="col-auto">
<q-btn color="primary" label="Salva" :loading="saving" @click="save" />
</div>
<div class="col-auto">
<q-btn outline label="Ricarica" @click="reloadFromStore" />
</div>
<div class="col-auto">
<q-btn color="grey-7" label="Chiudi" @click="$emit('close', draft.path)" />
</div>
<div class="col-auto">
<q-btn flat color="grey-7" label="Reset draft" @click="resetDraft" />
</div>
</div>
<q-card-actions
align="center"
class="q-pa-md q-gutter-md"
>
<q-btn
color="primary"
label="Salva"
:loading="saving"
@click="checkAndSave(draft)"
/>
<q-btn
outline
label="Ricarica"
@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>
</template>
<script lang="ts" src="./PageEditor.ts">
</script>
<script lang="ts" src="./PageEditor.ts"></script>
<style lang="scss" scoped>
@import './PageEditor.scss';

View File

@@ -1,160 +1,542 @@
import { defineComponent, ref, computed, onMounted, watch, onBeforeUnmount } from 'vue';
import { IMyPage } from 'app/src/model';
import { PageEditor } from '../PageEditor';
import { defineComponent, ref, computed, watch } from 'vue';
import type { IMyPage } from 'app/src/model';
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({
name: 'PagesConfigurator',
components: { PageEditor },
components: { PageEditor, MenuPageItem, draggable },
props: {
modelValue: {
type: Array as () => IMyPage[],
required: true,
},
modelValue: { type: Array as () => IMyPage[], required: true },
},
emits: ['update:modelValue', 'save', 'change-order'],
setup(props, { emit }) {
const pages = ref<IMyPage[]>(props.modelValue ? [...props.modelValue] : []);
const selectedIndex = ref<number>(-1);
const $q = useQuasar();
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(
() => props.modelValue,
(v) => {
pages.value = v ? [...v] : [];
if (selectedIndex.value >= pages.value.length) selectedIndex.value = -1;
}
pages.value = (v || []).map((p) => ({ ...p }));
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(() => {
return [...pages.value].sort((a, b) => (a.order || 0) - (b.order || 0));
});
// ricostruisci la vista quando pages cambia (evita durante apply)
watch(
() => pages.value,
() => {
if (applyingRows.value) return;
rebuildAllViews();
},
{ deep: true, immediate: true }
);
function originalIndex(sortedIdx: number) {
// mappa lindice sortato allindice reale in pages
const item = localPagesSorted.value[sortedIdx];
return pages.value.indexOf(item);
// ---- SELEZIONE / UTILS -------------------------------------------------
const currentIdx = computed(() =>
pages.value.findIndex((p) => p.__key === selectedKey.value)
);
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
function addPage() {
const newOrder =
pages.value.reduce((max: number, p: IMyPage) => Math.max(max, p.order ?? 0), 0) +
1;
const np: IMyPage = {
if (!Array.isArray(p.sottoMenu)) p.sottoMenu = [];
if (p.submenu !== true) p.submenu = true;
if (p.mainMenu !== true) p.mainMenu = true;
// placeholder path
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: '',
path: '/nuova-pagina-' + (Math.floor(Math.random() * 8999) + 1000).toString(),
icon: 'fa-regular fa-file-lines',
path: '/nuova-pagina',
icon: 'far fa-file-alt',
iconsize: '24px',
active: true,
inmenu: true,
inmenu: bucket === 'menu',
submenu: false,
onlyif_logged: false,
order: newOrder,
order:
bucket === 'menu' ? computeAppendOrderForMenu() : computeAppendOrderForOff(),
__key: `tmp_${uidSeed++}`,
};
pages.value.push(np);
selectedIndex.value = pages.value.length - 1;
emit('update:modelValue', [...pages.value]);
emit(
'update:modelValue',
pages.value.map((p) => ({ ...p }))
);
rebuildAllViews();
selectedKey.value = np.__key!;
}
function removePage(idx: number) {
if (idx < 0) return;
const page = pages.value[idx];
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);
function removeAt(bucket: Bucket, idx: number) {
const target = bucket === 'menu' ? menuRows.value[idx] : offList.value[idx];
if (!target) return;
$q.dialog({
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);
// pulisci eventuali riferimenti nei sottoMenu dei parent
for (const parent of pages.value) {
if (parent.inmenu && !parent.submenu && Array.isArray(parent.sottoMenu)) {
parent.sottoMenu = parent.sottoMenu.filter((p) => norm(p) !== pathN);
}
}
async function deletePageFromServer(page: IMyPage) {
if (!page._id) return;
emit(
'update:modelValue',
pages.value.map((p) => ({ ...p }))
);
rebuildAllViews();
if (selectedKey.value === key) selectedKey.value = null;
// opzionale: elimina anche lato server
try {
const response = await fetch(`/api/pages/${page._id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
await globalStore.deletePage($q, target._id || '');
} catch {}
});
if (!response.ok) {
throw new Error('Failed to delete page from server');
}
} catch (error) {
console.error('Error deleting page:', error);
// Revert changes if deletion fails
pages.value.splice(originalIndex, 0, page);
emit('update:modelValue', [...pages.value]);
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) {
selectedIndex.value = idx;
// ⬇️ Sostituisci la tua onMenuDragChange
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) {
const a = pages.value[i];
const b = pages.value[j];
if (!a || !b) return;
const ao = a.order ?? i;
const bo = b.order ?? j;
a.order = bo;
b.order = ao;
emit('update:modelValue', [...pages.value]);
emit(
'update:modelValue',
pages.value.map((p) => ({ ...p }))
);
if (deltas.length) emit('change-order', deltas);
if (typeof globalStore.aggiornaMenu === 'function') {
try {
globalStore.aggiornaMenu($router);
} catch {}
}
function moveUp(idx: number) {
if (idx <= 0) return;
const prev = idx - 1;
const a = pages.value[idx];
const b = pages.value[prev];
if (!a || !b) return;
const ao = a.order ?? idx;
const bo = b.order ?? prev;
a.order = bo;
b.order = ao;
emit('update:modelValue', [...pages.value]);
}
// ⬇️ Sostituisci la tua onOffDragChange
function onOffDragChange(evt?: any) {
const movedIndex: number | undefined = evt?.moved?.newIndex;
const deltas = applyOffList(offList.value, movedIndex);
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]);
rebuildAllViews();
emit(
'update:modelValue',
pages.value.map((p) => ({ ...p }))
);
if (deltas.length) emit('change-order', deltas);
if (typeof globalStore.aggiornaMenu === 'function') {
try {
globalStore.aggiornaMenu($router);
} catch {}
}
}
function onApply() {
emit('update:modelValue', [...pages.value]);
emit('save', current.value);
}
function onClose() {
selectedIndex.value = -1;
emit(
'update:modelValue',
pages.value.map((p) => ({ ...p }))
);
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 {
pages,
selectedIndex,
localPagesSorted,
current,
menuRows,
offList,
selectedKey,
currentIdx,
// actions
select,
addPage,
removePage,
moveUp,
moveDown,
originalIndex,
addSubmenu,
removeAt,
move,
onMenuDragChange,
onOffDragChange,
onApply,
onClose,
displayPath,
editAt,
openKey,
visualizzaEditor, // ⬅️ aggiungi
nuovaPagina, // ⬅️ aggiungi
};
},
});

View File

@@ -1,67 +1,122 @@
<template>
<div class="row q-col-gutter-md">
<!-- Lista pagine -->
<div class="col-12 col-md-5">
<q-card flat bordered>
<!-- COLONNA: Nel menu -->
<div class="col-12 col-md-6">
<q-card
flat
bordered
>
<q-toolbar>
<q-toolbar-title>Pagine</q-toolbar-title>
<q-btn dense icon="fas fa-plus" label="Aggiungi" @click="addPage" />
<q-toolbar-title>Menu</q-toolbar-title>
<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-list separator>
<q-item
v-for="(p, idx) in localPagesSorted"
:key="p._id || idx"
clickable
:active="selectedIndex === originalIndex(idx)"
@click="select(originalIndex(idx))"
<draggable
v-model="menuRows"
item-key="__key"
group="pages"
handle=".drag-handle"
:animation="180"
ghost-class="bg-grey-2"
@change="onMenuDragChange($event)"
>
<q-item-section avatar>
<q-icon :name="p.icon || 'fa-regular fa-file-lines'" />
</q-item-section>
<q-item-section>
<q-item-label>{{ p.title || '(senza titolo)' }}</q-item-label>
<q-item-label caption>{{ p.path || '-' }}</q-item-label>
</q-item-section>
<q-item-section side top>
<div class="row items-center q-gutter-xs">
<q-badge :color="p.active ? 'positive' : 'grey'">{{ p.active ? 'attiva' : 'spenta' }}</q-badge>
<q-badge :color="p.inmenu ? 'primary' : 'grey'">menu</q-badge>
</div>
</q-item-section>
<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>
</div>
<!-- Editor pagina selezionata -->
<div class="col-12 col-md-7">
<page-editor
v-if="current"
v-model="pages[selectedIndex]"
@apply="onApply"
@close="onClose"
<template #item="{ element, index }">
<MenuPageItem
:item="element"
:selected="selectedKey === element.__key"
v-model:active="element.active"
:depth="element.__depth"
variant="menu"
@select="select(element.__key)"
@edit="editAt(index)"
@delete="removeAt('menu', index)"
@open="openKey(element.__key)"
/>
<q-card v-else flat bordered class="q-pa-lg flex flex-center text-grey">
Seleziona o aggiungi una pagina.
</template>
</draggable>
</q-card>
</div>
<!-- COLONNA: Fuori menu -->
<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
v-if="currentIdx !== -1"
v-model="pages[currentIdx]"
@apply="onApply"
@hide="visualizzaEditor = false"
:nuovaPagina="nuovaPagina"
/>
</q-dialog>
</div>
</template>
<script lang="ts" src="./PagesConfigurator.ts">
</script>
<script lang="ts" src="./PagesConfigurator.ts"></script>
<style lang="scss" scoped>
@import './PagesConfigurator.scss';
</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 { useGlobalStore } from '@store/globalStore'
import { tools } from '@tools'
import { computed, defineComponent, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { static_data } from '@src/db/static_data'
import { useUserStore } from '@store/UserStore'
import type { IListRoutes } from '@src/model';
import { useGlobalStore } from '@store/globalStore';
import { tools } from '@tools';
import { computed, defineComponent, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { static_data } from '@src/db/static_data';
import { useUserStore } from '@store/UserStore';
import { CMenuItem } from '../../components/CMenuItem'
import { CMenuItem } from '../../components/CMenuItem';
export default defineComponent({
name: 'MenuOne',
components: { CMenuItem },
setup(props) {
const route = useRoute()
const userStore = useUserStore()
const globalStore = useGlobalStore()
const route = useRoute();
const userStore = useUserStore();
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) {
parent.routes.forEach((item: any) => {
if (path.value === item.path) {
parent.show = true
parent.show = true;
}
})
});
}
function updatemenu() {
const mymenu = globalStore.getmenu
console.log('*** updatemenu');
const mymenu = globalStore.getmenu;
Object.keys(mymenu).forEach((parentName: any) => {
// @ts-ignore
setParentVisibilityBasedOnRoute(mymenu[parentName])
})
myroutes.value = []
myroutes.value = static_data.routes
setParentVisibilityBasedOnRoute(mymenu[parentName]);
});
myroutes.value = [];
myroutes.value = static_data.routes;
globalStore.updateMenu = false;
}
watch(() => islogged.value, (to, from) => {
updatemenu()
})
watch(
() => islogged.value,
(to, from) => {
updatemenu();
}
);
watch(() => finishLoading.value, (to, from) => {
updatemenu()
})
watch(
() => finishLoading.value,
(to, from) => {
updatemenu();
}
);
watch(() => path, (to, from) => {
updatemenu()
})
watch(
() => updateMenu.value,
(newValue, oldValue) => {
if (newValue) updatemenu();
},
{ deep: true }
);
watch(
() => path,
(to, from) => {
updatemenu();
}
);
/* function replaceUnderlineToSpace(text: string) {
while (text.indexOf('_') !== -1) {
@@ -69,61 +90,56 @@ export default defineComponent({
} */
function getroute(elem: IListRoutes) {
let link = ''
let link = '';
if (elem.idelem) {
link = tools.getUrlByTipoProj(elem.urlroute ? elem.urlroute : '') + elem.idelem
link = tools.getUrlByTipoProj(elem.urlroute ? elem.urlroute : '') + elem.idelem;
}
if (!link)
link = elem.path
if (!link) link = elem.path;
// console.log('getroute LINK=', link)
return link
return link;
}
function getmymenuclass(elem: IListRoutes) {
let menu: string = clBase.value
let menu: string = clBase.value;
if (elem.color) {
menu += ` ${elem.color}`
menu += ` ${elem.color}`;
} else {
if (elem.onlyAdmin) menu += ' isAdmin'
if (elem.onlyManager) menu += ' isManager'
if (elem.onlySocioResidente) menu += ' isSocioResidente'
if (elem.onlyConsiglio) menu += ' isConsiglio'
if (elem.onlyDepartment) menu += ' isDepartment'
if (elem.onlyFacilitatore) menu += ' isFacilitatore'
if (elem.onlyEditor) menu += ' isEditor'
if (elem.onlyCommerciale) menu += ' isCommerciale'
if (elem.onlyCollaboratore) menu += ' isCollaboratore'
if (elem.onlyGrafico) menu += ' isGrafico'
if (elem.onlyAdmin) menu += ' isAdmin';
if (elem.onlyManager) menu += ' isManager';
if (elem.onlySocioResidente) menu += ' isSocioResidente';
if (elem.onlyConsiglio) menu += ' isConsiglio';
if (elem.onlyDepartment) menu += ' isDepartment';
if (elem.onlyFacilitatore) menu += ' isFacilitatore';
if (elem.onlyEditor) menu += ' isEditor';
if (elem.onlyCommerciale) menu += ' isCommerciale';
if (elem.onlyCollaboratore) menu += ' isCollaboratore';
if (elem.onlyGrafico) menu += ' isGrafico';
}
if (elem.extraclass) menu += ` ${elem.extraclass}`
if (elem.extraclass) menu += ` ${elem.extraclass}`;
// console.log('menu', menu)
return menu
return menu;
}
function getimgiconclass(elem: IListRoutes) {
if (elem.extraclass)
return elem.extraclass
else
return 'imgicon'
if (elem.extraclass) return elem.extraclass;
else return 'imgicon';
}
function getimgiconclass2(elem: IListRoutes) {
if (elem.extraclass)
return elem.extraclass
else
return 'clBase'
if (elem.extraclass) return elem.extraclass;
else return 'clBase';
}
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 {
getmenu,
@@ -138,6 +154,6 @@ export default defineComponent({
getimgiconclass2,
getmenuByPath,
clBase,
}
};
},
})
});

View File

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

View File

@@ -1,103 +1,153 @@
import { defineComponent, ref, watch, onMounted } from 'vue'
import { useQuasar } from 'quasar'
import { storeToRefs } from 'pinia'
import { IMyPage } from 'app/src/model'
import { useGlobalStore } from 'app/src/store'
import { PagesConfigurator } from '@src/components/PagesConfigurator'
import { useI18n } from 'vue-i18n'
import { defineComponent, ref, watch, onMounted } from 'vue';
import { useQuasar } from 'quasar';
import { storeToRefs } from 'pinia';
import { IMyPage } from 'app/src/model';
import { useGlobalStore } from 'app/src/store';
import { PagesConfigurator } from '@src/components/PagesConfigurator';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
export default defineComponent({
name: 'PagesAdmin',
components: { PagesConfigurator },
setup () {
const $q = useQuasar()
const { t } = useI18n()
setup() {
const $q = useQuasar();
const { t } = useI18n();
const globalStore = useGlobalStore()
const { mypage } = storeToRefs(globalStore)
const globalStore = useGlobalStore();
const { mypage } = storeToRefs(globalStore);
const $router = useRouter()
// Copia locale per lediting tramite v-model
const pagesLocal = ref<IMyPage[]>([])
const savingAll = ref(false)
const pagesLocal = ref<IMyPage[]>([]);
const savingAll = ref(false);
// Mantieni pagesLocal <-> store allineati
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 () => {
// Se hai una funzione per caricare tutte le pagine, chiamala qui
// es. await globalStore.loadAllPages()
syncFromStore()
})
syncFromStore();
});
// Salva singola pagina (chiamato da PagesConfigurator @save)
async function saveOne (page: IMyPage) {
const lastOrderSnapshot = ref<Map<string, number>>(new Map());
const savingOrders = ref(false);
// crea/ricostruisce lo snapshot (solo pagine con _id)
function initOrderSnapshot(arr: IMyPage[]) {
const m = new Map<string, number>();
for (const p of arr) {
if (p._id) m.set(p._id, p.order ?? 0);
}
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({ ...page })
if (saved && typeof saved === 'object') {
// rimpiazza nel local e nello store (lo store viene aggiornato da savePage)
const idx = pagesLocal.value.findIndex(p =>
(p._id && p._id === saved._id) || (p.path && p.path === saved.path)
)
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' })
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 (err) {
console.error(err)
$q.notify({ type: 'negative', message: 'Errore nel salvataggio della pagina' })
} catch (e) {
console.error(e);
$q.notify({ type: 'negative', message: 'Errore nel salvataggio della pagina' });
}
}
// 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 {
// Applica l'order nella copia locale
for (const { id, order } of orders) {
const p = pagesLocal.value.find(x => x._id === id) ||
pagesLocal.value.find(x => !id && typeof x.order === 'number' && x.order === order)
if (p) p.order = order
}
// Fallback: se non hai un endpoint /reorder, salvi singolarmente
await Promise.all(
pagesLocal.value.map(p => globalStore.savePage({ ...p }))
)
$q.notify({ type: 'positive', message: 'Ordinamento salvato' })
} catch (err) {
console.error(err)
$q.notify({ type: 'negative', message: 'Errore nel salvataggio dellordinamento' })
}
if (savingOrders.value) return;
savingOrders.value = true;
// 1) calcola solo i cambi VERI su pagine già persistite (_id presente)
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: salva solo le pagine cambiate
await Promise.all(
changedExisting.map(({ id, order }) => {
const p = pagesLocal.value.find((x) => x._id === id);
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) {
console.error(err);
$q.notify({
type: 'negative',
message: 'Errore nel salvataggio dellordinamento',
});
} finally {
savingOrders.value = false;
}
}
// Ricarica lelenco (se disponibile una API globale; altrimenti rilegge dallo store)
async function reloadAll () {
async function reloadAll() {
try {
// Se esiste: await globalStore.loadAllPages()
syncFromStore()
$q.notify({ type: 'info', message: 'Elenco ricaricato' })
syncFromStore();
$q.notify({ type: 'info', message: 'Elenco ricaricato' });
} catch (err) {
console.error(err)
$q.notify({ type: 'negative', message: 'Errore nel ricaricare lelenco' })
console.error(err);
$q.notify({ type: 'negative', message: 'Errore nel ricaricare lelenco' });
}
}
// Salva tutto (batch)
async function saveAll () {
async function saveAll() {
try {
savingAll.value = true
await Promise.all(
pagesLocal.value.map(p => globalStore.savePage({ ...p }))
)
$q.notify({ type: 'positive', message: 'Tutte le pagine salvate' })
savingAll.value = true;
await Promise.all(pagesLocal.value.map((p) => globalStore.savePage({ ...p })));
$q.notify({ type: 'positive', message: 'Tutte le pagine salvate' });
} catch (err) {
console.error(err)
$q.notify({ type: 'negative', message: 'Errore nel salvataggio massivo' })
console.error(err);
$q.notify({ type: 'negative', message: 'Errore nel salvataggio massivo' });
} finally {
savingAll.value = false
savingAll.value = false;
}
}
@@ -109,6 +159,6 @@ export default defineComponent({
saveAll,
savingAll,
t,
}
}
})
};
},
});

View File

@@ -1,7 +1,7 @@
<template>
<q-page class="q-pa-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-btn
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,
order: 10,
@@ -398,22 +382,6 @@ function getRoutesAd(site: ISites) {
onlyManager: true,
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,
order: 35,
@@ -471,6 +439,38 @@ function getRoutesAd(site: ISites) {
onlyAdmin: 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 = [

View File

@@ -70,6 +70,19 @@ function getRoutesEcomm(site: ISites) {
onlyManager: 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,
order: 30,

View File

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

View File

@@ -64,6 +64,7 @@ export const useGlobalStore = defineStore('GlobalStore', {
state: (): IGlobalState => ({
showHeader: true,
inStampa: false,
updateMenu: false,
finishLoading: false,
conta: 0,
wasAlreadySubscribed: false,
@@ -791,7 +792,7 @@ export const useGlobalStore = defineStore('GlobalStore', {
addDynamicPages($router: Router | null) {
// console.log('this.mypage', this.mypage)
/// console.log('addDynamicPages.........')
// console.log('addDynamicPages.........')
const arrpagesroute: IListRoutes[] = [];
for (const page of this.mypage) {
@@ -886,10 +887,6 @@ export const useGlobalStore = defineStore('GlobalStore', {
// Sort array
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) {
static_data.routes.forEach((route: any) => {
if (!$router.hasRoute(route.name) && !route.noroute) {
@@ -933,6 +930,7 @@ export const useGlobalStore = defineStore('GlobalStore', {
}
}
}
this.updateMenu = true;
},
async aggiornaMenu(router: Router) {
@@ -1446,7 +1444,7 @@ export const useGlobalStore = defineStore('GlobalStore', {
// console.log('mypage', mypage)
// 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;
}
@@ -1478,8 +1476,8 @@ export const useGlobalStore = defineStore('GlobalStore', {
return Api.SendReq('/savepage', 'POST', { page })
.then((res) => {
if (res && res.data && res.data.ris) {
return res.data.ris;
if (res && res.data && res.data.mypage) {
return res.data.mypage;
} else {
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 }) {
console.log('DeleteRec', table, id);

View File

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

5438
yarn.lock

File diff suppressed because it is too large Load Diff