Compare commits

..

30 commits
v1.7.1 ... main

Author SHA1 Message Date
a7149b2712 1.7.5 2025-07-24 08:57:54 +02:00
e54a6b91e0 Merge pull request 'patches v1.7.5' (#120) from develop into main
Reviewed-on: #120
2025-07-24 06:56:46 +00:00
4621c3e4e0 Merge branch 'main' into develop 2025-07-24 06:56:06 +00:00
9d3348b6d5 enhance: set username and routing on invite and setup 2025-07-24 08:51:36 +02:00
e10bfdd315 fix: navigation with restricted permissions 2025-07-24 08:49:18 +02:00
89bc0f9373 change: UI 2025-07-24 08:11:02 +02:00
d057512514 fix: allow admin user creation on setup with pw or totp 2025-07-24 07:53:31 +02:00
c4cc68fb78 package update and CVE close 2025-07-24 07:41:54 +02:00
167c26afe7 adapt changed version tag format 2025-07-22 08:59:53 +02:00
ff9beda069 1.7.4 2025-07-22 08:24:55 +02:00
720337c9c4 Merge pull request 'patches v1.7.4' (#118) from develop into main
Reviewed-on: #118
2025-07-22 06:24:04 +00:00
a2d1fdf97b Merge branch 'main' into develop 2025-07-22 06:23:50 +00:00
e223e1fd9f package update and CVE close 2025-07-22 08:23:10 +02:00
4d1ec2616d change: view style of new versions 2025-07-22 08:22:41 +02:00
63c9045d98 change: UI text and interaction 2025-07-22 07:50:38 +02:00
e755a4ec37 enhance: allow longpress and contextmenu copy paste 2025-07-22 07:50:26 +02:00
f94a1e5d2f 1.7.3 2025-07-19 11:09:17 +02:00
068111c6b9 Merge pull request 'patches v1.7.3' (#117) from develop into main
Reviewed-on: #117
2025-07-19 09:07:13 +00:00
518e05b842 Merge branch 'main' into develop 2025-07-19 09:07:03 +00:00
7252ae74ba package update and CVE close 2025-07-19 11:06:10 +02:00
ada4da7aa8 1.7.2 2025-07-18 16:17:00 +02:00
53fd89dd9e fix: return text with normal whitespace 2025-07-18 16:16:07 +02:00
563ae54c41 Merge pull request 'patches v1.7.2' (#115) from develop into main
Reviewed-on: #115
2025-07-18 14:15:54 +00:00
c49733ce13 Merge branch 'main' into develop 2025-07-18 13:55:08 +00:00
5005c33ec2 Merge pull request 'fix/vue-quill-xss_CVE-2021-3163' (#114) from fix/vue-quill-xss_CVE-2021-3163 into develop
Reviewed-on: #114
2025-07-18 13:53:50 +00:00
4265ed6791 enhance unmount and format 2025-07-18 15:52:54 +02:00
e6dfde058d rewirte in composition api to prevent this ref errors 2025-07-18 15:18:09 +02:00
08c3698dd8 update Editor usage 2025-07-17 13:57:50 +02:00
a2101db747 add QuillEditor Component 2025-07-17 12:56:33 +02:00
075c6cc7e4 change: improve pwa loading on new version 2025-07-14 10:27:27 +02:00
29 changed files with 653 additions and 386 deletions

503
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "ff-admin", "name": "ff-admin",
"version": "1.7.1", "version": "1.7.5",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ff-admin", "name": "ff-admin",
"version": "1.7.1", "version": "1.7.5",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"dependencies": { "dependencies": {
"@fullcalendar/core": "^6.1.18", "@fullcalendar/core": "^6.1.18",
@ -18,10 +18,10 @@
"@headlessui/vue": "^1.7.23", "@headlessui/vue": "^1.7.23",
"@heroicons/vue": "^2.2.0", "@heroicons/vue": "^2.2.0",
"@tailwindcss/vite": "^4.1.11", "@tailwindcss/vite": "^4.1.11",
"@vueup/vue-quill": "^1.2.0", "@vueuse/core": "^13.5.0",
"axios": "^1.10.0", "axios": "^1.11.0",
"event-source-polyfill": "^1.0.31", "event-source-polyfill": "^1.0.31",
"grapesjs": "^0.22.9", "grapesjs": "^0.22.11",
"grapesjs-preset-newsletter": "^1.0.2", "grapesjs-preset-newsletter": "^1.0.2",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
@ -38,10 +38,11 @@
"pwacompat": "^2.0.17", "pwacompat": "^2.0.17",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"qs": "^6.14.0", "qs": "^6.14.0",
"quill": "^2.0.3",
"socket.io-client": "^4.8.1", "socket.io-client": "^4.8.1",
"unplugin-vue-markdown": "^29.1.0", "unplugin-vue-markdown": "^29.1.0",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"vue": "^3.5.17", "vue": "^3.5.18",
"vue-router": "^4.5.1" "vue-router": "^4.5.1"
}, },
"devDependencies": { "devDependencies": {
@ -55,7 +56,7 @@
"@types/lodash.differencewith": "^4.5.9", "@types/lodash.differencewith": "^4.5.9",
"@types/lodash.isequal": "^4.5.8", "@types/lodash.isequal": "^4.5.8",
"@types/markdown-it": "^14.1.2", "@types/markdown-it": "^14.1.2",
"@types/node": "^24.0.13", "@types/node": "^24.1.0",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/qrcode": "^1.5.5", "@types/qrcode": "^1.5.5",
"@types/qs": "^6.14.0", "@types/qs": "^6.14.0",
@ -71,10 +72,10 @@
"prettier": "^3.6.2", "prettier": "^3.6.2",
"tailwindcss": "^4.1.11", "tailwindcss": "^4.1.11",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vite": "^7.0.4", "vite": "^7.0.6",
"vite-plugin-pwa": "^1.0.1", "vite-plugin-pwa": "^1.0.1",
"vite-plugin-vue-devtools": "^7.7.7", "vite-plugin-vue-devtools": "^7.7.7",
"vue-tsc": "^3.0.1" "vue-tsc": "^3.0.3"
} }
}, },
"node_modules/@alloc/quick-lru": { "node_modules/@alloc/quick-lru": {
@ -490,12 +491,12 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.27.5", "version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
"integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/types": "^7.27.3" "@babel/types": "^7.28.0"
}, },
"bin": { "bin": {
"parser": "bin/babel-parser.js" "parser": "bin/babel-parser.js"
@ -1759,9 +1760,9 @@
} }
}, },
"node_modules/@babel/types": { "node_modules/@babel/types": {
"version": "7.27.6", "version": "7.28.1",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz",
"integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-string-parser": "^7.27.1", "@babel/helper-string-parser": "^7.27.1",
@ -2267,9 +2268,9 @@
} }
}, },
"node_modules/@eslint/core": { "node_modules/@eslint/core": {
"version": "0.14.0", "version": "0.15.1",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
"integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@ -2351,13 +2352,13 @@
} }
}, },
"node_modules/@eslint/plugin-kit": { "node_modules/@eslint/plugin-kit": {
"version": "0.3.1", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz",
"integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/core": "^0.14.0", "@eslint/core": "^0.15.1",
"levn": "^0.4.1" "levn": "^0.4.1"
}, },
"engines": { "engines": {
@ -2892,29 +2893,6 @@
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
}, },
"node_modules/@isaacs/balanced-match": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@isaacs/brace-expansion": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@isaacs/balanced-match": "^4.0.1"
},
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@isaacs/fs-minipass": { "node_modules/@isaacs/fs-minipass": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
@ -3924,9 +3902,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "24.0.13", "version": "24.1.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz",
"integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==", "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -3990,6 +3968,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/web-bluetooth": {
"version": "0.0.21",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
"integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.36.0", "version": "8.36.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz",
@ -4276,30 +4260,30 @@
} }
}, },
"node_modules/@volar/language-core": { "node_modules/@volar/language-core": {
"version": "2.4.17", "version": "2.4.20",
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.17.tgz", "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.20.tgz",
"integrity": "sha512-chmRZMbKmcGpKMoO7Reb70uiLrzo0KWC2CkFttKUuKvrE+VYgi+fL9vWMJ07Fv5ulX0V1TAyyacN9q3nc5/ecA==", "integrity": "sha512-dRDF1G33xaAIDqR6+mXUIjXYdu9vzSxlMGfMEwBxQsfY/JMUEXSpLTR057oTKlUQ2nIvCmP9k94A8h8z2VrNSA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@volar/source-map": "2.4.17" "@volar/source-map": "2.4.20"
} }
}, },
"node_modules/@volar/source-map": { "node_modules/@volar/source-map": {
"version": "2.4.17", "version": "2.4.20",
"resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.17.tgz", "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.20.tgz",
"integrity": "sha512-QDybtQyO3Ms/NjFqNHTC5tbDN2oK5VH7ZaKrcubtfHBDj63n2pizHC3wlMQ+iT55kQXZUUAbmBX5L1C8CHFeBw==", "integrity": "sha512-mVjmFQH8mC+nUaVwmbxoYUy8cww+abaO8dWzqPUjilsavjxH0jCJ3Mp8HFuHsdewZs2c+SP+EO7hCd8Z92whJg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@volar/typescript": { "node_modules/@volar/typescript": {
"version": "2.4.17", "version": "2.4.20",
"resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.17.tgz", "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.20.tgz",
"integrity": "sha512-3paEFNh4P5DkgNUB2YkTRrfUekN4brAXxd3Ow1syMqdIPtCZHbUy4AW99S5RO/7mzyTWPMdDSo3mqTpB/LPObQ==", "integrity": "sha512-Oc4DczPwQyXcVbd+5RsNEqX6ia0+w3p+klwdZQ6ZKhFjWoBP9PCPQYlKYRi/tDemWphW93P/Vv13vcE9I9D2GQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@volar/language-core": "2.4.17", "@volar/language-core": "2.4.20",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"vscode-uri": "^3.0.8" "vscode-uri": "^3.0.8"
} }
@ -4358,39 +4342,39 @@
} }
}, },
"node_modules/@vue/compiler-core": { "node_modules/@vue/compiler-core": {
"version": "3.5.17", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz",
"integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==", "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.27.5", "@babel/parser": "^7.28.0",
"@vue/shared": "3.5.17", "@vue/shared": "3.5.18",
"entities": "^4.5.0", "entities": "^4.5.0",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
} }
}, },
"node_modules/@vue/compiler-dom": { "node_modules/@vue/compiler-dom": {
"version": "3.5.17", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz",
"integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==", "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-core": "3.5.17", "@vue/compiler-core": "3.5.18",
"@vue/shared": "3.5.17" "@vue/shared": "3.5.18"
} }
}, },
"node_modules/@vue/compiler-sfc": { "node_modules/@vue/compiler-sfc": {
"version": "3.5.17", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz",
"integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==", "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.27.5", "@babel/parser": "^7.28.0",
"@vue/compiler-core": "3.5.17", "@vue/compiler-core": "3.5.18",
"@vue/compiler-dom": "3.5.17", "@vue/compiler-dom": "3.5.18",
"@vue/compiler-ssr": "3.5.17", "@vue/compiler-ssr": "3.5.18",
"@vue/shared": "3.5.17", "@vue/shared": "3.5.18",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.30.17", "magic-string": "^0.30.17",
"postcss": "^8.5.6", "postcss": "^8.5.6",
@ -4398,13 +4382,13 @@
} }
}, },
"node_modules/@vue/compiler-ssr": { "node_modules/@vue/compiler-ssr": {
"version": "3.5.17", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz",
"integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==", "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.17", "@vue/compiler-dom": "3.5.18",
"@vue/shared": "3.5.17" "@vue/shared": "3.5.18"
} }
}, },
"node_modules/@vue/compiler-vue2": { "node_modules/@vue/compiler-vue2": {
@ -4530,20 +4514,20 @@
} }
}, },
"node_modules/@vue/language-core": { "node_modules/@vue/language-core": {
"version": "3.0.1", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.0.3.tgz",
"integrity": "sha512-sq+/Mc1IqIexWEQ+Q2XPiDb5SxSvY5JPqHnMOl/PlF5BekslzduX8dglSkpC17VeiAQB6dpS+4aiwNLJRduCNw==", "integrity": "sha512-I9wY0ULMN9tMSua+2C7g+ez1cIziVMUzIHlDYGSl2rtru3Eh4sXj95vZ+4GBuXwwPnEmYfzSApVbXiVbI8V5Gg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@volar/language-core": "2.4.17", "@volar/language-core": "2.4.20",
"@vue/compiler-dom": "^3.5.0", "@vue/compiler-dom": "^3.5.0",
"@vue/compiler-vue2": "^2.7.16", "@vue/compiler-vue2": "^2.7.16",
"@vue/shared": "^3.5.0", "@vue/shared": "^3.5.0",
"alien-signals": "^2.0.5", "alien-signals": "^2.0.5",
"minimatch": "^10.0.1",
"muggle-string": "^0.4.1", "muggle-string": "^0.4.1",
"path-browserify": "^1.0.1" "path-browserify": "^1.0.1",
"picomatch": "^4.0.2"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "*" "typescript": "*"
@ -4554,70 +4538,67 @@
} }
} }
}, },
"node_modules/@vue/language-core/node_modules/minimatch": { "node_modules/@vue/language-core/node_modules/picomatch": {
"version": "10.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true, "dev": true,
"license": "ISC", "license": "MIT",
"dependencies": {
"@isaacs/brace-expansion": "^5.0.0"
},
"engines": { "engines": {
"node": "20 || >=22" "node": ">=12"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/@vue/reactivity": { "node_modules/@vue/reactivity": {
"version": "3.5.17", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.17.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz",
"integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==", "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/shared": "3.5.17" "@vue/shared": "3.5.18"
} }
}, },
"node_modules/@vue/runtime-core": { "node_modules/@vue/runtime-core": {
"version": "3.5.17", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.17.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz",
"integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==", "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/reactivity": "3.5.17", "@vue/reactivity": "3.5.18",
"@vue/shared": "3.5.17" "@vue/shared": "3.5.18"
} }
}, },
"node_modules/@vue/runtime-dom": { "node_modules/@vue/runtime-dom": {
"version": "3.5.17", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz",
"integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==", "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/reactivity": "3.5.17", "@vue/reactivity": "3.5.18",
"@vue/runtime-core": "3.5.17", "@vue/runtime-core": "3.5.18",
"@vue/shared": "3.5.17", "@vue/shared": "3.5.18",
"csstype": "^3.1.3" "csstype": "^3.1.3"
} }
}, },
"node_modules/@vue/server-renderer": { "node_modules/@vue/server-renderer": {
"version": "3.5.17", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.17.tgz", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz",
"integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==", "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-ssr": "3.5.17", "@vue/compiler-ssr": "3.5.18",
"@vue/shared": "3.5.17" "@vue/shared": "3.5.18"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "3.5.17" "vue": "3.5.18"
} }
}, },
"node_modules/@vue/shared": { "node_modules/@vue/shared": {
"version": "3.5.17", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.17.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz",
"integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==", "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@vue/tsconfig": { "node_modules/@vue/tsconfig": {
@ -4639,17 +4620,42 @@
} }
} }
}, },
"node_modules/@vueup/vue-quill": { "node_modules/@vueuse/core": {
"version": "1.2.0", "version": "13.5.0",
"resolved": "https://registry.npmjs.org/@vueup/vue-quill/-/vue-quill-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.5.0.tgz",
"integrity": "sha512-kd5QPSHMDpycklojPXno2Kw2JSiKMYduKYQckTm1RJoVDA557MnyUXgcuuDpry4HY/Rny9nGNcK+m3AHk94wag==", "integrity": "sha512-wV7z0eUpifKmvmN78UBZX8T7lMW53Nrk6JP5+6hbzrB9+cJ3jr//hUlhl9TZO/03bUkMK6gGkQpqOPWoabr72g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"quill": "^1.3.7", "@types/web-bluetooth": "^0.0.21",
"quill-delta": "^4.2.2" "@vueuse/metadata": "13.5.0",
"@vueuse/shared": "13.5.0"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^3.2.41" "vue": "^3.5.0"
}
},
"node_modules/@vueuse/metadata": {
"version": "13.5.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.5.0.tgz",
"integrity": "sha512-euhItU3b0SqXxSy8u1XHxUCdQ8M++bsRs+TYhOLDU/OykS7KvJnyIFfep0XM5WjIFry9uAPlVSjmVHiqeshmkw==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared": {
"version": "13.5.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.5.0.tgz",
"integrity": "sha512-K7GrQIxJ/ANtucxIXbQlUHdB0TPA8c+q5i+zbrjxuhJCnJ9GtBg75sBSnvmLSxHKPg2Yo8w62PWksl9kwH0Q8g==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"vue": "^3.5.0"
} }
}, },
"node_modules/acorn": { "node_modules/acorn": {
@ -4817,13 +4823,13 @@
} }
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.10.0", "version": "1.11.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
"integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.6", "follow-redirects": "^1.15.6",
"form-data": "^4.0.0", "form-data": "^4.0.4",
"proxy-from-env": "^1.1.0" "proxy-from-env": "^1.1.0"
} }
}, },
@ -5015,6 +5021,7 @@
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"call-bind-apply-helpers": "^1.0.0", "call-bind-apply-helpers": "^1.0.0",
@ -5135,15 +5142,6 @@
"wrap-ansi": "^6.2.0" "wrap-ansi": "^6.2.0"
} }
}, },
"node_modules/clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
"license": "MIT",
"engines": {
"node": ">=0.8"
}
},
"node_modules/codemirror": { "node_modules/codemirror": {
"version": "5.63.0", "version": "5.63.0",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.63.0.tgz", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.63.0.tgz",
@ -5449,26 +5447,6 @@
"node": ">=8.6" "node": ">=8.6"
} }
}, },
"node_modules/deep-equal": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz",
"integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==",
"license": "MIT",
"dependencies": {
"is-arguments": "^1.1.1",
"is-date-object": "^1.0.5",
"is-regex": "^1.1.4",
"object-is": "^1.1.5",
"object-keys": "^1.1.1",
"regexp.prototype.flags": "^1.5.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/deep-is": { "node_modules/deep-is": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@ -5520,6 +5498,7 @@
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"es-define-property": "^1.0.0", "es-define-property": "^1.0.0",
@ -5550,6 +5529,7 @@
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"define-data-property": "^1.0.1", "define-data-property": "^1.0.1",
@ -6066,19 +6046,6 @@
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
} }
}, },
"node_modules/eslint/node_modules/@eslint/core": {
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
"integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/eslint/node_modules/brace-expansion": { "node_modules/eslint/node_modules/brace-expansion": {
"version": "1.1.12", "version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@ -6219,9 +6186,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/eventemitter3": { "node_modules/eventemitter3": {
"version": "2.0.3", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==", "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/execa": { "node_modules/execa": {
@ -6251,12 +6218,6 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1" "url": "https://github.com/sindresorhus/execa?sponsor=1"
} }
}, },
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"license": "MIT"
},
"node_modules/extend-shallow": { "node_modules/extend-shallow": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
@ -6280,7 +6241,6 @@
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"dev": true,
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/fast-glob": { "node_modules/fast-glob": {
@ -6494,14 +6454,15 @@
} }
}, },
"node_modules/form-data": { "node_modules/form-data": {
"version": "4.0.2", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "^1.0.8", "combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0", "es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
}, },
"engines": { "engines": {
@ -6599,6 +6560,7 @@
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
@ -6810,9 +6772,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/grapesjs": { "node_modules/grapesjs": {
"version": "0.22.9", "version": "0.22.11",
"resolved": "https://registry.npmjs.org/grapesjs/-/grapesjs-0.22.9.tgz", "resolved": "https://registry.npmjs.org/grapesjs/-/grapesjs-0.22.11.tgz",
"integrity": "sha512-ni9b/M1ydkq3ifkOVRWf1iL8JcGmwpQqX3zdrTvOg1KCxYX0dLX5bg5EpQmS/bewHW2r+8c47SdoNCbUiVOZKg==", "integrity": "sha512-zGG13MEPDwbnijjJ56GZ9mCWIPbcZ53OXyC6fIDiipJC3/ZnsRSDMIivzltoVOn61cv64tcHuyrcltCuVeMi+A==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"@types/backbone": "1.4.15", "@types/backbone": "1.4.15",
@ -6902,6 +6864,7 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"es-define-property": "^1.0.0" "es-define-property": "^1.0.0"
@ -7091,22 +7054,6 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/is-arguments": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
"integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"has-tostringtag": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-array-buffer": { "node_modules/is-array-buffer": {
"version": "3.0.5", "version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@ -7236,6 +7183,7 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
"integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"call-bound": "^1.0.2", "call-bound": "^1.0.2",
@ -7433,6 +7381,7 @@
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
"integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"call-bound": "^1.0.2", "call-bound": "^1.0.2",
@ -8129,6 +8078,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT"
},
"node_modules/lodash.clonedeep": { "node_modules/lodash.clonedeep": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
@ -8557,26 +8512,11 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/object-is": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
"integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.7",
"define-properties": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/object-keys": { "node_modules/object-keys": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@ -8710,9 +8650,9 @@
} }
}, },
"node_modules/parchment": { "node_modules/parchment": {
"version": "1.1.4", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz",
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==", "integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==",
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/parent-module": { "node_modules/parent-module": {
@ -9111,54 +9051,32 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/quill": { "node_modules/quill": {
"version": "1.3.7", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", "resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
"integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", "integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"clone": "^2.1.1", "eventemitter3": "^5.0.1",
"deep-equal": "^1.0.1", "lodash-es": "^4.17.21",
"eventemitter3": "^2.0.3", "parchment": "^3.0.0",
"extend": "^3.0.2", "quill-delta": "^5.1.0"
"parchment": "^1.1.4",
"quill-delta": "^3.6.2"
}
},
"node_modules/quill-delta": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-4.2.2.tgz",
"integrity": "sha512-qjbn82b/yJzOjstBgkhtBjN2TNK+ZHP/BgUQO+j6bRhWQQdmj2lH6hXG7+nwwLF41Xgn//7/83lxs9n2BkTtTg==",
"license": "MIT",
"dependencies": {
"fast-diff": "1.2.0",
"lodash.clonedeep": "^4.5.0",
"lodash.isequal": "^4.5.0"
}
},
"node_modules/quill-delta/node_modules/fast-diff": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
"license": "Apache-2.0"
},
"node_modules/quill/node_modules/fast-diff": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==",
"license": "Apache-2.0"
},
"node_modules/quill/node_modules/quill-delta": {
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
"license": "MIT",
"dependencies": {
"deep-equal": "^1.0.1",
"extend": "^3.0.2",
"fast-diff": "1.1.2"
}, },
"engines": { "engines": {
"node": ">=0.10" "npm": ">=8.2.3"
}
},
"node_modules/quill/node_modules/quill-delta": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
"integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
"license": "MIT",
"dependencies": {
"fast-diff": "^1.3.0",
"lodash.clonedeep": "^4.5.0",
"lodash.isequal": "^4.5.0"
},
"engines": {
"node": ">= 12.0.0"
} }
}, },
"node_modules/randombytes": { "node_modules/randombytes": {
@ -9232,6 +9150,7 @@
"version": "1.5.4", "version": "1.5.4",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
"integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.8", "call-bind": "^1.0.8",
@ -9570,6 +9489,7 @@
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"define-data-property": "^1.1.4", "define-data-property": "^1.1.4",
@ -9587,6 +9507,7 @@
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
"integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"define-data-property": "^1.1.4", "define-data-property": "^1.1.4",
@ -10805,14 +10726,14 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "7.0.4", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz",
"integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"fdir": "^6.4.6", "fdir": "^6.4.6",
"picomatch": "^4.0.2", "picomatch": "^4.0.3",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"rollup": "^4.40.0", "rollup": "^4.40.0",
"tinyglobby": "^0.2.14" "tinyglobby": "^0.2.14"
@ -11012,9 +10933,9 @@
} }
}, },
"node_modules/vite/node_modules/picomatch": { "node_modules/vite/node_modules/picomatch": {
"version": "4.0.2", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@ -11031,16 +10952,16 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/vue": { "node_modules/vue": {
"version": "3.5.17", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.17.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz",
"integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==", "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.17", "@vue/compiler-dom": "3.5.18",
"@vue/compiler-sfc": "3.5.17", "@vue/compiler-sfc": "3.5.18",
"@vue/runtime-dom": "3.5.17", "@vue/runtime-dom": "3.5.18",
"@vue/server-renderer": "3.5.17", "@vue/server-renderer": "3.5.18",
"@vue/shared": "3.5.17" "@vue/shared": "3.5.18"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "*" "typescript": "*"
@ -11110,14 +11031,14 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/vue-tsc": { "node_modules/vue-tsc": {
"version": "3.0.1", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.0.1.tgz", "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.0.3.tgz",
"integrity": "sha512-UvMLQD0hAGL1g/NfEQelnSVB4H5gtf/gz2lJKjMMwWNOUmSNyWkejwJagAxEbSjtV5CPPJYslOtoSuqJ63mhdg==", "integrity": "sha512-uU1OMSzWE8/y0+kDTc0iEIu9v82bmFkGyJpAO/x3wQqBkkHkButKgtygREyOkxL4E/xtcf/ExvgNhhjdzonldw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@volar/typescript": "2.4.17", "@volar/typescript": "2.4.20",
"@vue/language-core": "3.0.1" "@vue/language-core": "3.0.3"
}, },
"bin": { "bin": {
"vue-tsc": "bin/vue-tsc.js" "vue-tsc": "bin/vue-tsc.js"

View file

@ -1,10 +1,10 @@
{ {
"name": "ff-admin", "name": "ff-admin",
"version": "1.7.1", "version": "1.7.5",
"description": "Feuerwehr/Verein Mitgliederverwaltung UI", "description": "Feuerwehr/Verein Mitgliederverwaltung UI",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite --host",
"build": "run-p type-check \"build-only {@}\" --", "build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview", "preview": "vite preview",
"build-only": "vite build", "build-only": "vite build",
@ -33,10 +33,10 @@
"@headlessui/vue": "^1.7.23", "@headlessui/vue": "^1.7.23",
"@heroicons/vue": "^2.2.0", "@heroicons/vue": "^2.2.0",
"@tailwindcss/vite": "^4.1.11", "@tailwindcss/vite": "^4.1.11",
"@vueup/vue-quill": "^1.2.0", "@vueuse/core": "^13.5.0",
"axios": "^1.10.0", "axios": "^1.11.0",
"event-source-polyfill": "^1.0.31", "event-source-polyfill": "^1.0.31",
"grapesjs": "^0.22.9", "grapesjs": "^0.22.11",
"grapesjs-preset-newsletter": "^1.0.2", "grapesjs-preset-newsletter": "^1.0.2",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
@ -53,10 +53,11 @@
"pwacompat": "^2.0.17", "pwacompat": "^2.0.17",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"qs": "^6.14.0", "qs": "^6.14.0",
"quill": "^2.0.3",
"socket.io-client": "^4.8.1", "socket.io-client": "^4.8.1",
"unplugin-vue-markdown": "^29.1.0", "unplugin-vue-markdown": "^29.1.0",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"vue": "^3.5.17", "vue": "^3.5.18",
"vue-router": "^4.5.1" "vue-router": "^4.5.1"
}, },
"devDependencies": { "devDependencies": {
@ -70,7 +71,7 @@
"@types/lodash.differencewith": "^4.5.9", "@types/lodash.differencewith": "^4.5.9",
"@types/lodash.isequal": "^4.5.8", "@types/lodash.isequal": "^4.5.8",
"@types/markdown-it": "^14.1.2", "@types/markdown-it": "^14.1.2",
"@types/node": "^24.0.13", "@types/node": "^24.1.0",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/qrcode": "^1.5.5", "@types/qrcode": "^1.5.5",
"@types/qs": "^6.14.0", "@types/qs": "^6.14.0",
@ -86,9 +87,9 @@
"prettier": "^3.6.2", "prettier": "^3.6.2",
"tailwindcss": "^4.1.11", "tailwindcss": "^4.1.11",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vite": "^7.0.4", "vite": "^7.0.6",
"vite-plugin-pwa": "^1.0.1", "vite-plugin-pwa": "^1.0.1",
"vite-plugin-vue-devtools": "^7.7.7", "vite-plugin-vue-devtools": "^7.7.7",
"vue-tsc": "^3.0.1" "vue-tsc": "^3.0.3"
} }
} }

View file

@ -2,11 +2,11 @@
<Modal /> <Modal />
<ContextMenu /> <ContextMenu />
<Header @contextmenu.prevent /> <AppHeader />
<div class="grow overflow-x-hidden overflow-y-auto p-2 md:p-4" @contextmenu.prevent> <div class="grow overflow-x-hidden overflow-y-auto p-2 md:p-4">
<RouterView /> <RouterView />
</div> </div>
<Footer @contextmenu.prevent /> <AppFooter />
<Notification /> <Notification />
<Teleport to="head"> <Teleport to="head">
@ -18,10 +18,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineComponent } from "vue"; import { defineAsyncComponent, defineComponent, markRaw } from "vue";
import { onLongPress } from "@vueuse/core";
import { RouterView } from "vue-router"; import { RouterView } from "vue-router";
import Header from "./components/Header.vue"; import AppHeader from "./components/Header.vue";
import Footer from "./components/Footer.vue"; import AppFooter from "./components/Footer.vue";
import { mapActions, mapState } from "pinia"; import { mapActions, mapState } from "pinia";
import { useAuthStore } from "./stores/auth"; import { useAuthStore } from "./stores/auth";
import { isAuthenticatedPromise } from "./router/authGuard"; import { isAuthenticatedPromise } from "./router/authGuard";
@ -31,6 +32,7 @@ import Notification from "./components/Notification.vue";
import { config } from "./config"; import { config } from "./config";
import { useConfigurationStore } from "@/stores/configuration"; import { useConfigurationStore } from "@/stores/configuration";
import { resetAllPiniaStores } from "@/helpers/piniaReset"; import { resetAllPiniaStores } from "@/helpers/piniaReset";
import { useContextMenuStore } from "./stores/context-menu";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -40,6 +42,11 @@ export default defineComponent({
...mapState(useConfigurationStore, ["clubName"]), ...mapState(useConfigurationStore, ["clubName"]),
}, },
mounted() { mounted() {
document.body.addEventListener("contextmenu", (event) => {
this.handleContextMenu(event);
});
onLongPress(document.body, this.handleContextMenu);
resetAllPiniaStores(); resetAllPiniaStores();
this.configure(); this.configure();
@ -52,6 +59,21 @@ export default defineComponent({
}, },
methods: { methods: {
...mapActions(useConfigurationStore, ["configure"]), ...mapActions(useConfigurationStore, ["configure"]),
...mapActions(useContextMenuStore, ["openContextMenu"]),
handleContextMenu(e: MouseEvent) {
e.preventDefault();
// TODO allow contextmenu on elements with special attribute with reduced selection
const target = e.target as HTMLElement | null;
if (!target) return;
if (["INPUT", "TEXTAREA", "P", "H1", "H2", "H3", "H4"].includes((target as HTMLElement).nodeName)) {
this.openContextMenu(e, {
component_ref: markRaw(defineAsyncComponent(() => import("@/components/CopyPasteContextMenu.vue"))),
data: ["INPUT", "TEXTAREA"].includes((e.target as HTMLElement).nodeName) ? "" : "nopaste",
});
}
},
}, },
}); });
</script> </script>

