Compare commits
No commits in common. "main" and "v1.7.3" have entirely different histories.
22 changed files with 161 additions and 408 deletions
216
package-lock.json
generated
216
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "ff-admin",
|
||||
"version": "1.7.5",
|
||||
"version": "1.7.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ff-admin",
|
||||
"version": "1.7.5",
|
||||
"version": "1.7.3",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"@fullcalendar/core": "^6.1.18",
|
||||
|
@ -18,8 +18,7 @@
|
|||
"@headlessui/vue": "^1.7.23",
|
||||
"@heroicons/vue": "^2.2.0",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@vueuse/core": "^13.5.0",
|
||||
"axios": "^1.11.0",
|
||||
"axios": "^1.10.0",
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
"grapesjs": "^0.22.11",
|
||||
"grapesjs-preset-newsletter": "^1.0.2",
|
||||
|
@ -42,7 +41,7 @@
|
|||
"socket.io-client": "^4.8.1",
|
||||
"unplugin-vue-markdown": "^29.1.0",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.18",
|
||||
"vue": "^3.5.17",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -56,7 +55,7 @@
|
|||
"@types/lodash.differencewith": "^4.5.9",
|
||||
"@types/lodash.isequal": "^4.5.8",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@types/node": "^24.1.0",
|
||||
"@types/node": "^24.0.15",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/qs": "^6.14.0",
|
||||
|
@ -72,7 +71,7 @@
|
|||
"prettier": "^3.6.2",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^7.0.6",
|
||||
"vite": "^7.0.5",
|
||||
"vite-plugin-pwa": "^1.0.1",
|
||||
"vite-plugin-vue-devtools": "^7.7.7",
|
||||
"vue-tsc": "^3.0.3"
|
||||
|
@ -491,12 +490,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
|
||||
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
|
||||
"version": "7.27.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz",
|
||||
"integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.28.0"
|
||||
"@babel/types": "^7.27.3"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
|
@ -1760,9 +1759,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz",
|
||||
"integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==",
|
||||
"version": "7.27.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz",
|
||||
"integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
|
@ -3902,9 +3901,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz",
|
||||
"integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
|
||||
"version": "24.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.15.tgz",
|
||||
"integrity": "sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -3968,12 +3967,6 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz",
|
||||
|
@ -4342,39 +4335,39 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.5.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz",
|
||||
"integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==",
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz",
|
||||
"integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@vue/shared": "3.5.18",
|
||||
"@babel/parser": "^7.27.5",
|
||||
"@vue/shared": "3.5.17",
|
||||
"entities": "^4.5.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-dom": {
|
||||
"version": "3.5.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz",
|
||||
"integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==",
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz",
|
||||
"integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.5.18",
|
||||
"@vue/shared": "3.5.18"
|
||||
"@vue/compiler-core": "3.5.17",
|
||||
"@vue/shared": "3.5.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc": {
|
||||
"version": "3.5.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz",
|
||||
"integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==",
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz",
|
||||
"integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@vue/compiler-core": "3.5.18",
|
||||
"@vue/compiler-dom": "3.5.18",
|
||||
"@vue/compiler-ssr": "3.5.18",
|
||||
"@vue/shared": "3.5.18",
|
||||
"@babel/parser": "^7.27.5",
|
||||
"@vue/compiler-core": "3.5.17",
|
||||
"@vue/compiler-dom": "3.5.17",
|
||||
"@vue/compiler-ssr": "3.5.17",
|
||||
"@vue/shared": "3.5.17",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.17",
|
||||
"postcss": "^8.5.6",
|
||||
|
@ -4382,13 +4375,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.5.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz",
|
||||
"integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==",
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz",
|
||||
"integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.18",
|
||||
"@vue/shared": "3.5.18"
|
||||
"@vue/compiler-dom": "3.5.17",
|
||||
"@vue/shared": "3.5.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-vue2": {
|
||||
|
@ -4552,53 +4545,53 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
"version": "3.5.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz",
|
||||
"integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==",
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.17.tgz",
|
||||
"integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.5.18"
|
||||
"@vue/shared": "3.5.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core": {
|
||||
"version": "3.5.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz",
|
||||
"integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==",
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.17.tgz",
|
||||
"integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.5.18",
|
||||
"@vue/shared": "3.5.18"
|
||||
"@vue/reactivity": "3.5.17",
|
||||
"@vue/shared": "3.5.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom": {
|
||||
"version": "3.5.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz",
|
||||
"integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==",
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz",
|
||||
"integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.5.18",
|
||||
"@vue/runtime-core": "3.5.18",
|
||||
"@vue/shared": "3.5.18",
|
||||
"@vue/reactivity": "3.5.17",
|
||||
"@vue/runtime-core": "3.5.17",
|
||||
"@vue/shared": "3.5.17",
|
||||
"csstype": "^3.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer": {
|
||||
"version": "3.5.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz",
|
||||
"integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==",
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.17.tgz",
|
||||
"integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.5.18",
|
||||
"@vue/shared": "3.5.18"
|
||||
"@vue/compiler-ssr": "3.5.17",
|
||||
"@vue/shared": "3.5.17"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "3.5.18"
|
||||
"vue": "3.5.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
"version": "3.5.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz",
|
||||
"integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==",
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.17.tgz",
|
||||
"integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/tsconfig": {
|
||||
|
@ -4620,44 +4613,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "13.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.5.0.tgz",
|
||||
"integrity": "sha512-wV7z0eUpifKmvmN78UBZX8T7lMW53Nrk6JP5+6hbzrB9+cJ3jr//hUlhl9TZO/03bUkMK6gGkQpqOPWoabr72g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.21",
|
||||
"@vueuse/metadata": "13.5.0",
|
||||
"@vueuse/shared": "13.5.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"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": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
|
@ -4823,13 +4778,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
|
||||
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
|
||||
"integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
|
@ -6454,15 +6409,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
||||
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -10726,14 +10680,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz",
|
||||
"integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==",
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz",
|
||||
"integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.6",
|
||||
"picomatch": "^4.0.3",
|
||||
"picomatch": "^4.0.2",
|
||||
"postcss": "^8.5.6",
|
||||
"rollup": "^4.40.0",
|
||||
"tinyglobby": "^0.2.14"
|
||||
|
@ -10933,9 +10887,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
|
@ -10952,16 +10906,16 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.5.18",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz",
|
||||
"integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==",
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.17.tgz",
|
||||
"integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.18",
|
||||
"@vue/compiler-sfc": "3.5.18",
|
||||
"@vue/runtime-dom": "3.5.18",
|
||||
"@vue/server-renderer": "3.5.18",
|
||||
"@vue/shared": "3.5.18"
|
||||
"@vue/compiler-dom": "3.5.17",
|
||||
"@vue/compiler-sfc": "3.5.17",
|
||||
"@vue/runtime-dom": "3.5.17",
|
||||
"@vue/server-renderer": "3.5.17",
|
||||
"@vue/shared": "3.5.17"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "*"
|
||||
|
|
13
package.json
13
package.json
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"name": "ff-admin",
|
||||
"version": "1.7.5",
|
||||
"version": "1.7.3",
|
||||
"description": "Feuerwehr/Verein Mitgliederverwaltung UI",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
|
@ -33,8 +33,7 @@
|
|||
"@headlessui/vue": "^1.7.23",
|
||||
"@heroicons/vue": "^2.2.0",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@vueuse/core": "^13.5.0",
|
||||
"axios": "^1.11.0",
|
||||
"axios": "^1.10.0",
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
"grapesjs": "^0.22.11",
|
||||
"grapesjs-preset-newsletter": "^1.0.2",
|
||||
|
@ -57,7 +56,7 @@
|
|||
"socket.io-client": "^4.8.1",
|
||||
"unplugin-vue-markdown": "^29.1.0",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.18",
|
||||
"vue": "^3.5.17",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -71,7 +70,7 @@
|
|||
"@types/lodash.differencewith": "^4.5.9",
|
||||
"@types/lodash.isequal": "^4.5.8",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@types/node": "^24.1.0",
|
||||
"@types/node": "^24.0.15",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/qs": "^6.14.0",
|
||||
|
@ -87,7 +86,7 @@
|
|||
"prettier": "^3.6.2",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^7.0.6",
|
||||
"vite": "^7.0.5",
|
||||
"vite-plugin-pwa": "^1.0.1",
|
||||
"vite-plugin-vue-devtools": "^7.7.7",
|
||||
"vue-tsc": "^3.0.3"
|
||||
|
|
34
src/App.vue
34
src/App.vue
|
@ -2,11 +2,11 @@
|
|||
<Modal />
|
||||
<ContextMenu />
|
||||
|
||||
<AppHeader />
|
||||
<div class="grow overflow-x-hidden overflow-y-auto p-2 md:p-4">
|
||||
<Header @contextmenu.prevent />
|
||||
<div class="grow overflow-x-hidden overflow-y-auto p-2 md:p-4" @contextmenu.prevent>
|
||||
<RouterView />
|
||||
</div>
|
||||
<AppFooter />
|
||||
<Footer @contextmenu.prevent />
|
||||
<Notification />
|
||||
|
||||
<Teleport to="head">
|
||||
|
@ -18,11 +18,10 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineAsyncComponent, defineComponent, markRaw } from "vue";
|
||||
import { onLongPress } from "@vueuse/core";
|
||||
import { defineComponent } from "vue";
|
||||
import { RouterView } from "vue-router";
|
||||
import AppHeader from "./components/Header.vue";
|
||||
import AppFooter from "./components/Footer.vue";
|
||||
import Header from "./components/Header.vue";
|
||||
import Footer from "./components/Footer.vue";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useAuthStore } from "./stores/auth";
|
||||
import { isAuthenticatedPromise } from "./router/authGuard";
|
||||
|
@ -32,7 +31,6 @@ import Notification from "./components/Notification.vue";
|
|||
import { config } from "./config";
|
||||
import { useConfigurationStore } from "@/stores/configuration";
|
||||
import { resetAllPiniaStores } from "@/helpers/piniaReset";
|
||||
import { useContextMenuStore } from "./stores/context-menu";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -42,11 +40,6 @@ export default defineComponent({
|
|||
...mapState(useConfigurationStore, ["clubName"]),
|
||||
},
|
||||
mounted() {
|
||||
document.body.addEventListener("contextmenu", (event) => {
|
||||
this.handleContextMenu(event);
|
||||
});
|
||||
onLongPress(document.body, this.handleContextMenu);
|
||||
|
||||
resetAllPiniaStores();
|
||||
this.configure();
|
||||
|
||||
|
@ -59,21 +52,6 @@ export default defineComponent({
|
|||
},
|
||||
methods: {
|
||||
...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>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<div
|
||||
ref="contextMenu"
|
||||
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"
|
||||
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"
|
||||
v-show="show"
|
||||
:style="contextMenuStyle"
|
||||
@contextmenu.prevent
|
||||
@click="closeContextMenu"
|
||||
>
|
||||
<component :is="component_ref" :data="data" />
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
<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>
|
|
@ -6,7 +6,7 @@
|
|||
<div class="w-full flex flex-row gap-2 h-full align-middle">
|
||||
<TopLevelLink
|
||||
v-if="routeName == 'admin' || routeName.includes('admin-')"
|
||||
v-for="item in topLevelObject"
|
||||
v-for="item in topLevel"
|
||||
:key="item.key"
|
||||
:link="item"
|
||||
:disableSubLink="true"
|
||||
|
@ -34,7 +34,7 @@ import TopLevelLink from "./admin/TopLevelLink.vue";
|
|||
export default defineComponent({
|
||||
computed: {
|
||||
...mapState(useAuthStore, ["authCheck"]),
|
||||
...mapState(useNavigationStore, ["topLevelObject"]),
|
||||
...mapState(useNavigationStore, ["topLevel"]),
|
||||
routeName() {
|
||||
return typeof this.$route.name == "string" ? this.$route.name : "";
|
||||
},
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<div v-if="authCheck" class="hidden md:flex flex-row gap-2 h-full align-middle">
|
||||
<TopLevelLink
|
||||
v-if="routeName == 'admin' || routeName.includes('admin-')"
|
||||
v-for="item in topLevelObject"
|
||||
v-for="item in topLevel"
|
||||
:key="item.key"
|
||||
:link="item"
|
||||
/>
|
||||
|
@ -46,7 +46,7 @@ import { useConfigurationStore } from "@/stores/configuration";
|
|||
export default defineComponent({
|
||||
computed: {
|
||||
...mapState(useAuthStore, ["authCheck"]),
|
||||
...mapState(useNavigationStore, ["topLevelObject"]),
|
||||
...mapState(useNavigationStore, ["topLevel"]),
|
||||
...mapState(useConfigurationStore, ["clubName"]),
|
||||
routeName() {
|
||||
return typeof this.$route.name == "string" ? this.$route.name : "";
|
||||
|
|
|
@ -3,15 +3,21 @@
|
|||
ref="contextMenu"
|
||||
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"
|
||||
@contextmenu.prevent
|
||||
>
|
||||
<component :is="component_ref" :data="data" class="p-4 bg-white rounded-lg max-h-[95%] overflow-y-auto" />
|
||||
<!-- @click="closeModal" -->
|
||||
<component
|
||||
:is="component_ref"
|
||||
:data="data"
|
||||
@click.stop
|
||||
class="p-4 bg-white rounded-lg max-h-[95%] overflow-y-auto"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { mapState, mapActions } from "pinia";
|
||||
import { useModalStore } from "@/stores/modal";
|
||||
import { useContextMenuStore } from "@/stores/context-menu";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -21,7 +27,6 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
...mapActions(useModalStore, ["closeModal"]),
|
||||
...mapActions(useContextMenuStore, ["closeContextMenu"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="flex relative">
|
||||
<input type="text" readonly :value="copyText" />
|
||||
<input type="text" :value="copyText" />
|
||||
<ClipboardIcon
|
||||
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"
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
<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>
|
|
@ -19,16 +19,11 @@ export async function abilityAndNavUpdate(to: any, from: any, next: any) {
|
|||
navigation.updateNavigation();
|
||||
NProgress.done();
|
||||
next();
|
||||
} else if (module && ((admin && ability.isAdmin()) || ability.can(type, section, module))) {
|
||||
} else if ((admin && ability.isAdmin()) || ability.can(type, section, module)) {
|
||||
NProgress.done();
|
||||
navigation.activeNavigation = to.name.split("-")[1];
|
||||
navigation.activeLink = to.name.split("-")[2];
|
||||
next();
|
||||
} else if (!module && ((admin && ability.isAdmin()) || ability.canSection(type, section))) {
|
||||
NProgress.done();
|
||||
navigation.activeNavigation = to.name.split("-")[1];
|
||||
navigation.activeLink = null;
|
||||
next();
|
||||
} else {
|
||||
NProgress.done();
|
||||
next({ name: "admin-default" });
|
||||
|
|
|
@ -33,8 +33,7 @@ export const useAbilityStore = defineStore("ability", {
|
|||
if (type == "admin") return permissions?.admin ?? permissions?.adminByOwner ?? false;
|
||||
if (permissions?.admin || permissions?.adminByOwner) return true;
|
||||
if (
|
||||
permissions[section]?.all == "*" ||
|
||||
permissions[section]?.all?.includes(type) ||
|
||||
(permissions[section]?.all == "*" || permissions[section]?.all?.includes(type)) &&
|
||||
permissions[section] != undefined
|
||||
)
|
||||
return true;
|
||||
|
|
|
@ -49,8 +49,7 @@ export const useMembershipStore = defineStore("membership", {
|
|||
http
|
||||
.get(`/admin/member/${memberId}/memberships/totalstatistics`)
|
||||
.then((result) => {
|
||||
if (result.status == 200) this.totalMembershipStatistics = result.data;
|
||||
else this.totalMembershipStatistics = undefined;
|
||||
this.totalMembershipStatistics = result.data;
|
||||
})
|
||||
.catch((err) => {});
|
||||
},
|
||||
|
|
|
@ -37,11 +37,6 @@ export const useNavigationStore = defineStore("navigation", {
|
|||
};
|
||||
},
|
||||
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,
|
||||
activeTopLevelObject: (state) =>
|
||||
(state.topLevel.find((elem) => elem.key == state.activeNavigation) ?? {}) as topLevelNavigationModel,
|
||||
|
|
|
@ -8,7 +8,6 @@ export const useContextMenuStore = defineStore("context-menu", {
|
|||
show: false,
|
||||
component_ref: null as any,
|
||||
data: null as any,
|
||||
clickedOnEl: null as any,
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
|
@ -17,18 +16,16 @@ export const useContextMenuStore = defineStore("context-menu", {
|
|||
},
|
||||
},
|
||||
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.data = content.data;
|
||||
this.contextX = e.pageX;
|
||||
this.contextY = e.pageY;
|
||||
this.clickedOnEl = e.target;
|
||||
this.show = true;
|
||||
},
|
||||
closeContextMenu() {
|
||||
this.component_ref = null;
|
||||
this.data = null;
|
||||
this.clickedOnEl = null;
|
||||
this.show = false;
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="flex flex-col gap-2 items-center">
|
||||
<div class="flex flex-col items-center">
|
||||
<br />
|
||||
<h1 class="w-full p-4 text-center font-bold text-3xl">Kein Zugriff</h1>
|
||||
<br />
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
type="text"
|
||||
name="title"
|
||||
id="title"
|
||||
placeholder="Beschluss"
|
||||
placeholder="Entscheidung"
|
||||
autocomplete="off"
|
||||
v-model="item.topic"
|
||||
@keyup.prevent
|
||||
|
@ -57,7 +57,7 @@
|
|||
<QuillEditor
|
||||
id="top"
|
||||
theme="snow"
|
||||
placeholder="Beschluss Inhalt..."
|
||||
placeholder="Entscheidung Inhalt..."
|
||||
style="height: 250px; max-height: 250px; min-height: 250px"
|
||||
contentType="html"
|
||||
:toolbar="toolbarOptions"
|
||||
|
|
|
@ -57,8 +57,8 @@
|
|||
<QuillEditor
|
||||
id="top"
|
||||
theme="snow"
|
||||
placeholder="Abstimmung Inhalt..."
|
||||
style="height: 150px; max-height: 150px; min-height: 150px"
|
||||
placeholder="Entscheidung Inhalt..."
|
||||
style="height: 100px; max-height: 100px; min-height: 100px"
|
||||
contentType="html"
|
||||
:toolbar="toolbarOptions"
|
||||
v-model:content="item.context"
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</small>
|
||||
</h1>
|
||||
<p>
|
||||
v{{ clientVersion }} ({{
|
||||
V{{ clientVersion }} ({{
|
||||
new Date(clientVersionRelease).toLocaleDateString("de", {
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
|
@ -23,7 +23,19 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="grow flex flex-col gap-4 overflow-y-scroll">
|
||||
<VersionItem v-for="version in newerClientVersions" :key="version.title" :version="version" />
|
||||
<div v-for="version in newerClientVersions">
|
||||
<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">
|
||||
<p>Der Client ist auf der neuesten Version.</p>
|
||||
</div>
|
||||
|
@ -38,7 +50,7 @@
|
|||
</small>
|
||||
</h1>
|
||||
<p>
|
||||
v{{ serverVersion }} ({{
|
||||
V{{ serverVersion }} ({{
|
||||
new Date(serverVersionRelease).toLocaleDateString("de", {
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
|
@ -49,8 +61,20 @@
|
|||
}})
|
||||
</p>
|
||||
</div>
|
||||
<div class="grow flex flex-col gap-4 overflow-y-scroll">
|
||||
<VersionItem v-for="version in newerServerVersions" :key="version.title" :version="version" />
|
||||
<div class="grow flex flex-col gap-2 overflow-y-scroll">
|
||||
<div v-for="version in newerServerVersions">
|
||||
<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">
|
||||
<p>Der Server ist auf der neuesten Version.</p>
|
||||
</div>
|
||||
|
@ -66,7 +90,6 @@ import { defineComponent } from "vue";
|
|||
import MainTemplate from "@/templates/Main.vue";
|
||||
import clientPackage from "../../../../../package.json";
|
||||
import type { Releases } from "@/viewmodels/version.models";
|
||||
import VersionItem from "@/components/admin/management/version/VersionItem.vue";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -90,11 +113,11 @@ export default defineComponent({
|
|||
},
|
||||
serverVersionRelease() {
|
||||
if (!this.serverRss) return "";
|
||||
return this.serverRss.items.find((i) => i.title == `v${this.serverVersion}`)?.isoDate ?? "";
|
||||
return this.serverRss.items.find((i) => i.title == this.serverVersion)?.isoDate ?? "";
|
||||
},
|
||||
clientVersionRelease() {
|
||||
if (!this.clientRss) return "";
|
||||
return this.clientRss.items.find((i) => i.title == `v${this.clientVersion}`)?.isoDate ?? "";
|
||||
return this.clientRss.items.find((i) => i.title == this.clientVersion)?.isoDate ?? "";
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
@ -157,8 +157,6 @@ export default defineComponent({
|
|||
this.inviteStatus = "success";
|
||||
localStorage.setItem("accessToken", result.data.accessToken);
|
||||
localStorage.setItem("refreshToken", result.data.refreshToken);
|
||||
localStorage.setItem("routine", this.tab);
|
||||
localStorage.setItem("username", this.username);
|
||||
setTimeout(() => {
|
||||
this.$router.push(`/admin`);
|
||||
}, 1000);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<div class="flex flex-row gap-2">
|
||||
<button type="submit" primary :disabled="resetStatus == 'loading' || resetStatus == 'success'">
|
||||
Zugangsdaten zurücksetzen
|
||||
TOTP zurücksetzen
|
||||
</button>
|
||||
<Spinner v-if="resetStatus == 'loading'" class="my-auto" />
|
||||
<SuccessCheckmark v-else-if="resetStatus == 'success'" />
|
||||
|
|
|
@ -15,61 +15,15 @@
|
|||
<RouterLink to="/setup" class="text-primary">Zum Einrichtungsstart</RouterLink>
|
||||
</div>
|
||||
<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" />
|
||||
|
||||
<div class="-space-y-px">
|
||||
<div>
|
||||
<input id="totp" name="totp" type="text" required placeholder="TOTP" />
|
||||
</div>
|
||||
<div class="-space-y-px">
|
||||
<div>
|
||||
<input id="totp" name="totp" type="text" required placeholder="TOTP" />
|
||||
</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">
|
||||
<button type="submit" primary :disabled="setupStatus == 'loading' || setupStatus == 'success'">
|
||||
|
@ -97,7 +51,6 @@ import { RouterLink } from "vue-router";
|
|||
import FormBottomBar from "@/components/FormBottomBar.vue";
|
||||
import TextCopy from "@/components/TextCopy.vue";
|
||||
import AppLogo from "@/components/AppLogo.vue";
|
||||
import { hashString } from "@/helpers/crypto";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -108,14 +61,11 @@ export default defineComponent({
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
tab: "totp",
|
||||
verification: "loading" as "success" | "loading" | "failed",
|
||||
image: undefined as undefined | string,
|
||||
otp: undefined as undefined | string,
|
||||
username: "" as string,
|
||||
setupStatus: undefined as undefined | "loading" | "success" | "failed",
|
||||
setupError: "" as string,
|
||||
notMatching: false as boolean,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
@ -129,7 +79,6 @@ export default defineComponent({
|
|||
this.verification = "success";
|
||||
this.image = result.data.dataUrl;
|
||||
this.otp = result.data.otp;
|
||||
this.username = result.data.username;
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err) => {
|
||||
|
@ -139,28 +88,20 @@ export default defineComponent({
|
|||
});
|
||||
},
|
||||
methods: {
|
||||
async setup(e: any) {
|
||||
let secret = "";
|
||||
if (this.tab == "totp") secret = this.totp(e);
|
||||
else secret = await this.password(e);
|
||||
|
||||
if (secret == "") return;
|
||||
|
||||
setup(e: any) {
|
||||
let formData = e.target.elements;
|
||||
this.setupStatus = "loading";
|
||||
this.setupError = "";
|
||||
this.$http
|
||||
.post(`/setup/finish`, {
|
||||
token: this.token,
|
||||
mail: this.mail,
|
||||
secret: secret,
|
||||
routine: this.tab,
|
||||
totp: formData.totp.value,
|
||||
})
|
||||
.then((result) => {
|
||||
this.setupStatus = "success";
|
||||
localStorage.setItem("accessToken", result.data.accessToken);
|
||||
localStorage.setItem("refreshToken", result.data.refreshToken);
|
||||
localStorage.setItem("routine", this.tab);
|
||||
localStorage.setItem("username", this.username);
|
||||
setTimeout(() => {
|
||||
this.$router.push(`/admin`);
|
||||
}, 1000);
|
||||
|
@ -170,24 +111,6 @@ export default defineComponent({
|
|||
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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue