websockets implemented

This commit is contained in:
Torsten Schulz
2024-12-04 19:08:26 +01:00
parent d46a51db38
commit 069c97fa90
64 changed files with 2488 additions and 562 deletions

View File

@@ -12,11 +12,14 @@
"axios": "^1.7.2",
"date-fns": "^3.6.0",
"dotenv": "^16.4.5",
"mitt": "^3.0.1",
"socket.io-client": "^4.8.1",
"tinymce": "^7.3.0",
"vue": "~3.4.31",
"vue-i18n": "^10.0.0-beta.2",
"vue-multiselect": "^3.1.0",
"vue-router": "^4.0.13",
"vuetify": "^3.7.4",
"vuex": "^4.1.0"
},
"devDependencies": {
@@ -464,12 +467,12 @@
}
},
"node_modules/@intlify/core-base": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.1.tgz",
"integrity": "sha512-6kpRGjhos95ph7QmEtP4tnWFTW102s71CLQAQwfsIGqOAcoJhzcYFpzIQ0gKXzqAIXsMD/hwM5qJ4ewqMHw3gg==",
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.5.tgz",
"integrity": "sha512-F3snDTQs0MdvnnyzTDTVkOYVAZOE/MHwRvF7mn7Jw1yuih4NrFYLNYIymGlLmq4HU2iIdzYsZ7f47bOcwY73XQ==",
"dependencies": {
"@intlify/message-compiler": "10.0.1",
"@intlify/shared": "10.0.1"
"@intlify/message-compiler": "10.0.5",
"@intlify/shared": "10.0.5"
},
"engines": {
"node": ">= 16"
@@ -479,11 +482,11 @@
}
},
"node_modules/@intlify/message-compiler": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.1.tgz",
"integrity": "sha512-fPeykrcgVT5eOIlshTHiPCN8FV3AZyBOdMS3XaXzfQ6eL5wqfc29I/EdIv5YXVW5X8e/BgYeWjBC0Cuznsl/2g==",
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.5.tgz",
"integrity": "sha512-6GT1BJ852gZ0gItNZN2krX5QAmea+cmdjMvsWohArAZ3GmHdnNANEcF9JjPXAMRtQ6Ux5E269ymamg/+WU6tQA==",
"dependencies": {
"@intlify/shared": "10.0.1",
"@intlify/shared": "10.0.5",
"source-map-js": "^1.0.2"
},
"engines": {
@@ -494,9 +497,9 @@
}
},
"node_modules/@intlify/shared": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.1.tgz",
"integrity": "sha512-b4h7IWdZl710DnAhET8lgfgZ4Y9A2IZx/gbli3Ec/zHtYCoPqLHmiM7kUNBrSZj7d/SSjcMMZHuz5I09x3PYZw==",
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.5.tgz",
"integrity": "sha512-bmsP4L2HqBF6i6uaMqJMcFBONVjKt+siGluRq4Ca4C0q7W2eMaVZr8iCgF9dKbcVXutftkC7D6z2SaSMmLiDyA==",
"engines": {
"node": ">= 16"
},
@@ -510,213 +513,262 @@
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz",
"integrity": "sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.26.0.tgz",
"integrity": "sha512-gJNwtPDGEaOEgejbaseY6xMFu+CPltsc8/T+diUTTbOQLqD+bnrJq9ulH6WD69TqwqWmrfRAtUv30cCFZlbGTQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz",
"integrity": "sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.26.0.tgz",
"integrity": "sha512-YJa5Gy8mEZgz5JquFruhJODMq3lTHWLm1fOy+HIANquLzfIOzE9RA5ie3JjCdVb9r46qfAQY/l947V0zfGJ0OQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz",
"integrity": "sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.26.0.tgz",
"integrity": "sha512-ErTASs8YKbqTBoPLp/kA1B1Um5YSom8QAc4rKhg7b9tyyVqDBlQxy7Bf2wW7yIlPGPg2UODDQcbkTlruPzDosw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz",
"integrity": "sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.26.0.tgz",
"integrity": "sha512-wbgkYDHcdWW+NqP2mnf2NOuEbOLzDblalrOWcPyY6+BRbVhliavon15UploG7PpBRQ2bZJnbmh8o3yLoBvDIHA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.26.0.tgz",
"integrity": "sha512-Y9vpjfp9CDkAG4q/uwuhZk96LP11fBz/bYdyg9oaHYhtGZp7NrbkQrj/66DYMMP2Yo/QPAsVHkV891KyO52fhg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.26.0.tgz",
"integrity": "sha512-A/jvfCZ55EYPsqeaAt/yDAG4q5tt1ZboWMHEvKAH9Zl92DWvMIbnZe/f/eOXze65aJaaKbL+YeM0Hz4kLQvdwg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz",
"integrity": "sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.26.0.tgz",
"integrity": "sha512-paHF1bMXKDuizaMODm2bBTjRiHxESWiIyIdMugKeLnjuS1TCS54MF5+Y5Dx8Ui/1RBPVRE09i5OUlaLnv8OGnA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz",
"integrity": "sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.26.0.tgz",
"integrity": "sha512-cwxiHZU1GAs+TMxvgPfUDtVZjdBdTsQwVnNlzRXC5QzIJ6nhfB4I1ahKoe9yPmoaA/Vhf7m9dB1chGPpDRdGXg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz",
"integrity": "sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.26.0.tgz",
"integrity": "sha512-4daeEUQutGRCW/9zEo8JtdAgtJ1q2g5oHaoQaZbMSKaIWKDQwQ3Yx0/3jJNmpzrsScIPtx/V+1AfibLisb3AMQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz",
"integrity": "sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.26.0.tgz",
"integrity": "sha512-eGkX7zzkNxvvS05ROzJ/cO/AKqNvR/7t1jA3VZDi2vRniLKwAWxUr85fH3NsvtxU5vnUUKFHKh8flIBdlo2b3Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz",
"integrity": "sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.26.0.tgz",
"integrity": "sha512-Odp/lgHbW/mAqw/pU21goo5ruWsytP7/HCC/liOt0zcGG0llYWKrd10k9Fj0pdj3prQ63N5yQLCLiE7HTX+MYw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz",
"integrity": "sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.26.0.tgz",
"integrity": "sha512-MBR2ZhCTzUgVD0OJdTzNeF4+zsVogIR1U/FsyuFerwcqjZGvg2nYe24SAHp8O5sN8ZkRVbHwlYeHqcSQ8tcYew==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz",
"integrity": "sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.26.0.tgz",
"integrity": "sha512-YYcg8MkbN17fMbRMZuxwmxWqsmQufh3ZJFxFGoHjrE7bv0X+T6l3glcdzd7IKLiwhT+PZOJCblpnNlz1/C3kGQ==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz",
"integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.26.0.tgz",
"integrity": "sha512-ZuwpfjCwjPkAOxpjAEjabg6LRSfL7cAJb6gSQGZYjGhadlzKKywDkCUnJ+KEfrNY1jH5EEoSIKLCb572jSiglA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz",
"integrity": "sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.26.0.tgz",
"integrity": "sha512-+HJD2lFS86qkeF8kNu0kALtifMpPCZU80HvwztIKnYwym3KnA1os6nsX4BGSTLtS2QVAGG1P3guRgsYyMA0Yhg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz",
"integrity": "sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.26.0.tgz",
"integrity": "sha512-WUQzVFWPSw2uJzX4j6YEbMAiLbs0BUysgysh8s817doAYhR5ybqTI1wtKARQKo6cGop3pHnrUJPFCsXdoFaimQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz",
"integrity": "sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.26.0.tgz",
"integrity": "sha512-D4CxkazFKBfN1akAIY6ieyOqzoOoBV1OICxgUblWxff/pSjCA2khXlASUx7mK6W1oP4McqhgcCsu6QaLj3WMWg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz",
"integrity": "sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.26.0.tgz",
"integrity": "sha512-2x8MO1rm4PGEP0xWbubJW5RtbNLk3puzAMaLQd3B3JHVw4KcHlmXcO+Wewx9zCoo7EUFiMlu/aZbCJ7VjMzAag==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
},
"node_modules/@tinymce/tinymce-vue": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@tinymce/tinymce-vue/-/tinymce-vue-6.0.1.tgz",
@@ -729,10 +781,11 @@
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true,
"license": "MIT"
},
"node_modules/@vitejs/plugin-vue": {
"version": "5.1.3",
@@ -991,6 +1044,22 @@
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
@@ -1044,6 +1113,26 @@
"url": "https://dotenvx.com"
}
},
"node_modules/engine.io-client": {
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.2.tgz",
"integrity": "sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.17.1",
"xmlhttprequest-ssl": "~2.1.1"
}
},
"node_modules/engine.io-parser": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
@@ -1468,6 +1557,17 @@
"node": ">= 0.6"
}
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@@ -1622,12 +1722,13 @@
}
},
"node_modules/rollup": {
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.3.tgz",
"integrity": "sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==",
"version": "4.26.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.26.0.tgz",
"integrity": "sha512-ilcl12hnWonG8f+NxU6BlgysVA0gvY2l8N0R84S1HcINbW20bvwuCngJkkInV6LXhwRpucsW5k1ovDwEdBVrNg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.5"
"@types/estree": "1.0.6"
},
"bin": {
"rollup": "dist/bin/rollup"
@@ -1637,22 +1738,24 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.21.3",
"@rollup/rollup-android-arm64": "4.21.3",
"@rollup/rollup-darwin-arm64": "4.21.3",
"@rollup/rollup-darwin-x64": "4.21.3",
"@rollup/rollup-linux-arm-gnueabihf": "4.21.3",
"@rollup/rollup-linux-arm-musleabihf": "4.21.3",
"@rollup/rollup-linux-arm64-gnu": "4.21.3",
"@rollup/rollup-linux-arm64-musl": "4.21.3",
"@rollup/rollup-linux-powerpc64le-gnu": "4.21.3",
"@rollup/rollup-linux-riscv64-gnu": "4.21.3",
"@rollup/rollup-linux-s390x-gnu": "4.21.3",
"@rollup/rollup-linux-x64-gnu": "4.21.3",
"@rollup/rollup-linux-x64-musl": "4.21.3",
"@rollup/rollup-win32-arm64-msvc": "4.21.3",
"@rollup/rollup-win32-ia32-msvc": "4.21.3",
"@rollup/rollup-win32-x64-msvc": "4.21.3",
"@rollup/rollup-android-arm-eabi": "4.26.0",
"@rollup/rollup-android-arm64": "4.26.0",
"@rollup/rollup-darwin-arm64": "4.26.0",
"@rollup/rollup-darwin-x64": "4.26.0",
"@rollup/rollup-freebsd-arm64": "4.26.0",
"@rollup/rollup-freebsd-x64": "4.26.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.26.0",
"@rollup/rollup-linux-arm-musleabihf": "4.26.0",
"@rollup/rollup-linux-arm64-gnu": "4.26.0",
"@rollup/rollup-linux-arm64-musl": "4.26.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.26.0",
"@rollup/rollup-linux-riscv64-gnu": "4.26.0",
"@rollup/rollup-linux-s390x-gnu": "4.26.0",
"@rollup/rollup-linux-x64-gnu": "4.26.0",
"@rollup/rollup-linux-x64-musl": "4.26.0",
"@rollup/rollup-win32-arm64-msvc": "4.26.0",
"@rollup/rollup-win32-ia32-msvc": "4.26.0",
"@rollup/rollup-win32-x64-msvc": "4.26.0",
"fsevents": "~2.3.2"
}
},
@@ -1761,6 +1864,32 @@
"node": ">= 0.4"
}
},
"node_modules/socket.io-client": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.6.1",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1919,12 +2048,12 @@
}
},
"node_modules/vue-i18n": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.1.tgz",
"integrity": "sha512-SQVlSm/1S6AaG1wexvwq3ebXUrrkx75ZHD78UAs4/rYD/X3tsQxfm6ElpT4ZPegJQEgRtOJjGripqSrfqAENtg==",
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.5.tgz",
"integrity": "sha512-9/gmDlCblz3i8ypu/afiIc/SUIfTTE1mr0mZhb9pk70xo2csHAM9mp2gdQ3KD2O0AM3Hz/5ypb+FycTj/lHlPQ==",
"dependencies": {
"@intlify/core-base": "10.0.1",
"@intlify/shared": "10.0.1",
"@intlify/core-base": "10.0.5",
"@intlify/shared": "10.0.5",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
@@ -1960,6 +2089,35 @@
"vue": "^3.2.0"
}
},
"node_modules/vuetify": {
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.4.tgz",
"integrity": "sha512-Y8UU5wUDQXC3oz2uumPb8IOdvB4XMCxtxnmqdOc+LihNuPlkSgxIwf92ndRzbOtJFKHsggFUxpyLqpQp+A+5kg==",
"engines": {
"node": "^12.20 || >=14.13"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/johnleider"
},
"peerDependencies": {
"typescript": ">=4.7",
"vite-plugin-vuetify": ">=1.0.0",
"vue": "^3.3.0",
"webpack-plugin-vuetify": ">=2.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
},
"vite-plugin-vuetify": {
"optional": true
},
"webpack-plugin-vuetify": {
"optional": true
}
}
},
"node_modules/vuex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
@@ -1989,6 +2147,34 @@
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
"engines": {
"node": ">=0.4.0"
}
}
}
}

View File

@@ -12,11 +12,14 @@
"axios": "^1.7.2",
"date-fns": "^3.6.0",
"dotenv": "^16.4.5",
"mitt": "^3.0.1",
"socket.io-client": "^4.8.1",
"tinymce": "^7.3.0",
"vue": "~3.4.31",
"vue-i18n": "^10.0.0-beta.2",
"vue-multiselect": "^3.1.0",
"vue-router": "^4.0.13",
"vuetify": "^3.7.4",
"vuex": "^4.1.0"
},
"devDependencies": {

View File

@@ -0,0 +1,22 @@
import apiClient from "@/utils/axios.js";
export const getFriendships = async (acceptedOnly) => {
const response = await apiClient.get(`/api/friendships?acceptedOnly=${acceptedOnly}`);
return response.data;
};
export const endFriendship = async (friendUserId) => {
await apiClient.post("/api/friendships/end", { friendUserId });
};
export const acceptFriendship = async (friendUserId) => {
await apiClient.post("/api/friendships/accept", { friendUserId });
};
export const rejectFriendship = async (friendUserId) => {
await apiClient.post("/api/friendships/reject", { friendUserId });
};
export const withdrawRequest = async (friendUserId) => {
await apiClient.post("/api/friendships/withdraw", { friendUserId });
};

View File

@@ -92,4 +92,24 @@ span.button:hover {
background: #fdf1db;
color: #7E471B;
border: 1px solid #7E471B;
}
.font-color-gender-male {
color: #1E90FF;
}
.font-color-gender-female {
color: #FF69B4;
}
.font-color-gender-transmale {
color: #00CED1;
}
.font-color-gender-transfemale {
color: #FFB6C1;
}
.font-color-gender-nonbinary {
color: #DAA520;
}

View File

@@ -1,7 +1,8 @@
<template>
<nav>
<ul>
<li v-for="(item, key) in menu" :key="key" class="mainmenuitem" @click="openPage(item.path ?? null, !!item.children)">
<li v-for="(item, key) in menu" :key="key" class="mainmenuitem"
@click="openPage(item.path ?? null, !!item.children)">
<span v-if="item.icon" :style="`background-image:url('/images/icons/${item.icon}')`"
class="menu-icon">&nbsp;</span>
<span>{{ $t(`navigation.${key}`) }}</span>
@@ -18,6 +19,15 @@
</li>
</ul>
</li>
<li v-if="item.showLoggedinFriends === 1 && friendsList.length > 0" v-for="friend in friendsList" :key="friend.id">
{{ friend.username }}
<ul class="submenu2">
<li @click="openChat(friend.id)">{{ $t('navigation.m-friends.chat') }}</li>
<li @click="openProfile(friend.id)">{{ $t('navigation.m-friends.profile') }}</li>
</ul>
</li>
</ul>
<ul class="submenu1">
</ul>
</li>
</ul>
@@ -39,28 +49,44 @@ export default {
name: 'AppNavigation',
data() {
return {
forumList: []
forumList: [],
friendsList: [],
}
},
computed: {
...mapGetters(['menu', 'user']),
...mapGetters(['menu', 'user', 'menuNeedsUpdate']),
},
watch: {
menuNeedsUpdate(newValue) {
if (newValue) {
console.log('Menu needs update, loading menu...');
this.loadMenu();
}
},
},
created() {
if (this.user && this.user.id) {
this.loadMenu();
this.fetchForums();
this.fetchFriends();
} else {
console.log(this.user);
}
},
mounted() {
this.forumInterval = setInterval(() => {
this.fetchForums();
}, 60000);
if (this.$store.getters.socket) {
this.$store.getters.socket.on('forumschanged', (data) => {
this.fetchForums();
});
this.$store.getters.socket.on('friendloginchanged', () => {
this.fetchFriends();
});
}
},
beforeDestroy() {
if (this.forumInterval) {
clearInterval(this.forumInterval);
beforeUnmount() {
if (this.$store.getters.socket) {
this.$store.getters.socket.off('forumschanged');
this.$store.getters.socket.off('friendloginchanged');
}
},
methods: {
@@ -70,7 +96,6 @@ export default {
return;
}
if (url) {
console.log('openPage', url);
this.$router.push(url);
}
},
@@ -87,6 +112,29 @@ export default {
} catch (error) {
console.error('Error fetching forums:', error);
}
},
async fetchFriends() {
try {
const response = await apiClient.get('/api/socialnetwork/friends/loggedin');
this.friendsList = response.data;
} catch (error) {
console.error('Error fetching friends:', error);
}
},
async openProfile(hashedId) {
console.log(hashedId);
this.$root.$refs.userProfileDialog.userId = hashedId;
this.$root.$refs.userProfileDialog.open();
},
async openChat(hashedId) {
try {
} catch (error) {
}
}
}
};

View File

@@ -6,11 +6,11 @@
<span @click="$emit('delete-folder', folder)" class="icon delete-icon" title="Delete folder"></span>
</span>
<template v-for="i in depth">
<span v-if="showPipe(i)" class="marker filler">|</span>
<span v-if="showPipe(i)" class="marker filler tee">|</span>
<span v-else class="marker filler">&nbsp;</span>
</template>
<span v-if="isLastItem" class="end-marker marker"></span>
<span v-else class="marker">&#x251C;</span>
<span v-else class="marker tee">&#x251C;</span>
<span class="folder-name-text">&nbsp;{{ folder.name }}</span>
</div>
@@ -115,4 +115,8 @@ export default {
.folder-name-text {
cursor: pointer;
}
.tee {
margin: 0 1px 0 -1px;
}
</style>

View File

@@ -5,12 +5,10 @@
<div>
<div class="form-group">
<label>{{ $t("socialnetwork.gallery.create_folder_dialog.parent_folder") }}</label>
<!-- Hier wird der übergeordnete Ordner angezeigt, aber nicht bearbeitbar -->
<input type="text" :value="parentFolder.name" disabled />
</div>
<div class="form-group">
<label for="folderTitle">{{ $t("socialnetwork.gallery.create_folder_dialog.folder_title") }}</label>
<!-- Setze den Titel des Ordners für Bearbeiten -->
<input type="text" v-model="folderTitle"
:placeholder="$t('socialnetwork.gallery.create_folder_dialog.folder_title')" required />
</div>
@@ -42,6 +40,7 @@ import Multiselect from 'vue-multiselect';
import DialogWidget from '@/components/DialogWidget.vue';
import { mapGetters } from 'vuex';
import apiClient from '@/utils/axios.js';
import { EventBus } from '@/utils/eventBus.js';
export default {
name: 'CreateFolderDialog',
@@ -90,7 +89,6 @@ export default {
);
}
} catch (error) {
console.error('Error loading visibility options:', error);
}
},
async createFolder() {
@@ -105,11 +103,11 @@ export default {
};
try {
if (this.parentFolder.id) {
await apiClient.put(`/api/socialnetwork/folders/${this.parentFolder.id}`, payload);
await apiClient.post(`/api/socialnetwork/folders/${this.parentFolder.id}`, payload);
} else {
await apiClient.post(`/api/socialnetwork/folders/${this.folderId}`, payload);
}
this.$emit('created', payload);
EventBus.emit('folderCreated');
this.closeDialog();
} catch (error) {
console.error('Fehler beim Erstellen/Bearbeiten des Ordners:', error);

View File

@@ -4,9 +4,8 @@
name="UserProfileDialog" display="flex">
<div class="activities">
<span>{{ $t(`socialnetwork.friendship.state.${friendshipState}`) }}</span>
<img v-if="['none', 'denied', 'withdrawn'].includes(friendshipState)" src="/images/icons/request-friendship.png"
@click="handleFriendship()" />
<img v-else-if="['accepted', 'open']" src="/images/icons/cancel-friendship.png"
<img :src="'/images/icons/' +
(['none', 'denied', 'withdrawn'].includes(friendshipState) ? 'request-friendship.png' : 'cancel-friendship.png')"
@click="handleFriendship()" />
</div>
<div class="profile-content">
@@ -320,7 +319,7 @@ export default {
},
async handleFriendship() {
console.log(this.friendshipState);
if (this.friendshipState === 'none') {
if (['none', 'withdrawn'].includes(this.friendshipState)) {
this.requestFriendship();
} else if (this.friendshipState === 'waiting') {
this.cancelFriendship();

View File

@@ -12,6 +12,7 @@ import enActivate from './locales/en/activate.json';
import enSettings from './locales/en/settings.json';
import enAdmin from './locales/en/admin.json';
import enSocialNetwork from './locales/en/socialnetwork.json';
import enFriends from './locales/en/friends.json';
import deGeneral from './locales/de/general.json';
import deHeader from './locales/de/header.json';
@@ -24,6 +25,7 @@ import deActivate from './locales/de/activate.json';
import deSettings from './locales/de/settings.json';
import deAdmin from './locales/de/admin.json';
import deSocialNetwork from './locales/de/socialnetwork.json';
import deFriends from './locales/de/friends.json';
const messages = {
en: {
@@ -38,6 +40,7 @@ const messages = {
...enSettings,
...enAdmin,
...enSocialNetwork,
...enFriends,
},
de: {
'Ok': 'Ok',
@@ -52,6 +55,7 @@ const messages = {
...deSettings,
...deAdmin,
...deSocialNetwork,
...deFriends,
}
};

View File

@@ -0,0 +1,23 @@
{
"friends": {
"title": "Freunde",
"tabs": {
"existing": "Bestehende",
"rejected": "Abgelehnte",
"pending": "Ausstehende",
"requested": "Angefragte"
},
"actions": {
"end": "Beenden",
"accept": "Annehmen",
"reject": "Ablehnen",
"withdraw": "Zurückziehen"
},
"headers": {
"name": "Name",
"age": "Alter",
"gender": "Geschlecht",
"actions": "Aktionen"
}
}
}

View File

@@ -31,5 +31,15 @@
"OK": "Ok",
"Cancel": "Abbrechen",
"yes": "Ja",
"no": "Nein"
"no": "Nein",
"message": {
"close": "Schließen"
},
"gender": {
"male": "Männlich",
"female": "Weiblich",
"transmale": "Trans-Mann",
"transfemale": "Trans-Frau",
"nonbinary": "Nichtbinär"
}
}

View File

@@ -41,7 +41,9 @@
"falukant": "Falukant"
},
"m-friends": {
"manageFriends": "Freunde verwalten"
"manageFriends": "Freunde verwalten",
"chat": "Chatten",
"profile": "Profil"
}
}
}

View File

@@ -0,0 +1,3 @@
{
}

View File

@@ -4,6 +4,9 @@ import store from './store';
import router from './router';
import './assets/styles.scss';
import i18n from './i18n';
import { createVuetify } from 'vuetify';
import * as components from 'vuetify/components';
import * as directives from 'vuetify/directives';
function getBrowserLanguage() {
const browserLanguage = navigator.language || navigator.languages[0];
@@ -14,6 +17,11 @@ function getBrowserLanguage() {
}
}
const vuetify = createVuetify({
components,
directives,
});
store.dispatch('setLanguage', getBrowserLanguage());
const app = createApp(App);
@@ -21,6 +29,7 @@ const app = createApp(App);
app.use(store);
app.use(router);
app.use(i18n);
app.use(vuetify);
store.dispatch('loadLoginState');

View File

@@ -17,6 +17,7 @@ import DiaryView from '../views/social/DiaryView.vue';
import ForumAdminView from '../dialogues/admin/ForumAdminView.vue';
import ForumView from '../views/social/ForumView.vue';
import ForumTopicView from '../views/social/ForumTopicView.vue';
import FriendsView from '../views/social/FriendsView.vue';
const routes = [
{
@@ -29,6 +30,12 @@ const routes = [
name: 'Activate page',
component: ActivateView
},
{
path: '/friends',
name: 'Friends',
component: FriendsView,
meta: { requiresAuth: true }
},
{
path: '/socialnetwork/guestbook',
name: 'Guestbook',

View File

@@ -2,6 +2,8 @@ import { createStore } from 'vuex';
import dialogs from './modules/dialogs';
import loadMenu from '../utils/menuLoader.js';
import router from '../router';
import apiClient from '../utils/axios.js';
import { io } from 'socket.io-client';
const store = createStore({
state: {
@@ -9,6 +11,8 @@ const store = createStore({
user: null,
language: navigator.language.startsWith('de') ? 'de' : 'en',
menu: [],
socket: null,
menuNeedsUpdate: false,
},
mutations: {
async dologin(state, user) {
@@ -16,48 +20,74 @@ const store = createStore({
state.user = user;
localStorage.setItem('isLoggedIn', 'true');
localStorage.setItem('user', JSON.stringify(user));
state.menuNeedsUpdate = true;
if (user.param.filter(param => ['birthdate', 'gender'].includes(param.name)).length < 2) {
router.push({ path: '/settings/personal' });
}
},
dologout(state) {
async dologout(state) {
state.isLoggedIn = false;
state.user = null;
localStorage.removeItem('isLoggedIn');
localStorage.removeItem('user');
localStorage.removeItem('menu');
},
loadLoginState(state) {
const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
let userData = {};
try {
userData = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')) : {};
} catch (e) {
}
const menu = localStorage.getItem('menu');
const user = userData;
state.isLoggedIn = isLoggedIn;
state.user = user;
state.menu = menu;
state.menuNeedsUpdate = false;
// await apiClient.get('/api/auth/logout');
},
setLanguage(state, language) {
state.language = language;
},
setMenu(state, menu) {
state.menu = menu;
}
state.menuNeedsUpdate = false;
},
setSocket(state, socket) {
state.socket = socket;
},
clearSocket(state) {
state.socket = null;
},
},
actions: {
async login({ commit, dispatch }, user) {
async login({ commit, dispatch }, user) {
console.log('login', user);
await commit('dologin', user);
await dispatch('loadMenu');
await dispatch('initializeSocket');
const socket = this.getters.socket;
if (socket) {
console.log('Emitting setUserId:', user.id);
socket.emit('setUserId', user.id);
console.log('setUserId emitted successfully');
} else {
console.error('Socket not initialized');
}
await dispatch('loadMenu');
dispatch('startMenuReload');
},
logout({ commit }) {
logout({ commit, state }) {
if (state.socket) {
state.socket.disconnect();
commit('clearSocket');
}
commit('dologout');
router.push('/');
},
initializeSocket({ commit, state }) {
if (state.isLoggedIn && state.user) {
const socket = io(import.meta.env.VITE_API_BASE_URL); // oder Ihre URL
console.log('Socket initialized:', socket);
socket.on('connect', () => {
console.log('Socket connected:', socket.id);
});
socket.on('disconnect', (reason) => {
console.log('Socket disconnected:', reason);
});
commit('setSocket', socket);
}
},
loadLoginState({ commit }) {
commit('loadLoginState');
},
@@ -73,17 +103,13 @@ const store = createStore({
commit('setMenu', []);
}
},
startMenuReload({ dispatch }) {
setInterval(() => {
dispatch('loadMenu');
}, 10000);
},
},
getters: {
isLoggedIn: state => state.isLoggedIn,
user: state => state.user,
language: state => state.language,
menu: state => state.menu,
socket: state => state.socket,
},
modules: {
dialogs,

View File

@@ -2,7 +2,7 @@ import axios from 'axios';
import store from '../store';
const apiClient = axios.create({
baseURL: import.meta.env.VUE_APP_API_BASE_URL || 'http://localhost:3001',
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:3001',
headers: {
'Content-Type': 'application/json'
}
@@ -19,4 +19,13 @@ apiClient.interceptors.request.use(config => {
return Promise.reject(error);
});
apiClient.interceptors.response.use(response => {
return response;
}, error => {
if (error.response && error.response.status === 401) {
store.dispatch('logout');
}
return Promise.reject(error);
});
export default apiClient;

View File

@@ -0,0 +1,2 @@
import mitt from 'mitt';
export const EventBus = mitt();

View File

@@ -10,7 +10,6 @@ const loadMenu = async () => {
const response = await apiClient.get('/api/navigation/' + userId);
return response.data;
} catch (err) {
console.error(err);
return [];
}
};

View File

@@ -0,0 +1,208 @@
<template>
<div>
<h2>{{ $t('friends.title') }}</h2>
<div class="tabs-container">
<div class="tab" v-for="(tab, index) in tabs" :key="tab.name" :class="{ active: activeTab === index }"
@click="selectTab(index)">
{{ $t(tab.label) }}
</div>
</div>
<div v-for="(tab, index) in tabs" v-show="activeTab === index" :key="tab.name">
<v-data-table :items="paginatedData(tab.data, tab.pagination.page)" :headers="headers"
:items-per-page="tab.pagination.itemsPerPage" class="elevation-1">
<template v-slot:body="{ items }">
<tr v-for="(item, index) in items" :key="index">
<td :class="'font-color-gender-' + item.user.gender.toLowerCase().replace(' ', '')">
{{ item.user.username }}
</td>
<td>{{ item.user.age }}</td>
<td>{{ $t(`gender.${item.user.gender}`) }}</td>
<td>
<template v-if="tab.name === 'existingFriends'">
<v-btn color="error" @click="endFriendship(item.user.hashedId)">{{ $t('friends.actions.end')
}}</v-btn>
</template>
<template v-if="tab.name === 'pendingFriends'">
<v-btn color="success" @click="acceptFriendship(item.user.hashedId)">{{
$t('friends.actions.accept') }}</v-btn>
<v-btn color="error" @click="rejectFriendship(item.user.hashedId)">{{
$t('friends.actions.reject') }}</v-btn>
</template>
<template v-if="tab.name === 'requestedFriends'">
<v-btn color="warning" @click="withdrawRequest(item.user.hashedId)">{{
$t('friends.actions.withdraw') }}</v-btn>
</template>
<template v-if="tab.name === 'rejectedFriends'">
<v-btn color="success" @click="acceptFriendship(item.user.hashedId)">{{
$t('friends.actions.accept') }}</v-btn>
</template>
</td>
</tr>
</template>
</v-data-table>
<v-pagination v-model="tab.pagination.page"
:length="Math.ceil(tab.data.length / tab.pagination.itemsPerPage)" :total-visible="5"
class="mt-4"></v-pagination>
</div>
</div>
</template>
<script>
import { getFriendships, endFriendship, acceptFriendship, rejectFriendship, withdrawRequest } from "@/api/friendshipApi";
import { mapActions, mapGetters } from 'vuex';
export default {
name: "FriendsView",
data() {
return {
activeTab: 0,
headers: [
{ text: this.$t("friends.headers.name"), value: "name" },
{ text: this.$t("friends.headers.age"), value: "age" },
{ text: this.$t("friends.headers.gender"), value: "gender" },
{ text: this.$t("friends.headers.actions"), value: "actions", sortable: false },
],
tabs: [
{ name: "existingFriends", label: "friends.tabs.existing", data: [], pagination: { page: 1, itemsPerPage: 10 } },
{ name: "pendingFriends", label: "friends.tabs.pending", data: [], pagination: { page: 1, itemsPerPage: 10 } },
{ name: "requestedFriends", label: "friends.tabs.requested", data: [], pagination: { page: 1, itemsPerPage: 10 } },
{ name: "rejectedFriends", label: "friends.tabs.rejected", data: [], pagination: { page: 1, itemsPerPage: 10 } },
],
};
},
computed: {
...mapGetters(['socket', 'user']),
},
methods: {
async fetchFriendships() {
try {
const friendships = await getFriendships(false);
this.distributeFriendships(friendships);
} catch (error) {
console.error("Error fetching friendships:", error);
}
},
distributeFriendships(friendships) {
this.tabs.forEach(tab => (tab.data = []));
friendships.forEach(friendship => {
if (friendship.accepted) {
this.tabs.find(tab => tab.name === "existingFriends").data.push(friendship);
} else if (friendship.denied) {
const tabName = friendship.initiatorId === this.$store.state.user.hashedId
? "requestedFriends"
: "rejectedFriends";
this.tabs.find(tab => tab.name === tabName).data.push(friendship);
} else if (friendship.withdrawn) {
this.tabs.find(tab => tab.name === "requestedFriends").data.push(friendship);
} else {
const tabName = friendship.isInitiator
? "requestedFriends"
: "pendingFriends";
this.tabs.find(tab => tab.name === tabName).data.push(friendship);
}
});
},
selectTab(index) {
this.activeTab = index;
},
paginatedData(data, page) {
const start = (page - 1) * 10;
const end = start + 10;
return data.slice(start, end);
},
async endFriendship(friendUserId) {
try {
await endFriendship(friendUserId);
this.fetchFriendships();
} catch (error) {
console.error("Error ending friendship:", error);
}
},
async acceptFriendship(friendUserId) {
try {
await acceptFriendship(friendUserId);
this.fetchFriendships();
} catch (error) {
console.error("Error accepting friendship:", error);
}
},
async rejectFriendship(friendUserId) {
try {
await rejectFriendship(friendUserId);
this.fetchFriendships();
} catch (error) {
console.error("Error rejecting friendship:", error);
}
},
async withdrawRequest(friendUserId) {
try {
await withdrawRequest(friendUserId);
this.fetchFriendships();
} catch (error) {
console.error("Error withdrawing request:", error);
}
},
setupSocketListener() {
if (this.socket) {
console.log("Setting up friendshipChanged listener");
this.socket.on("friendshipChanged", (data) => {
console.log("Friendship changed:", data);
this.fetchFriendships();
});
} else {
console.error("Socket not initialized");
}
},
},
mounted() {
this.fetchFriendships();
this.setupSocketListener();
},
beforeUnmount() {
if (this.socket) {
this.socket.off("friendshipChanged");
}
},
};
</script>
<style scoped>
.tabs-container {
display: flex;
border-bottom: 1px solid #999;
padding: 5px 0;
}
.tab {
padding: 2px 4px;
text-align: center;
cursor: pointer;
background-color: #fff;
color: #333;
font-weight: bold;
border: 1px solid #999;
transition: background-color 0.3s ease, color 0.3s ease;
}
.tab:not(.active):hover {
background-color: #ddd;
}
.tab.active {
background-color: #F9A22C;
color: #7E471B;
border-color: #F9A22C;
}
.font-color-gender-male {
color: #1E90FF;
}
.font-color-gender-female {
color: #FF69B4;
}
.font-color-gender-nonbinary {
color: #DAA520;
}
</style>

View File

@@ -82,6 +82,7 @@ import apiClient from '@/utils/axios.js';
import Multiselect from 'vue-multiselect';
import FolderItem from '../../components/FolderItem.vue';
import 'vue-multiselect/dist/vue-multiselect.min.css';
import { EventBus } from '@/utils/eventBus.js';
export default {
components: {
@@ -107,6 +108,10 @@ export default {
if (this.folders) {
this.selectFolder(this.folders);
}
EventBus.on('folderCreated', this.loadFolders);
},
beforeUnmount() {
EventBus.off('folderCreated', this.loadFolders);
},
methods: {
async loadFolders() {

View File

@@ -23,5 +23,5 @@ export default defineConfig({
util: 'util',
assert: 'assert',
}
}
},
})