View file

@ -1,10 +1,9 @@
<template> <template>
<div <div
ref="contextMenu" ref="contextMenu"
class="absolute flex flex-col gap-1 border border-gray-400 bg-white rounded-md select-none text-left shadow-md z-50 p-1" class="absolute flex flex-col gap-1 border border-gray-400 bg-white rounded-md select-none text-left shadow-md z-[100] p-1"
v-show="show" v-show="show"
:style="contextMenuStyle" :style="contextMenuStyle"
@contextmenu.prevent
@click="closeContextMenu" @click="closeContextMenu"
> >
<component :is="component_ref" :data="data" /> <component :is="component_ref" :data="data" />

View file

@ -0,0 +1,61 @@
<template>
<div class="flex flex-row gap-2 cursor-pointer hover:bg-gray-300 p-1 rounded-md" @click="copy">
<DocumentDuplicateIcon class="w-5 h-5" />
<p>kopieren</p>
</div>
<div
v-if="data != 'nopaste'"
class="flex flex-row gap-2 cursor-pointer hover:bg-gray-300 p-1 rounded-md"
@click="paste"
>
<ClipboardDocumentIcon class="w-5 h-5" />
<p>einfügen</p>
</div>
</template>
<script setup lang="ts">
import { defineComponent } from "vue";
import { ClipboardDocumentIcon, DocumentDuplicateIcon } from "@heroicons/vue/24/outline";
import { mapState } from "pinia";
import { useContextMenuStore } from "@/stores/context-menu";
</script>
<script lang="ts">
export default defineComponent({
props: ["data"],
data() {
return {
selectedText: "",
};
},
computed: {
...mapState(useContextMenuStore, ["clickedOnEl"]),
},
mounted() {
this.selectedText =
document.getSelection()?.toString() || this.clickedOnEl.value || this.clickedOnEl.innerText || "";
let selection = document.getSelection()?.toString();
console.log(selection);
if (selection == "") {
console.log("jo");
const range = document.createRange();
range.selectNode(this.clickedOnEl);
console.log(range);
window.getSelection()?.removeAllRanges();
window.getSelection()?.addRange(range);
}
},
methods: {
copy() {
navigator.clipboard.writeText(this.selectedText);
},
paste() {
const el = this.clickedOnEl;
navigator.clipboard.readText().then((e) => {
el.value = e;
});
},
},
});
</script>

View file

@ -6,7 +6,7 @@
<div class="w-full flex flex-row gap-2 h-full align-middle"> <div class="w-full flex flex-row gap-2 h-full align-middle">
<TopLevelLink <TopLevelLink
v-if="routeName == 'admin' || routeName.includes('admin-')" v-if="routeName == 'admin' || routeName.includes('admin-')"
v-for="item in topLevel" v-for="item in topLevelObject"
:key="item.key" :key="item.key"
:link="item" :link="item"
:disableSubLink="true" :disableSubLink="true"
@ -34,7 +34,7 @@ import TopLevelLink from "./admin/TopLevelLink.vue";
export default defineComponent({ export default defineComponent({
computed: { computed: {
...mapState(useAuthStore, ["authCheck"]), ...mapState(useAuthStore, ["authCheck"]),
...mapState(useNavigationStore, ["topLevel"]), ...mapState(useNavigationStore, ["topLevelObject"]),
routeName() { routeName() {
return typeof this.$route.name == "string" ? this.$route.name : ""; return typeof this.$route.name == "string" ? this.$route.name : "";
}, },

View file

@ -10,7 +10,7 @@
<div v-if="authCheck" class="hidden md:flex flex-row gap-2 h-full align-middle"> <div v-if="authCheck" class="hidden md:flex flex-row gap-2 h-full align-middle">
<TopLevelLink <TopLevelLink
v-if="routeName == 'admin' || routeName.includes('admin-')" v-if="routeName == 'admin' || routeName.includes('admin-')"
v-for="item in topLevel" v-for="item in topLevelObject"
:key="item.key" :key="item.key"
:link="item" :link="item"
/> />
@ -46,7 +46,7 @@ import { useConfigurationStore } from "@/stores/configuration";
export default defineComponent({ export default defineComponent({
computed: { computed: {
...mapState(useAuthStore, ["authCheck"]), ...mapState(useAuthStore, ["authCheck"]),
...mapState(useNavigationStore, ["topLevel"]), ...mapState(useNavigationStore, ["topLevelObject"]),
...mapState(useConfigurationStore, ["clubName"]), ...mapState(useConfigurationStore, ["clubName"]),
routeName() { routeName() {
return typeof this.$route.name == "string" ? this.$route.name : ""; return typeof this.$route.name == "string" ? this.$route.name : "";

View file

@ -3,21 +3,15 @@
ref="contextMenu" ref="contextMenu"
class="absolute inset-0 w-full h-full flex justify-center items-center bg-black/50 select-none z-50 p-2" class="absolute inset-0 w-full h-full flex justify-center items-center bg-black/50 select-none z-50 p-2"
v-show="show" v-show="show"
@contextmenu.prevent
> >
<!-- @click="closeModal" --> <component :is="component_ref" :data="data" class="p-4 bg-white rounded-lg max-h-[95%] overflow-y-auto" />
<component
:is="component_ref"
:data="data"
@click.stop
class="p-4 bg-white rounded-lg max-h-[95%] overflow-y-auto"
/>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { mapState, mapActions } from "pinia"; import { mapState, mapActions } from "pinia";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useContextMenuStore } from "@/stores/context-menu";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -27,6 +21,7 @@ export default {
}, },
methods: { methods: {
...mapActions(useModalStore, ["closeModal"]), ...mapActions(useModalStore, ["closeModal"]),
...mapActions(useContextMenuStore, ["closeContextMenu"]),
}, },
}; };
</script> </script>

View file

@ -0,0 +1,151 @@
<template>
<div :id="id" :style="style" class="flex flex-col">
<div ref="quill"></div>
</div>
</template>
<script setup lang="ts">
import { markRaw, onMounted, onUnmounted, ref, useTemplateRef, watch, type PropType } from "vue";
import Quill, { Delta, type QuillOptions } from "quill";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import isEqual from "lodash.isequal";
import cloneDeep from "lodash.clonedeep";
type RangeStatic = { index: number; length: number };
const quillElement = useTemplateRef("quill");
const instance = ref<undefined | Quill>();
const model = ref<string | Delta>("");
const props = defineProps({
id: String,
style: [String, Object],
options: {
type: Object as PropType<QuillOptions>,
default: {},
},
toolbar: {
type: [String, Array, Object],
default: "",
},
content: {
type: [String, Object] as PropType<string | Delta | null>,
default: "",
},
contentType: {
type: String as PropType<"delta" | "html" | "text">,
default: "text",
validator: (value: string) => {
return ["delta", "html", "text"].includes(value);
},
},
readonly: { type: Boolean, default: false },
placeholder: String,
});
const emit = defineEmits({
"update:content": (content: Delta | string) => true,
textChange: (tc: { delta: Delta; oldDelta: Delta; source: string }) => true,
selectionChange: (sc: { range: RangeStatic; oldRange: RangeStatic; source: string }) => true,
focus: () => true,
blur: () => true,
ready: (quill: Quill) => true,
});
watch(
() => props.content,
(val, oldVal) => {
if (!instance.value || !val || isEqual(val, model.value)) return;
setContent(cloneDeep(val));
},
{ deep: true }
);
watch(
() => props.readonly,
(val, oldVal) => {
instance.value?.enable(val);
}
);
watch(model, (val, oldVal) => {
emit("update:content", cloneDeep(val));
});
onMounted(() => {
if (!quillElement.value) return;
const quill = new Quill(quillElement.value, {
theme: "snow",
modules: {
toolbar: props.toolbar,
},
placeholder: props.placeholder,
readOnly: props.readonly,
...props.options,
});
quill.on(Quill.events.SELECTION_CHANGE, (range, oldRange, source) => {
if (!range) {
emit("blur");
} else {
emit("focus");
}
emit("selectionChange", { range, oldRange, source });
});
quill.on(Quill.events.TEXT_CHANGE, (delta, oldDelta, source) => {
model.value = getContent();
emit("textChange", { delta, oldDelta, source });
});
emit("ready", quill);
instance.value = markRaw(quill);
setContent(cloneDeep(props.content ?? ""));
});
onUnmounted(() => {
if (instance.value) {
instance.value.off(Quill.events.SELECTION_CHANGE);
instance.value.off(Quill.events.TEXT_CHANGE);
instance.value = undefined;
}
});
function getContent(): string | Delta {
if (props.contentType === "delta") {
return (instance.value?.getContents() ?? {}) as Delta;
} else if (props.contentType === "html") {
return instance.value?.getSemanticHTML().replace(/&nbsp;/g, " ") ?? "";
} else {
return instance.value?.getText().replace(/&nbsp;/g, " ") ?? "";
}
}
function setContent(content: Delta | string) {
if (content == "") return;
if (props.contentType === "delta") {
if (typeof content !== "string") {
instance.value?.setContents(content);
}
} else if (props.contentType === "html") {
if (typeof content === "string" && instance.value) {
instance.value.clipboard.dangerouslyPasteHTML(content);
let delta = instance.value.clipboard.convert({ html: content });
instance.value?.setContents(delta);
}
} else {
if (typeof content === "string") {
instance.value?.setText(content);
}
}
}
</script>
<style>
.ql-container {
border-bottom-left-radius: 0.5em;
border-bottom-right-radius: 0.5em;
}
.ql-toolbar {
background: var(--color-gray-100);
border-top-left-radius: 0.5em;
border-top-right-radius: 0.5em;
}
</style>

View file

@ -1,6 +1,6 @@
<template> <template>
<div class="flex relative"> <div class="flex relative">
<input type="text" :value="copyText" /> <input type="text" readonly :value="copyText" />
<ClipboardIcon <ClipboardIcon
class="w-5 h-5 p-2 box-content absolute right-1 top-1/2 -translate-y-1/2 bg-white cursor-pointer" class="w-5 h-5 p-2 box-content absolute right-1 top-1/2 -translate-y-1/2 bg-white cursor-pointer"
@click="copyToClipboard" @click="copyToClipboard"

View file

@ -0,0 +1,52 @@
<template>
<div class="flex flex-col gap-1">
<p>
<span class="font-semibold text-lg">{{ version.title }}</span> vom
{{
new Date(version.isoDate).toLocaleDateString("de", {
month: "2-digit",
day: "2-digit",
year: "numeric",
})
}}
</p>
<div class="versionDisplay flex flex-col" v-html="version['content:encoded']"></div>
</div>
</template>
<script setup lang="ts">
import type { Release } from "@/viewmodels/version.models";
import { defineComponent, type PropType } from "vue";
</script>
<script lang="ts">
export default defineComponent({
props: {
version: {
type: Object as PropType<Release>,
required: true,
},
},
});
</script>
<style lang="css" scoped>
@reference "@/main.css";
.versionDisplay :deep() ul {
list-style: none;
padding-left: 10px;
padding-bottom: 5px;
}
.versionDisplay :deep() ul li::before {
content: "-";
margin-right: 10px;
color: black;
font-weight: 600;
}
.versionDisplay :deep() a {
@apply text-primary;
}
</style>

View file

@ -19,11 +19,16 @@ export async function abilityAndNavUpdate(to: any, from: any, next: any) {
navigation.updateNavigation(); navigation.updateNavigation();
NProgress.done(); NProgress.done();
next(); next();
} else if ((admin && ability.isAdmin()) || ability.can(type, section, module)) { } else if (module && ((admin && ability.isAdmin()) || ability.can(type, section, module))) {
NProgress.done(); NProgress.done();
navigation.activeNavigation = to.name.split("-")[1]; navigation.activeNavigation = to.name.split("-")[1];
navigation.activeLink = to.name.split("-")[2]; navigation.activeLink = to.name.split("-")[2];
next(); next();
} else if (!module && ((admin && ability.isAdmin()) || ability.canSection(type, section))) {
NProgress.done();
navigation.activeNavigation = to.name.split("-")[1];
navigation.activeLink = null;
next();
} else { } else {
NProgress.done(); NProgress.done();
next({ name: "admin-default" }); next({ name: "admin-default" });

View file

@ -33,7 +33,8 @@ export const useAbilityStore = defineStore("ability", {
if (type == "admin") return permissions?.admin ?? permissions?.adminByOwner ?? false; if (type == "admin") return permissions?.admin ?? permissions?.adminByOwner ?? false;
if (permissions?.admin || permissions?.adminByOwner) return true; if (permissions?.admin || permissions?.adminByOwner) return true;
if ( if (
(permissions[section]?.all == "*" || permissions[section]?.all?.includes(type)) && permissions[section]?.all == "*" ||
permissions[section]?.all?.includes(type) ||
permissions[section] != undefined permissions[section] != undefined
) )
return true; return true;

View file

@ -49,7 +49,8 @@ export const useMembershipStore = defineStore("membership", {
http http
.get(`/admin/member/${memberId}/memberships/totalstatistics`) .get(`/admin/member/${memberId}/memberships/totalstatistics`)
.then((result) => { .then((result) => {
this.totalMembershipStatistics = result.data; if (result.status == 200) this.totalMembershipStatistics = result.data;
else this.totalMembershipStatistics = undefined;
}) })
.catch((err) => {}); .catch((err) => {});
}, },

View file

@ -37,6 +37,11 @@ export const useNavigationStore = defineStore("navigation", {
}; };
}, },
getters: { getters: {
topLevelObject: (state) =>
state.topLevel.map((tl) => ({
...tl,
levelDefault: state.navigation[tl.key].main.filter((m) => !m.key.includes("divider"))[0]?.key ?? "",
})),
activeNavigationObject: (state) => (state.navigation[state.activeNavigation] ?? {}) as navigationSplitModel, activeNavigationObject: (state) => (state.navigation[state.activeNavigation] ?? {}) as navigationSplitModel,
activeTopLevelObject: (state) => activeTopLevelObject: (state) =>
(state.topLevel.find((elem) => elem.key == state.activeNavigation) ?? {}) as topLevelNavigationModel, (state.topLevel.find((elem) => elem.key == state.activeNavigation) ?? {}) as topLevelNavigationModel,

View file

@ -8,6 +8,7 @@ export const useContextMenuStore = defineStore("context-menu", {
show: false, show: false,
component_ref: null as any, component_ref: null as any,
data: null as any, data: null as any,
clickedOnEl: null as any,
}; };
}, },
getters: { getters: {
@ -16,16 +17,18 @@ export const useContextMenuStore = defineStore("context-menu", {
}, },
}, },
actions: { actions: {
openContextMenu(e: MouseEvent, content: { component_ref: any; data: any }) { openContextMenu(e: MouseEvent, content: { component_ref: any; data?: any }) {
this.component_ref = content.component_ref; this.component_ref = content.component_ref;
this.data = content.data; this.data = content.data;
this.contextX = e.pageX; this.contextX = e.pageX;
this.contextY = e.pageY; this.contextY = e.pageY;
this.clickedOnEl = e.target;
this.show = true; this.show = true;
}, },
closeContextMenu() { closeContextMenu() {
this.component_ref = null; this.component_ref = null;
this.data = null; this.data = null;
this.clickedOnEl = null;
this.show = false; this.show = false;
}, },
}, },

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="flex flex-col items-center"> <div class="flex flex-col gap-2 items-center">
<br /> <br />
<h1 class="w-full p-4 text-center font-bold text-3xl">Kein Zugriff</h1> <h1 class="w-full p-4 text-center font-bold text-3xl">Kein Zugriff</h1>
<br /> <br />

View file

@ -52,8 +52,7 @@ import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia"; import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue"; import Spinner from "@/components/Spinner.vue";
import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter"; import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter";
import { QuillEditor } from "@vueup/vue-quill"; import QuillEditor from "@/components/QuillEditor.vue";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig"; import { toolbarOptions } from "@/helpers/quillConfig";
import { useAbilityStore } from "@/stores/ability"; import { useAbilityStore } from "@/stores/ability";
</script> </script>

View file

@ -107,8 +107,7 @@
import { defineComponent } from "vue"; import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia"; import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue"; import Spinner from "@/components/Spinner.vue";
import { QuillEditor } from "@vueup/vue-quill"; import QuillEditor from "@/components/QuillEditor.vue";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig"; import { toolbarOptions } from "@/helpers/quillConfig";
import { useNewsletterDatesStore } from "@/stores/admin/club/newsletter/newsletterDates"; import { useNewsletterDatesStore } from "@/stores/admin/club/newsletter/newsletterDates";
import { useAbilityStore } from "@/stores/ability"; import { useAbilityStore } from "@/stores/ability";

View file

@ -18,13 +18,12 @@
<label for="summary">Zusammenfassung</label> <label for="summary">Zusammenfassung</label>
<QuillEditor <QuillEditor
id="summary" id="summary"
theme="snow"
placeholder="Zusammenfassung zum Newsletter..." placeholder="Zusammenfassung zum Newsletter..."
style="height: 250px; max-height: 250px; min-height: 250px" style="height: 250px; max-height: 250px; min-height: 250px"
contentType="html" contentType="html"
:toolbar="toolbarOptions" :toolbar="toolbarOptions"
v-model:content="activeNewsletterObj.description" v-model:content="activeNewsletterObj.description"
:enable="can('create', 'club', 'newsletter')" :readonly="!can('create', 'club', 'newsletter')"
:style="!can('create', 'club', 'newsletter') ? 'opacity: 75%; background: rgb(243 244 246)' : ''" :style="!can('create', 'club', 'newsletter') ? 'opacity: 75%; background: rgb(243 244 246)' : ''"
/> />
</div> </div>
@ -42,8 +41,7 @@ import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia"; import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue"; import Spinner from "@/components/Spinner.vue";
import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter"; import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter";
import { QuillEditor } from "@vueup/vue-quill"; import QuillEditor from "@/components/QuillEditor.vue";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig"; import { toolbarOptions } from "@/helpers/quillConfig";
import { useAbilityStore } from "@/stores/ability"; import { useAbilityStore } from "@/stores/ability";
</script> </script>

View file

@ -78,8 +78,7 @@
import { defineComponent } from "vue"; import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia"; import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue"; import Spinner from "@/components/Spinner.vue";
import { QuillEditor } from "@vueup/vue-quill"; import QuillEditor from "@/components/QuillEditor.vue";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig"; import { toolbarOptions } from "@/helpers/quillConfig";
import { useProtocolAgendaStore } from "@/stores/admin/club/protocol/protocolAgenda"; import { useProtocolAgendaStore } from "@/stores/admin/club/protocol/protocolAgenda";
import { useAbilityStore } from "@/stores/ability"; import { useAbilityStore } from "@/stores/ability";

View file

@ -24,7 +24,7 @@
type="text" type="text"
name="title" name="title"
id="title" id="title"
placeholder="Entscheidung" placeholder="Beschluss"
autocomplete="off" autocomplete="off"
v-model="item.topic" v-model="item.topic"
@keyup.prevent @keyup.prevent
@ -57,7 +57,7 @@
<QuillEditor <QuillEditor
id="top" id="top"
theme="snow" theme="snow"
placeholder="Entscheidung Inhalt..." placeholder="Beschluss Inhalt..."
style="height: 250px; max-height: 250px; min-height: 250px" style="height: 250px; max-height: 250px; min-height: 250px"
contentType="html" contentType="html"
:toolbar="toolbarOptions" :toolbar="toolbarOptions"
@ -78,8 +78,7 @@
import { defineComponent } from "vue"; import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia"; import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue"; import Spinner from "@/components/Spinner.vue";
import { QuillEditor } from "@vueup/vue-quill"; import QuillEditor from "@/components/QuillEditor.vue";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig"; import { toolbarOptions } from "@/helpers/quillConfig";
import { useProtocolDecisionStore } from "@/stores/admin/club/protocol/protocolDecision"; import { useProtocolDecisionStore } from "@/stores/admin/club/protocol/protocolDecision";
import { useAbilityStore } from "@/stores/ability"; import { useAbilityStore } from "@/stores/ability";

View file

@ -64,8 +64,7 @@ import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia"; import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue"; import Spinner from "@/components/Spinner.vue";
import { useProtocolStore } from "@/stores/admin/club/protocol/protocol"; import { useProtocolStore } from "@/stores/admin/club/protocol/protocol";
import { QuillEditor } from "@vueup/vue-quill"; import QuillEditor from "@/components/QuillEditor.vue";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig"; import { toolbarOptions } from "@/helpers/quillConfig";
import { useAbilityStore } from "@/stores/ability"; import { useAbilityStore } from "@/stores/ability";
</script> </script>

View file

@ -57,8 +57,8 @@
<QuillEditor <QuillEditor
id="top" id="top"
theme="snow" theme="snow"
placeholder="Entscheidung Inhalt..." placeholder="Abstimmung Inhalt..."
style="height: 100px; max-height: 100px; min-height: 100px" style="height: 150px; max-height: 150px; min-height: 150px"
contentType="html" contentType="html"
:toolbar="toolbarOptions" :toolbar="toolbarOptions"
v-model:content="item.context" v-model:content="item.context"
@ -95,8 +95,7 @@
import { defineComponent } from "vue"; import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia"; import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue"; import Spinner from "@/components/Spinner.vue";
import { QuillEditor } from "@vueup/vue-quill"; import QuillEditor from "@/components/QuillEditor.vue";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig"; import { toolbarOptions } from "@/helpers/quillConfig";
import { useProtocolVotingStore } from "@/stores/admin/club/protocol/protocolVoting"; import { useProtocolVotingStore } from "@/stores/admin/club/protocol/protocolVoting";
import { useAbilityStore } from "@/stores/ability"; import { useAbilityStore } from "@/stores/ability";

View file

@ -11,7 +11,7 @@
</small> </small>
</h1> </h1>
<p> <p>
V{{ clientVersion }} ({{ v{{ clientVersion }} ({{
new Date(clientVersionRelease).toLocaleDateString("de", { new Date(clientVersionRelease).toLocaleDateString("de", {
month: "2-digit", month: "2-digit",
day: "2-digit", day: "2-digit",
@ -23,19 +23,7 @@
</p> </p>
</div> </div>
<div class="grow flex flex-col gap-4 overflow-y-scroll"> <div class="grow flex flex-col gap-4 overflow-y-scroll">
<div v-for="version in newerClientVersions"> <VersionItem v-for="version in newerClientVersions" :key="version.title" :version="version" />
<p>
<span class="font-semibold text-lg">V{{ version.title }}</span> vom
{{
new Date(version.isoDate).toLocaleDateString("de", {
month: "2-digit",
day: "2-digit",
year: "numeric",
})
}}
</p>
<div class="flex flex-col" v-html="version['content:encoded']"></div>
</div>
<div v-if="newerClientVersions.length == 0" class="flex items-center justify-center"> <div v-if="newerClientVersions.length == 0" class="flex items-center justify-center">
<p>Der Client ist auf der neuesten Version.</p> <p>Der Client ist auf der neuesten Version.</p>
</div> </div>
@ -50,7 +38,7 @@
</small> </small>
</h1> </h1>
<p> <p>
V{{ serverVersion }} ({{ v{{ serverVersion }} ({{
new Date(serverVersionRelease).toLocaleDateString("de", { new Date(serverVersionRelease).toLocaleDateString("de", {
month: "2-digit", month: "2-digit",
day: "2-digit", day: "2-digit",
@ -61,20 +49,8 @@
}}) }})
</p> </p>
</div> </div>
<div class="grow flex flex-col gap-2 overflow-y-scroll"> <div class="grow flex flex-col gap-4 overflow-y-scroll">
<div v-for="version in newerServerVersions"> <VersionItem v-for="version in newerServerVersions" :key="version.title" :version="version" />
<p>
<span class="font-semibold text-lg">V{{ version.title }}</span> vom
{{
new Date(version.isoDate).toLocaleDateString("de", {
month: "2-digit",
day: "2-digit",
year: "numeric",
})
}}
</p>
<div class="flex flex-col" v-html="version['content:encoded']"></div>
</div>
<div v-if="newerServerVersions.length == 0" class="flex items-center justify-center"> <div v-if="newerServerVersions.length == 0" class="flex items-center justify-center">
<p>Der Server ist auf der neuesten Version.</p> <p>Der Server ist auf der neuesten Version.</p>
</div> </div>
@ -90,6 +66,7 @@ import { defineComponent } from "vue";
import MainTemplate from "@/templates/Main.vue"; import MainTemplate from "@/templates/Main.vue";
import clientPackage from "../../../../../package.json"; import clientPackage from "../../../../../package.json";
import type { Releases } from "@/viewmodels/version.models"; import type { Releases } from "@/viewmodels/version.models";
import VersionItem from "@/components/admin/management/version/VersionItem.vue";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -113,11 +90,11 @@ export default defineComponent({
}, },
serverVersionRelease() { serverVersionRelease() {
if (!this.serverRss) return ""; if (!this.serverRss) return "";
return this.serverRss.items.find((i) => i.title == this.serverVersion)?.isoDate ?? ""; return this.serverRss.items.find((i) => i.title == `v${this.serverVersion}`)?.isoDate ?? "";
}, },
clientVersionRelease() { clientVersionRelease() {
if (!this.clientRss) return ""; if (!this.clientRss) return "";
return this.clientRss.items.find((i) => i.title == this.clientVersion)?.isoDate ?? ""; return this.clientRss.items.find((i) => i.title == `v${this.clientVersion}`)?.isoDate ?? "";
}, },
}, },
mounted() { mounted() {

View file

@ -157,6 +157,8 @@ export default defineComponent({
this.inviteStatus = "success"; this.inviteStatus = "success";
localStorage.setItem("accessToken", result.data.accessToken); localStorage.setItem("accessToken", result.data.accessToken);
localStorage.setItem("refreshToken", result.data.refreshToken); localStorage.setItem("refreshToken", result.data.refreshToken);
localStorage.setItem("routine", this.tab);
localStorage.setItem("username", this.username);
setTimeout(() => { setTimeout(() => {
this.$router.push(`/admin`); this.$router.push(`/admin`);
}, 1000); }, 1000);

View file

@ -16,7 +16,7 @@
<div class="flex flex-row gap-2"> <div class="flex flex-row gap-2">
<button type="submit" primary :disabled="resetStatus == 'loading' || resetStatus == 'success'"> <button type="submit" primary :disabled="resetStatus == 'loading' || resetStatus == 'success'">
TOTP zurücksetzen Zugangsdaten zurücksetzen
</button> </button>
<Spinner v-if="resetStatus == 'loading'" class="my-auto" /> <Spinner v-if="resetStatus == 'loading'" class="my-auto" />
<SuccessCheckmark v-else-if="resetStatus == 'success'" /> <SuccessCheckmark v-else-if="resetStatus == 'success'" />

View file

@ -15,6 +15,28 @@
<RouterLink to="/setup" class="text-primary">Zum Einrichtungsstart</RouterLink> <RouterLink to="/setup" class="text-primary">Zum Einrichtungsstart</RouterLink>
</div> </div>
<form v-else class="flex flex-col gap-2" @submit.prevent="setup"> <form v-else class="flex flex-col gap-2" @submit.prevent="setup">
<div class="w-full flex flex-row gap-2 justify-center">
<p
class="w-1/2 p-0.5 pl-0 rounded-lg py-2.5 text-sm text-center font-medium leading-5 outline-hidden cursor-pointer"
:class="
tab == 'totp' ? 'bg-red-200 shadow-sm border-b-2 border-primary rounded-b-none' : ' hover:bg-red-200'
"
@click="tab = 'totp'"
>
TOTP
</p>
<p
class="w-1/2 p-0.5 rounded-lg py-2.5 text-sm text-center font-medium leading-5 outline-hidden cursor-pointer"
:class="
tab == 'password' ? 'bg-red-200 shadow-sm border-b-2 border-primary rounded-b-none' : 'hover:bg-red-200'
"
@click="tab = 'password'"
>
Passwort
</p>
</div>
<p class="text-center">Dein Nutzername: {{ username }}</p>
<div v-if="tab == 'totp'" class="flex flex-col gap-2">
<img :src="image" alt="totp" class="w-56 h-56 self-center" /> <img :src="image" alt="totp" class="w-56 h-56 self-center" />
<TextCopy :copyText="otp" /> <TextCopy :copyText="otp" />
@ -24,6 +46,30 @@
<input id="totp" name="totp" type="text" required placeholder="TOTP" /> <input id="totp" name="totp" type="text" required placeholder="TOTP" />
</div> </div>
</div> </div>
</div>
<div v-else>
<input
id="password"
name="password"
type="password"
required
placeholder="Passwort"
class="rounded-b-none!"
autocomplete="new-password"
:class="notMatching ? 'border-red-600!' : ''"
/>
<input
id="password_rep"
name="password_rep"
type="password"
required
placeholder="Passwort wiederholen"
class="rounded-t-none!"
autocomplete="new-password"
:class="notMatching ? 'border-red-600!' : ''"
/>
<p v-if="notMatching">Passwörter stimmen nicht überein</p>
</div>
<div class="flex flex-row gap-2"> <div class="flex flex-row gap-2">
<button type="submit" primary :disabled="setupStatus == 'loading' || setupStatus == 'success'"> <button type="submit" primary :disabled="setupStatus == 'loading' || setupStatus == 'success'">
@ -51,6 +97,7 @@ import { RouterLink } from "vue-router";
import FormBottomBar from "@/components/FormBottomBar.vue"; import FormBottomBar from "@/components/FormBottomBar.vue";
import TextCopy from "@/components/TextCopy.vue"; import TextCopy from "@/components/TextCopy.vue";
import AppLogo from "@/components/AppLogo.vue"; import AppLogo from "@/components/AppLogo.vue";
import { hashString } from "@/helpers/crypto";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -61,11 +108,14 @@ export default defineComponent({
}, },
data() { data() {
return { return {
tab: "totp",
verification: "loading" as "success" | "loading" | "failed", verification: "loading" as "success" | "loading" | "failed",
image: undefined as undefined | string, image: undefined as undefined | string,
otp: undefined as undefined | string, otp: undefined as undefined | string,
username: "" as string,
setupStatus: undefined as undefined | "loading" | "success" | "failed", setupStatus: undefined as undefined | "loading" | "success" | "failed",
setupError: "" as string, setupError: "" as string,
notMatching: false as boolean,
}; };
}, },
mounted() { mounted() {
@ -79,6 +129,7 @@ export default defineComponent({
this.verification = "success"; this.verification = "success";
this.image = result.data.dataUrl; this.image = result.data.dataUrl;
this.otp = result.data.otp; this.otp = result.data.otp;
this.username = result.data.username;
}, 1000); }, 1000);
}) })
.catch((err) => { .catch((err) => {
@ -88,20 +139,28 @@ export default defineComponent({
}); });
}, },
methods: { methods: {
setup(e: any) { async setup(e: any) {
let formData = e.target.elements; let secret = "";
if (this.tab == "totp") secret = this.totp(e);
else secret = await this.password(e);
if (secret == "") return;
this.setupStatus = "loading"; this.setupStatus = "loading";
this.setupError = ""; this.setupError = "";
this.$http this.$http
.post(`/setup/finish`, { .post(`/setup/finish`, {
token: this.token, token: this.token,
mail: this.mail, mail: this.mail,
totp: formData.totp.value, secret: secret,
routine: this.tab,
}) })
.then((result) => { .then((result) => {
this.setupStatus = "success"; this.setupStatus = "success";
localStorage.setItem("accessToken", result.data.accessToken); localStorage.setItem("accessToken", result.data.accessToken);
localStorage.setItem("refreshToken", result.data.refreshToken); localStorage.setItem("refreshToken", result.data.refreshToken);
localStorage.setItem("routine", this.tab);
localStorage.setItem("username", this.username);
setTimeout(() => { setTimeout(() => {
this.$router.push(`/admin`); this.$router.push(`/admin`);
}, 1000); }, 1000);
@ -111,6 +170,24 @@ export default defineComponent({
this.setupError = err.response.data; this.setupError = err.response.data;
}); });
}, },
totp(e: any) {
let formData = e.target.elements;
return formData.totp.value;
},
async password(e: any) {
let formData = e.target.elements;
let new_pw = await hashString(formData.password.value);
let new_rep = await hashString(formData.password_rep.value);
if (new_pw != new_rep) {
this.notMatching = true;
return "";
}
this.notMatching = false;
return await hashString(formData.password.value);
},
}, },
}); });
</script> </script>

View file

@ -41,6 +41,8 @@ export default defineConfig({
injectRegister: "auto", injectRegister: "auto",
manifest: false, manifest: false,
workbox: { workbox: {
cleanupOutdatedCaches: true,
skipWaiting: true,
navigateFallbackDenylist: [/^\/api*/], navigateFallbackDenylist: [/^\/api*/],
runtimeCaching: [ runtimeCaching: [
{ {