diff --git a/Dockerfile b/Dockerfile index 474988fe7a2eacbea6dcebdaaf59f6937902430c..a9dccf5ee222d7e84f87053d48867f5907bf4478 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,7 @@ FROM node:21-alpine +RUN apk update && apk add build-base g++ cairo-dev pango-dev giflib-dev + WORKDIR /app COPY package*.json ./ COPY .env ./ diff --git a/docker-compose.yml b/docker-compose.yml index 1c4033c9143cb80a2bbc4329320f9056803ca98b..b9a3f0b927c33ed82492d87a9594aaac9facc020 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,9 @@ version: 3.8 services: web: - build: . + build: + context: . + dockerfile: Dockerfile ports: - 3000:3000 restart: always diff --git a/package-lock.json b/package-lock.json index 59a2e42e3e19df57ac018ab45ec5f85d1a3578da..59fd83bf5aa83e036d824a358515ba833e1c5e44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1369,9 +1369,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz", - "integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.2.tgz", + "integrity": "sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==", "cpu": [ "arm" ], @@ -1383,9 +1383,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz", - "integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.2.tgz", + "integrity": "sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==", "cpu": [ "arm64" ], @@ -1397,9 +1397,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz", - "integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.2.tgz", + "integrity": "sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==", "cpu": [ "arm64" ], @@ -1411,9 +1411,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz", - "integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.2.tgz", + "integrity": "sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==", "cpu": [ "x64" ], @@ -1425,9 +1425,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz", - "integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.2.tgz", + "integrity": "sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==", "cpu": [ "arm" ], @@ -1439,9 +1439,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz", - "integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.2.tgz", + "integrity": "sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==", "cpu": [ "arm" ], @@ -1453,9 +1453,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz", - "integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.2.tgz", + "integrity": "sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==", "cpu": [ "arm64" ], @@ -1467,9 +1467,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz", - "integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.2.tgz", + "integrity": "sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==", "cpu": [ "arm64" ], @@ -1481,9 +1481,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz", - "integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.2.tgz", + "integrity": "sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==", "cpu": [ "ppc64" ], @@ -1495,9 +1495,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz", - "integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.2.tgz", + "integrity": "sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==", "cpu": [ "riscv64" ], @@ -1509,9 +1509,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz", - "integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.2.tgz", + "integrity": "sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==", "cpu": [ "s390x" ], @@ -1523,9 +1523,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz", - "integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz", + "integrity": "sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==", "cpu": [ "x64" ], @@ -1537,9 +1537,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz", - "integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.2.tgz", + "integrity": "sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==", "cpu": [ "x64" ], @@ -1551,9 +1551,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz", - "integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.2.tgz", + "integrity": "sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==", "cpu": [ "arm64" ], @@ -1565,9 +1565,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz", - "integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.2.tgz", + "integrity": "sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==", "cpu": [ "ia32" ], @@ -1579,9 +1579,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz", - "integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.2.tgz", + "integrity": "sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==", "cpu": [ "x64" ], @@ -1622,9 +1622,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.5.24", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.24.tgz", - "integrity": "sha512-Nr2oxsCsDfEkdS/zzQQQbsPYTbu692Qs3/iE3L7VHzCVjG2+WujF9oMUozWI7GuX98KxYSoPMlAsfmDLSg44hQ==", + "version": "2.5.25", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.25.tgz", + "integrity": "sha512-5hBSEN8XEjDZ5+2bHkFh8Z0QyOk0C187cyb12aANe1c8aeKbfu5ZD5XaC2vEH4h0alJFDXPdUkXQBmeeXeMr1A==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1655,9 +1655,9 @@ } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "4.0.0-next.6", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.0-next.6.tgz", - "integrity": "sha512-7+bEFN5F9pthG6nOEHNz9yioHxNXK6yl+0GnTy9WOfxN/SvPykkH/Hs6MqTGjo47a9G2q3QXQnzuxG5WXNX4Tg==", + "version": "4.0.0-next.7", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.0-next.7.tgz", + "integrity": "sha512-yMUnAqquoayvBDztk1rWUgdtvjv7YcHgopCAB7sWl9SQht8U/7lqwTlJU0ZTAY09pFFRe6bbakd7YoiyyIvJiA==", "dev": true, "license": "MIT", "dependencies": { @@ -1666,7 +1666,7 @@ "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.11", - "vitefu": "^0.2.5" + "vitefu": "^1.0.2" }, "engines": { "node": "^18.0.0 || ^20.0.0 || >=22" @@ -1711,9 +1711,9 @@ "license": "MIT" }, "node_modules/@types/eslint": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz", - "integrity": "sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", "dependencies": { @@ -1784,9 +1784,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", - "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", + "version": "22.5.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.3.tgz", + "integrity": "sha512-njripolh85IA9SQGTAqbmnNZTdxv7X/4OYGPz8tgy5JDr8MP+uDBa921GpYEoDDnwm0Hmn5ZPeJgiiSTPoOzkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1835,17 +1835,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz", - "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz", + "integrity": "sha512-rg8LGdv7ri3oAlenMACk9e+AR4wUV0yrrG+XKsGKOK0EVgeEDqurkXMPILG2836fW4ibokTB5v4b6Z9+GYQDEw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.2.0", - "@typescript-eslint/type-utils": "8.2.0", - "@typescript-eslint/utils": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/type-utils": "8.4.0", + "@typescript-eslint/utils": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1869,16 +1869,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz", - "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.4.0.tgz", + "integrity": "sha512-NHgWmKSgJk5K9N16GIhQ4jSobBoJwrmURaLErad0qlLjrpP5bECYg+wxVTGlGZmJbU03jj/dfnb6V9bw+5icsA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.2.0", - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/typescript-estree": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "debug": "^4.3.4" }, "engines": { @@ -1898,14 +1898,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz", - "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.4.0.tgz", + "integrity": "sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0" + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1916,14 +1916,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz", - "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.4.0.tgz", + "integrity": "sha512-pu2PAmNrl9KX6TtirVOrbLPLwDmASpZhK/XU7WvoKoCUkdtq9zF7qQ7gna0GBZFN0hci0vHaSusiL2WpsQk37A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.2.0", - "@typescript-eslint/utils": "8.2.0", + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/utils": "8.4.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1941,9 +1941,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz", - "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.4.0.tgz", + "integrity": "sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==", "dev": true, "license": "MIT", "engines": { @@ -1955,16 +1955,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz", - "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.4.0.tgz", + "integrity": "sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", @@ -1984,16 +1984,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz", - "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.4.0.tgz", + "integrity": "sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.2.0", - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/typescript-estree": "8.2.0" + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2007,13 +2007,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz", - "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.4.0.tgz", + "integrity": "sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/types": "8.4.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2142,9 +2142,9 @@ } }, "node_modules/apexcharts": { - "version": "3.52.0", - "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.52.0.tgz", - "integrity": "sha512-7dg0ADKs8AA89iYMZMe2sFDG0XK5PfqllKV9N+i3hKHm3vEtdhwz8AlXGm+/b0nJ6jKiaXsqci5LfVxNhtB+dA==", + "version": "3.53.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.53.0.tgz", + "integrity": "sha512-QESZHZY3w9LPQ64PGh1gEdfjYjJ5Jp+Dfy0D/CLjsLOPTpXzdxwlNMqRj+vPbTcP0nAHgjWv1maDqcEq6u5olw==", "license": "MIT", "dependencies": { "@yr/monotone-cubic-spline": "^1.0.3", @@ -2215,16 +2215,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", @@ -2460,9 +2450,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001653", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz", - "integrity": "sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==", + "version": "1.0.30001655", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", + "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", "dev": true, "funding": [ { @@ -2859,19 +2849,6 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "license": "Apache-2.0" }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -3096,9 +3073,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -3589,15 +3566,15 @@ } }, "node_modules/flowbite-svelte": { - "version": "0.46.15", - "resolved": "https://registry.npmjs.org/flowbite-svelte/-/flowbite-svelte-0.46.15.tgz", - "integrity": "sha512-xWhyLDez/gafTAmQayPMPKmWj1BAoq80SoA48yHZ12Wk3Vu3hFrELLUZf0UksxnjQL9hMOKRUlYl3/IH6pHwnQ==", + "version": "0.46.16", + "resolved": "https://registry.npmjs.org/flowbite-svelte/-/flowbite-svelte-0.46.16.tgz", + "integrity": "sha512-NkyMS/d1EwuL1cqstSUflnG9vhhBiNyUiAw51D8lfPKDfUG1iXc4+HueQw01zhHv3uSXRJRToFBrg6npxeJ3jw==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.6.7", - "apexcharts": "^3.49.2", - "flowbite": "^2.4.1", - "tailwind-merge": "^2.3.0" + "@floating-ui/dom": "^1.6.10", + "apexcharts": "^3.53.0", + "flowbite": "^2.5.1", + "tailwind-merge": "^2.5.2" }, "engines": { "node": ">=18.0.0", @@ -3898,27 +3875,6 @@ "dev": true, "license": "MIT" }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globrex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", @@ -4570,9 +4526,9 @@ } }, "node_modules/jose": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.7.0.tgz", - "integrity": "sha512-3P9qfTYDVnNn642LCAqIKbTGb9a1TBxZ9ti5zEVEr48aDdflgRjhspWFb6WM4PzAfFbGMJYC4+803v8riCRAKw==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.8.0.tgz", + "integrity": "sha512-E7CqYpL/t7MMnfGnK/eg416OsFCVUrU/Y3Vwe7QjKhu/BkS1Ms455+2xsqZQVN57/U2MHMBvEb5SrmAZWAIntA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -5067,9 +5023,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "6.9.14", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.14.tgz", - "integrity": "sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==", + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.15.tgz", + "integrity": "sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -5347,9 +5303,9 @@ } }, "node_modules/oauth4webapi": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.12.0.tgz", - "integrity": "sha512-WFmcHzhFtq2Ar91crpGQZUD8DS0SG7Zti1AgbansUAfdpIsoRXE+hcMNi8MW6bGNNObWis0x8BZRl6K+FR4oQg==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.12.1.tgz", + "integrity": "sha512-7lgyz74z5NJ1dRbZSnL/YCJt7UMhNOqQ8I6Jokh/Et66GVK6KfFikzU98i5PTbgbrsSmxzsQprJdI3Z+5Uwj2Q==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -5558,13 +5514,16 @@ } }, "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/path2d": { @@ -5578,9 +5537,9 @@ } }, "node_modules/pdfjs-dist": { - "version": "4.5.136", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.5.136.tgz", - "integrity": "sha512-V1BALcAN/FmxBEShLxoP73PlQZAZtzlaNfRbRhJrKvXzjLC5VaIlBAQUJuWP8iaYUmIdmdLHmt3E2TBglxOm3w==", + "version": "4.6.82", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.6.82.tgz", + "integrity": "sha512-BUOryeRFwvbLe0lOU6NhkJNuVQUp06WxlJVVCsxdmJ4y5cU3O3s3/0DunVdK1PMm7v2MUw52qKYaidhDH1Z9+w==", "license": "Apache-2.0", "engines": { "node": ">=18" @@ -5591,9 +5550,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "license": "ISC" }, "node_modules/picomatch": { @@ -5651,9 +5610,9 @@ } }, "node_modules/postcss": { - "version": "8.4.41", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", - "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "version": "8.4.45", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", + "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", "funding": [ { "type": "opencollective", @@ -5968,19 +5927,6 @@ "node": ">=4" } }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -6123,9 +6069,9 @@ } }, "node_modules/rollup": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz", - "integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.2.tgz", + "integrity": "sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==", "dev": true, "license": "MIT", "dependencies": { @@ -6139,22 +6085,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.0", - "@rollup/rollup-android-arm64": "4.21.0", - "@rollup/rollup-darwin-arm64": "4.21.0", - "@rollup/rollup-darwin-x64": "4.21.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.0", - "@rollup/rollup-linux-arm-musleabihf": "4.21.0", - "@rollup/rollup-linux-arm64-gnu": "4.21.0", - "@rollup/rollup-linux-arm64-musl": "4.21.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.0", - "@rollup/rollup-linux-riscv64-gnu": "4.21.0", - "@rollup/rollup-linux-s390x-gnu": "4.21.0", - "@rollup/rollup-linux-x64-gnu": "4.21.0", - "@rollup/rollup-linux-x64-musl": "4.21.0", - "@rollup/rollup-win32-arm64-msvc": "4.21.0", - "@rollup/rollup-win32-ia32-msvc": "4.21.0", - "@rollup/rollup-win32-x64-msvc": "4.21.0", + "@rollup/rollup-android-arm-eabi": "4.21.2", + "@rollup/rollup-android-arm64": "4.21.2", + "@rollup/rollup-darwin-arm64": "4.21.2", + "@rollup/rollup-darwin-x64": "4.21.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.2", + "@rollup/rollup-linux-arm-musleabihf": "4.21.2", + "@rollup/rollup-linux-arm64-gnu": "4.21.2", + "@rollup/rollup-linux-arm64-musl": "4.21.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.2", + "@rollup/rollup-linux-riscv64-gnu": "4.21.2", + "@rollup/rollup-linux-s390x-gnu": "4.21.2", + "@rollup/rollup-linux-x64-gnu": "4.21.2", + "@rollup/rollup-linux-x64-musl": "4.21.2", + "@rollup/rollup-win32-arm64-msvc": "4.21.2", + "@rollup/rollup-win32-ia32-msvc": "4.21.2", + "@rollup/rollup-win32-x64-msvc": "4.21.2", "fsevents": "~2.3.2" } }, @@ -6544,16 +6490,6 @@ "node": ">= 10" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/sorcery": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.1.tgz", @@ -6869,24 +6805,24 @@ } }, "node_modules/svelte": { - "version": "5.0.0-next.238", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.0.0-next.238.tgz", - "integrity": "sha512-fCPNBqQA/MYadkI58LOV3kpYHP5zOsMSjjPagc63Z5LXRkdZe/TKOJbVtLK9Gp4+Shf3qTaxBCp/BR1845Rd/A==", + "version": "5.0.0-next.243", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.0.0-next.243.tgz", + "integrity": "sha512-+oXjRInUyBfZXAEY8hmpf3F0eghAVCoWasotz1iOp2G5CyH4KR7jPxWOgjbgsgpL4zlMiN32MEYU1+I+QsC+nQ==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", + "@ampproject/remapping": "^2.3.0", + "@jridgewell/sourcemap-codec": "^1.5.0", "@types/estree": "^1.0.5", - "acorn": "^8.11.3", + "acorn": "^8.12.1", "acorn-typescript": "^1.4.13", "aria-query": "^5.3.0", - "axobject-query": "^4.0.0", + "axobject-query": "^4.1.0", "esm-env": "^1.0.0", "esrap": "^1.2.2", "is-reference": "^3.0.2", "locate-character": "^3.0.0", - "magic-string": "^0.30.5", + "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" }, "engines": { @@ -7245,9 +7181,9 @@ } }, "node_modules/tailwindcss/node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -7594,14 +7530,14 @@ } }, "node_modules/vite": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", - "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz", + "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.41", + "postcss": "^8.4.43", "rollup": "^4.20.0" }, "bin": { @@ -7654,11 +7590,15 @@ } }, "node_modules/vitefu": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", - "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.2.tgz", + "integrity": "sha512-0/iAvbXyM3RiPPJ4lyD4w6Mjgtf4ejTK6TPvTNG3H32PLwuT0N/ZjJLiXug7ETE/LWtTeHw9WRv7uX/tIKYyKg==", "dev": true, "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*" + ], "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, diff --git a/src/lib/components/MailInput.svelte b/src/lib/components/MailInput.svelte index 0fd79d3f18380f9c929c60bb14e4a20a253b3436..66992a3739fed0f0c7a5a94cfa2eb926a3424056 100644 --- a/src/lib/components/MailInput.svelte +++ b/src/lib/components/MailInput.svelte @@ -83,8 +83,8 @@ <Input name="replyTo" bind:value={replyTo} placeholder="Antwortadresse" class="w-full" {disabled} /> {:else} <Select name="replyTo" value={replyTo} on:change={e=>{ - customReplyTo = e.target!.value==="custom"; - if(!customReplyTo) replyTo = e.target!.value; + customReplyTo = (e.target! as HTMLSelectElement).value==="custom"; + if(!customReplyTo) replyTo = (e.target! as HTMLSelectElement).value; else if(replyTo === "-") replyTo = ""; }} {disabled}> <option value={"-"}>keine</option> @@ -101,7 +101,7 @@ {#each locales as locale} <Label class="w-full"> Betreff ({locale}) - <Input name="subject[{locale}]" bind:value={subject[locale]} oninput={e=>subject=Object.assign(subject,{[locale]:e.target.value})} {disabled} required={locale==="de"} /> + <Input name="subject[{locale}]" bind:value={subject[locale]} on:input={e=>subject=Object.assign(subject,{[locale]:(e.target as HTMLInputElement)!.value})} {disabled} required={locale==="de"} /> </Label> {/each} </div> @@ -130,7 +130,7 @@ <Span slot="header">Beispiel-Tutor</Span> <Label> Studiengang - <Select value={exampleTutor.studyProgram.id} on:change={e=>exampleTutor.studyProgram=studyPrograms.find(s=>s.id==e.target!.value)}> + <Select value={exampleTutor.studyProgram.id} on:change={e=>exampleTutor.studyProgram=(studyPrograms.find(s=>s.id==parseInt((e.target! as HTMLSelectElement).value))!)}> {#each studyPrograms as studyProgram} <option value={studyProgram.id}>{studyProgram.name[$locale]}</option> {/each} @@ -160,7 +160,7 @@ </Label> <Label> Schulung - <Select value={exampleTutor.training?.id ?? -1} on:change={e=>exampleTutor.training=trainings.find(t=>t.id===e.target!.value)}> + <Select value={exampleTutor.training?.id ?? -1} on:change={e=>exampleTutor.training=trainings.find(t=>t.id===(e.target as HTMLSelectElement)!.value)}> <option value={-1}>keine</option> {#each trainings as training} <option value={training.id}>{training.date}</option> @@ -182,6 +182,7 @@ </AccordionItem> <AccordionItem> <Span slot="header">Formatierer</Span> + <P class="mb-2">Formatierer können mit Argumenten erstellt werden. Dafür nutzt man <code>formatierer:argument</code>, wobei das Argument gültiges JSON sein muss. Es muss darauf geachtet werden, dass sich kein <code>{"}}"}</code> bildet.</P> <List tag="ul" position="outside" class="ml-4"> {#each Object.entries(formatters) as [name, {description}]} <Li><code>{name}</code> - {description}</Li> @@ -191,7 +192,7 @@ <AccordionItem> <Span slot="header">Bedingungen</Span> <List tag="ul" position="outside" class="ml-4"> - {#each conditions as condition} + {#each conditions[type] as condition} <Li> <code>{"{{if "}{condition.name}{#if condition.arguments}{" ...args"}{/if}{"}}"}</code> - {condition.description} {#if condition.arguments} @@ -216,6 +217,7 @@ <b>Formatierung</b> <List tag="ul" position="outside" class="ml-6"> <Li>Werte von Variablen können formatiert werden, indem die Formatierer getrennt durch <code>|</code> nach dem Variablennamen gelistet werden, z.B. <code>{"{{"}variablenname|formatierer1|formatierer2{"}}"}</code>. Die Formatierer werden in der angegebenen Reihenfolge angewandt.</Li> + <Li>Unterstützt ein Formatierer Konstruktorenargumente, so können diese mit <code>:</code> getrennt hinter dem Namen angegeben werden. Das Argument muss gültiges JSON sein und kein frühzeitiges {"}}"} bilden. Beispiel: <code>{"{{"}argument|date:{"{"}"year":"2-digit"{"} "}{"}}"}</code>, wobei das Leerzeichen am Ende wichtig ist, weil sich sonst {"}}}"} bilden würde.</Li> </List> </Li> <Li> diff --git a/src/lib/components/SortableTable.svelte b/src/lib/components/SortableTable.svelte index 074766cb3fb4dbef4afc7f3d4397306ed98ea7fa..ee913483e6f87dbf83eeb682f3c8d74bd27f1975 100644 --- a/src/lib/components/SortableTable.svelte +++ b/src/lib/components/SortableTable.svelte @@ -11,28 +11,10 @@ } & Record<string, any>; let { items, row, children, ...restProps }: ComponentProps = $props(); - const sorting = writable({sorted: items, sortDirection: 1, sorter: null}); + const sorting = writable({sorted: items, sortDirection: 1, sorter: ""}); setContext("sorting", sorting); </script> -<style> - div :global(.sortable) { - cursor: pointer; - position: relative; - } - div :global(.sortable::after) { - content: ""; - position: absolute; - padding-left: .7rem; - } - div :global(.sorting-asc::after) { - content: "▲"; - } - div :global(.sorting-desc::after) { - content: "▼"; - } -</style> - <div> <Table {...restProps}> {@render children()} diff --git a/src/lib/components/SortableTableHeadCell.svelte b/src/lib/components/SortableTableHeadCell.svelte index 08cffc0f7f03967731f94c0c97891d5ec608b946..98df83f375f12b664bfff09b58cb9f328c14a73e 100644 --- a/src/lib/components/SortableTableHeadCell.svelte +++ b/src/lib/components/SortableTableHeadCell.svelte @@ -5,39 +5,30 @@ import { twMerge } from "tailwind-merge"; type T = $$Generic; - const sorting = getContext("sorting") as Writable<{sorted: T[], sortDirection: 1|-1, sorter?: TableHeadCell}>; + const sorting = getContext("sorting") as Writable<{sorted: T[], sortDirection: 1|-1, sorter: string}>; - let { sort, children, padding="px-6 py-3", defaultDirection="asc", default: def }: { sort: (a: T, b: T)=>number, children: Snippet, padding?: string, defaultDirection?: "asc"|"desc", default?: boolean } = $props(); - let self: TableHeadCell|undefined = $state(); + let { sort, children, padding="px-6 py-3", defaultDirection="asc", default: def, buttonClass="" }: { sort: (a: T, b: T)=>number, children: Snippet, padding?: string, defaultDirection?: "asc"|"desc", default?: boolean, buttonClass?: string } = $props(); + const self = Math.random().toString(36).substring(2); if(def){ - // update directly for SSR sorting.update(({sorted}) => { let dir = (defaultDirection === "asc" ? 1 : -1) as 1 | -1; return {sorted: sorted.sort((a,b)=>dir*sort(a,b)), sortDirection: dir, sorter: self}; }); - // update on initialization afte "self" has been initialized - onMount(()=>{ - sorting.update(({sorted}) => { - let dir = (defaultDirection === "asc" ? 1 : -1) as 1 | -1; - return {sorted: sorted.sort((a,b)=>dir*sort(a,b)), sortDirection: dir, sorter: self}; - }); - }); } function onclick(){ sorting.update(({sorted, sortDirection, sorter}) => { - if(sorter === self){ - return {sorted: sorted.sort((a,b)=>-sortDirection*sort(a,b)), sortDirection: -sortDirection as 1 | -1, sorter}; - } - let dir = (defaultDirection === "asc" ? 1 : -1) as 1 | -1; - return {sorted: sorted.sort((a,b)=>dir*sort(a,b)), sortDirection: dir, sorter: self}; + sortDirection = (sorter === self ? -sortDirection : defaultDirection === "asc" ? 1 : -1) as 1 | -1; + return {sorted: sorted.sort((a,b)=>sortDirection*sort(a,b)), sortDirection, sorter: self}; }); } + + let sortDirection = $derived($sorting.sorter === self ? $sorting.sortDirection === 1 ? "asc" : "desc" : null); </script> -<TableHeadCell bind:this={self} padding=""> - <button {onclick} class={twMerge(padding, "sortable w-full text-left", $sorting.sorter === self && `sorting-${$sorting.sortDirection === 1 ? "asc": "desc"}`)}> +<TableHeadCell padding="" aria-sort={sortDirection ? `${sortDirection}ending` : undefined}> + <button {onclick} class={twMerge(padding, "w-full text-left hover:bg-black hover:bg-opacity-10 relative after:absolute after:pl-3", sortDirection === "asc" && "after:content-['▲']", sortDirection === "desc" && "after:content-['▼']", buttonClass)}> {@render children()} </button> </TableHeadCell> diff --git a/src/lib/mail.ts b/src/lib/mail.ts index fd0fea3f9070266388cd89b8568793bd9c9592ea..07782fd104ebccb2e197e7938b391d0317f1e0ae 100644 --- a/src/lib/mail.ts +++ b/src/lib/mail.ts @@ -5,6 +5,7 @@ import type { Tutor } from "./server/database/entities/Tutor.entity"; import markdownit from "markdown-it"; import type { Localized } from "./utils"; import type { Config } from "./server/database/entities/Config.entity"; +import type { DateTimeFormatOptions } from "intl"; const md = markdownit({ breaks: true, @@ -21,7 +22,11 @@ export type MailTemplate = { type: TemplateType; }; -export const formatters: Record<string, { description: string, format: (l: Locale, arg: unknown)=>string }> = { +export const formatters: Record<string, { description: string, format: (l: Locale, arg: unknown, constructorArg: unknown|null)=>string }> = { + date: { + description: "Formatiert ein Datum, Kontruktorargumente sind die Optionen für Intl.DateTimeFormat (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options)", + format: (locale, arg, constructorArg)=>new Intl.DateTimeFormat(locale, constructorArg as DateTimeFormatOptions ?? undefined).format(arg as Date), + }, dateLong: { description: "Formatiert ein Datum, z.B. 01. Januar 1970", format: (locale, arg)=>new Intl.DateTimeFormat(locale, { year: "numeric", month: "long", day: "2-digit" }).format(arg as Date), @@ -38,6 +43,10 @@ export const formatters: Record<string, { description: string, format: (l: Local description: "Gibt den Wochentag eines Datums zurück, z.B. Mo", format: (locale, arg)=>new Intl.DateTimeFormat(locale, { weekday: "short" }).format(arg as Date), }, + dateRange: { + description: "Formatiert einen Datumsbereich, Konsruktorargumente sind die Optionen für Intl.DateTimeFormat (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options)", + format: (locale, arg, constructorArg)=>new Intl.DateTimeFormat(locale, constructorArg as DateTimeFormatOptions ?? undefined).formatRange((arg as [Date, Date])[0], (arg as [Date, Date])[1]), + }, dateRangeLong: { description: "Formatiert einen Datumsbereich, z.B. 1. - 2. Januar 1970", format: (locale, arg)=>new Intl.DateTimeFormat(locale, { year: "numeric", month: "long", day: "2-digit" }).formatRange((arg as [Date, Date])[0], (arg as [Date, Date])[1]), @@ -375,10 +384,15 @@ export function parseTemplate(type: TemplateType, template: Localized|string){ const [variableName, ...formatterNames] = name.split("|"); const variable = variables[type].find(v=>v.name === variableName); if(!variable) throw new Error(`Unknown variable ${name}`); - const fs = formatterNames.map(f=>formatters[f]); - if(fs.some(f=>!f)) throw new Error(`Unknown formatter ${formatterNames.find((_, i)=>!fs[i])}`); + const fs = formatterNames.map(f=>{ + const [formatterName, ...formatterArgs] = f.split(":"); + const formatter = formatters[formatterName]; + if(!formatter) throw new Error(`Unknown formatter ${formatterName}`); + const arg = formatterArgs.join(":"); + return { format: formatter.format, arg: arg ? JSON.parse(arg) : null }; + }); const replacement = (t: Tutor, l: Locale, c: PartialConfig)=>{ - return fs.reduce((v, f)=>f.format(l, v), variable.replacement(t, l, c)); + return fs.reduce((v, f)=>f.format(l, v, f.arg), variable.replacement(t, l, c)); }; parts.push({type: "variable", replacement}); } @@ -425,10 +439,15 @@ function consumeCondition(type: TemplateType, tokens: Token[]): [Part[], Part[]] const [variableName, ...formatterNames] = name.split("|"); const variable = variables[type].find(v=>v.name === variableName); if(!variable) throw new Error(`Unknown variable ${name}`); - const fs = formatterNames.map(f=>formatters[f]); - if(fs.some(f=>!f)) throw new Error(`Unknown formatter ${formatterNames.find((_, i)=>!fs[i])}`); + const fs = formatterNames.map(f=>{ + const [formatterName, ...formatterArgs] = f.split(":"); + const formatter = formatters[formatterName]; + if(!formatter) throw new Error(`Unknown formatter ${formatterName}`); + const arg = formatterArgs.join(":"); + return { format: formatter.format, arg: arg ? JSON.parse(arg) : null }; + }); const replacement = (t: Tutor, l: Locale, c: PartialConfig)=>{ - return fs.reduce((v, f)=>f.format(l, v), variable.replacement(t, l, c)); + return fs.reduce((v, f)=>f.format(l, v, f.arg), variable.replacement(t, l, c)); }; if(inElse) elseParts.push({type: "variable", replacement}); else thenParts.push({type: "variable", replacement}); diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 0c5c679074282f7098a8d6872e0f9b39be6467c0..c36770bd3a68ec544624e865619a31cb509aa1d4 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -96,13 +96,13 @@ export const handleAuthorization: Handle = async ({event, resolve})=>{ event.locals.user = user; }else if(event.url.pathname.startsWith("/intern/tutor/") || event.url.pathname === "/intern/tutor"){ const session = await event.locals.auth(); - if(!session || session.user.type !== "tutor") redirect(303, "/intern#tutor"); + if(!session || session.user.type !== "tutor") redirect(303, `/intern?redirect_url=${encodeURIComponent(event.url.href)}#tutor`); const tutor = await Tutor.getById(session.user.userId); if(!tutor) redirect(303, "/intern"); event.locals.tutor = tutor; }else if(event.url.pathname.startsWith("/intern/rallye/") || event.url.pathname === "/intern/rallye"){ const session = await event.locals.auth(); - if(!session || session.user.type !== "rally") redirect(303, "/intern#rallye"); + if(!session || session.user.type !== "rally") redirect(303, `/intern?redirect_url=${encodeURIComponent(event.url.href)}#rallye`); const supervisor = await RallyStationSupervisor.getById(session.user.userId); if(!supervisor) redirect(303, "/intern"); event.locals.supervisor = supervisor; diff --git a/src/lib/server/database/migrations/0_init.sql b/src/lib/server/database/migrations/0_init.sql index fee540882a3c0b91633f4a329f72161df3165e08..819bc3eb717fff48fec492ef504ce4a7f2c92746 100644 --- a/src/lib/server/database/migrations/0_init.sql +++ b/src/lib/server/database/migrations/0_init.sql @@ -177,3 +177,20 @@ CREATE TABLE IF NOT EXISTS master_freshers ( "study_program" TEXT NOT NULL, "aachen_experience" TEXT NOT NULL ); + +CREATE TABLE IF NOT EXISTS mrx ( + "id" SERIAL PRIMARY KEY +); + +CREATE TABLE IF NOT EXISTS mrx_entries ( + "id" SERIAL PRIMARY KEY, + "mrx" INT NOT NULL REFERENCES mrx(id), + "date" DATE NOT NULL, + "text" TEXT NOT NULL, +); + +CREATE TABLE IF NOT EXISTS mrx_attachments ( + "id" SERIAL PRIMARY KEY, + "mrx_entry" INT NOT NULL REFERENCES mrx_entries(id), + "path" TEXT NOT NULL, +); diff --git a/src/routes/(non-admin)/mr-x/+page.server.ts b/src/routes/(non-admin)/mr-x/+page.server.ts new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/routes/(non-admin)/mr-x/+page.svelte b/src/routes/(non-admin)/mr-x/+page.svelte new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/routes/admin/rabatte/[id=number]/+page.svelte b/src/routes/admin/rabatte/[id=number]/+page.svelte index ecf70bcbf8456e7371a0a489fdbb19e8b5908209..0c8255e44a4aff815b425c54a227936fe34ccf54 100644 --- a/src/routes/admin/rabatte/[id=number]/+page.svelte +++ b/src/routes/admin/rabatte/[id=number]/+page.svelte @@ -51,8 +51,6 @@ function roundTo5Digits(num: number): number{ return Math.round(num * 1e5) / 1e5; } - function refocusMap(address: string): void; - function refocusMap(latitude: number, longitude: number): void; async function refocusMap(){ if(arguments.length === 1){ const [address] = arguments; diff --git a/src/routes/admin/tutor/+page.svelte b/src/routes/admin/tutor/+page.svelte index 18206e1e69a287886ce4e9ac020b03fa3f215f2c..09d9dd43b5ac74c0a70a085177e61ca01862ff24 100644 --- a/src/routes/admin/tutor/+page.svelte +++ b/src/routes/admin/tutor/+page.svelte @@ -5,66 +5,35 @@ import { Permission } from "$lib/perms.js"; import { toCSV } from "$lib/csv"; import type { Tutor } from "$lib/server/database/entities/Tutor.entity"; + import SortableTable from "$lib/components/SortableTable.svelte"; + import SortableTableHeadCell from "$lib/components/SortableTableHeadCell.svelte"; export let data; - let sortedTutors = data.tutors; - $: dateFormatter = new Intl.DateTimeFormat($locale, { day: "2-digit", month: "2-digit", year: "numeric" }); - type SortProp = string|number|null|undefined; - type SortKey = ObjectToDotProp<typeof data.tutors[number], SortProp>; - let sortKey: SortKey = "firstname"; - let sortDirection: -1|1 = 1; - const sortTable = (key: SortKey) => { - if (sortKey === key) { - sortDirection *= -1; - } else { - sortKey = key; - sortDirection = 1; - } - } - $: { - sortedTutors = [...sortedTutors].sort((a, b) => { - const aVal = sortKey!.split(".").reduce((val, key) => val?.[key], a) as unknown as SortProp; - const bVal = sortKey!.split(".").reduce((val, key) => val?.[key], b) as unknown as SortProp; - if(aVal == null && bVal == null) return 0; - else if(aVal == null) return sortDirection; - else if(bVal == null) return -sortDirection; - else if(aVal < bVal) return -sortDirection; - else if(aVal > bVal) return sortDirection; - else return 0; - }); - } - - function getClassName(key: SortKey, sortKey: SortKey, sortDirection: -1|1){ - if(sortKey === key){ - return sortDirection === 1 ? "thc sorting-asc" : "thc sorting-desc"; - } - return "thc"; - } let showExportModal = false; let exportType = "json"; - const fields: { prop: ObjectToDotProp<Tutor>, label: string, value: string }[] = [ - { prop: "firstname", label: "Vorname", value: "firstname" }, - { prop: "lastname", label: "Nachname", value: "lastname" }, - { prop: "nickname", label: "Spitzname", value: "nickname" }, - { prop: "studyProgram.name.de", label: "Fach", value: "studyProgram" }, - { prop: "training.date", label: "Schulung", value: "training" }, - { prop: "notes", label: "Notiz", value: "notes" }, + const fields: { prop: (t: Tutor)=>unknown, label: string, value: string }[] = [ + { prop: t=>t.firstname, label: "Vorname", value: "firstname" }, + { prop: t=>t.lastname, label: "Nachname", value: "lastname" }, + { prop: t=>t.nickname, label: "Spitzname", value: "nickname" }, + { prop: t=>t.studyProgram.name.de, label: "Fach", value: "studyProgram" }, + { prop: t=>t.training?.date, label: "Schulung", value: "training" }, + { prop: t=>t.notes, label: "Notiz", value: "notes" }, ...(data.user.permissions.has(Permission.VIEW_TUTOR_DETAILS) ? [ - { prop: "gender", label: "Geschlecht", value: "gender" }, - { prop: "mentor", label: "Mentor", value: "mentor" }, - { prop: "shirtSize", label: "Shirt", value: "shirtSize" }, - { prop: "coTutorWish", label: "Co-Tutor", value: "coTutorWish" }, - { prop: "trained", label: "Geschult", value: "trained" }, - { prop: "degree", label: "Abschluss", value: "degree" }, - { prop: "email", label: "E-Mail", value: "email" }, - { prop: "phone", label: "Telefon", value: "phone" }, - { prop: "address", label: "Adresse", value: "address" }, - { prop: "birthday", label: "Geburtstag", value: "birthday" }, - { prop: "dietaryRestriction", label: "Essgewohnheiten", value: "dietaryRestriction" }, - ] satisfies {prop: ObjectToDotProp<Tutor>, label: string, value: string}[] : []), + { prop: t=>t.gender, label: "Geschlecht", value: "gender" }, + { prop: t=>t.mentor, label: "Mentor", value: "mentor" }, + { prop: t=>t.shirtSize, label: "Shirt", value: "shirtSize" }, + { prop: t=>t.coTutorWish, label: "Co-Tutor", value: "coTutorWish" }, + { prop: t=>t.trained, label: "Geschult", value: "trained" }, + { prop: t=>t.degree, label: "Abschluss", value: "degree" }, + { prop: t=>t.email, label: "E-Mail", value: "email" }, + { prop: t=>t.phone, label: "Telefon", value: "phone" }, + { prop: t=>t.address, label: "Adresse", value: "address" }, + { prop: t=>t.birthday, label: "Geburtstag", value: "birthday" }, + { prop: t=>t.dietaryRestriction, label: "Essgewohnheiten", value: "dietaryRestriction" }, + ] satisfies { prop: (t: Tutor)=>unknown, label: string, value: string }[] : []), ]; let selectedFields: string[] = fields.map(f => f.value); function exportTutors(){ @@ -72,7 +41,7 @@ const tutor: Record<string, unknown> = {}; for(const field of fields){ if(selectedFields.includes(field.value)){ - tutor[field.value] = field.prop.split(".").reduce((val, key) => val?.[key], t); + tutor[field.value] = field.prop(t); } } return tutor; @@ -96,25 +65,6 @@ } </script> -<style> - div :global(.thc) { - cursor: pointer; - position: relative; - } - div :global(.thc::after) { - content: ""; - position: absolute; - right: 0; - padding-right: 1rem; - } - div :global(.sorting-asc::after) { - content: " ▲"; - } - div :global(.sorting-desc::after) { - content: " ▼"; - } -</style> - <Breadcrumb class="mb-4"> <BreadcrumbItem href="/admin" home>Admin</BreadcrumbItem> <BreadcrumbItem href="/admin/tutor">Tutoren</BreadcrumbItem> @@ -200,34 +150,39 @@ </Table> {#if data.user.permissions.has(Permission.VIEW_TUTORS)} -<Table class="mb-6" hoverable> +<SortableTable class="mb-6" hoverable items={data.tutors}> <TableHead> - <TableHeadCell on:click={()=>sortTable("firstname")} class={getClassName("firstname", sortKey, sortDirection)}>Vorname</TableHeadCell> - <TableHeadCell on:click={()=>sortTable("lastname")} class={getClassName("lastname", sortKey, sortDirection)}>Nachname</TableHeadCell> - <TableHeadCell on:click={()=>sortTable(`studyProgram.name.${$locale}`)} class={getClassName(`studyProgram.name.${$locale}`, sortKey, sortDirection)}>Fach</TableHeadCell> - <TableHeadCell on:click={()=>sortTable("shirtSize")} class={getClassName("shirtSize", sortKey, sortDirection)}>Shirt</TableHeadCell> - <TableHeadCell on:click={()=>sortTable("coTutorWish")} class={getClassName("coTutorWish", sortKey, sortDirection)}>Co-Tutor</TableHeadCell> - <TableHeadCell on:click={()=>sortTable("training.date")} class={getClassName("training.date", sortKey, sortDirection)}>Schulung</TableHeadCell> - <TableHeadCell on:click={()=>sortTable("notes")} class={getClassName("notes", sortKey, sortDirection)}>Notiz</TableHeadCell> + <SortableTableHeadCell sort={(a: Tutor, b)=>a.firstname.localeCompare(b.firstname)}>Vorname</SortableTableHeadCell> + <SortableTableHeadCell sort={(a: Tutor, b)=>a.lastname.localeCompare(b.lastname)}>Nachname</SortableTableHeadCell> + <SortableTableHeadCell sort={(a: Tutor, b)=>a.studyProgram.name[$locale].localeCompare(b.studyProgram.name[$locale])}>Fach</SortableTableHeadCell> + <SortableTableHeadCell sort={(a: Tutor, b)=>a.shirtSize.localeCompare(b.shirtSize)}>T-Shirt</SortableTableHeadCell> + <SortableTableHeadCell sort={(a: Tutor, b)=>a.coTutorWish.localeCompare(b.coTutorWish)}>Co-Tutor</SortableTableHeadCell> + <SortableTableHeadCell sort={(a: Tutor, b)=>{ + if(a.training && b.training) return a.training.date.localeCompare(b.training.date); + else if(a.training) return -1; + else if(b.training) return 1; + else return 0; + }}>Schulung</SortableTableHeadCell> + <SortableTableHeadCell sort={(a: Tutor, b)=>a.notes.localeCompare(b.notes)}>Notiz</SortableTableHeadCell> + {#if data.user.permissions.has(Permission.UPDATE_TUTOR)} <TableHeadCell></TableHeadCell> + {/if} </TableHead> - <TableBody> - {#each sortedTutors as tutor} - <TableBodyRow> - <TableBodyCell>{tutor.firstname}{tutor.nickname?` (${tutor.nickname})`:""}</TableBodyCell> - <TableBodyCell>{tutor.lastname}</TableBodyCell> - <TableBodyCell>{tutor.studyProgram.name[$locale]}</TableBodyCell> - <TableBodyCell>{tutor.shirtSize}</TableBodyCell> - <TableBodyCell>{tutor.coTutorWish || ""}</TableBodyCell> - <TableBodyCell>{tutor.training?dateFormatter.format(new Date(tutor.training.date)):"Bereits geschult"}</TableBodyCell> - <TableBodyCell>{tutor.notes}</TableBodyCell> - <TableBodyCell> - <Button href={`/admin/tutor/${tutor.id}`} color="light" size="xs">Bearbeiten</Button> - </TableBodyCell> - </TableBodyRow> - {/each} - </TableBody> -</Table> + {#snippet row({item: tutor})} + <TableBodyRow> + <TableBodyCell>{tutor.firstname}{tutor.nickname?` (${tutor.nickname})`:""}</TableBodyCell> + <TableBodyCell>{tutor.lastname}</TableBodyCell> + <TableBodyCell>{tutor.studyProgram.name[$locale]}</TableBodyCell> + <TableBodyCell>{tutor.shirtSize}</TableBodyCell> + <TableBodyCell>{tutor.coTutorWish || ""}</TableBodyCell> + <TableBodyCell>{tutor.training?dateFormatter.format(new Date(tutor.training.date)):"Bereits geschult"}</TableBodyCell> + <TableBodyCell>{tutor.notes}</TableBodyCell> + {#if data.user.permissions.has(Permission.UPDATE_TUTOR)} + <TableBodyCell> + <Button href={`/admin/tutor/${tutor.id}`} color="light" size="xs">Bearbeiten</Button> + </TableBodyCell> + {/if} + </TableBodyRow> + {/snippet} +</SortableTable> {/if} - -<!-- TODO: auto match co-tutors based on wishes ("npm i modern-diacritics" for names) --> diff --git a/src/routes/intern/+page.svelte b/src/routes/intern/+page.svelte index d02a7f6354bca7f472feb1487eb958aa5a25b6a1..58a1bcc769771ca78faf3e5f03b825c0e53964bf 100644 --- a/src/routes/intern/+page.svelte +++ b/src/routes/intern/+page.svelte @@ -65,7 +65,7 @@ A merge request was created to get this issue fixed: https://github.com/nextauth } }}> <input type="hidden" name="providerId" value="tutor" /> - <input type="hidden" name="redirectTo" value="/intern/tutor" /> + <input type="hidden" name="redirectTo" value={$page.url.searchParams.get("redirect_url") ?? "/intern/tutor"} /> <Input type="email" name="email" placeholder="E-Mail" required class="mb-2" /> <Input type="password" name="password" placeholder="Passwort" required class="mb-2" /> <Button type="submit">Anmelden</Button> @@ -103,7 +103,7 @@ A merge request was created to get this issue fixed: https://github.com/nextauth } }}> <input type="hidden" name="providerId" value="rally" /> - <input type="hidden" name="redirectTo" value="/intern/rallye" /> + <input type="hidden" name="redirectTo" value={$page.url.searchParams.get("redirect_url") ?? "/intern/rallye"} /> <Input type="email" name="email" placeholder="E-Mail" required class="mb-2" /> <Input type="password" name="password" placeholder="Passwort" required class="mb-2" /> <Button type="submit">Anmelden</Button> diff --git a/src/routes/intern/tutor/+page.svelte b/src/routes/intern/tutor/+page.svelte index eea990dd895b57f1c0d3094f23cc9a97de8399fe..36cc0537817d4f6a69da370c1dc0d99ef3d9f4dd 100644 --- a/src/routes/intern/tutor/+page.svelte +++ b/src/routes/intern/tutor/+page.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import SortableTable from "$lib/components/SortableTable.svelte"; import SortableTableHeadCell from "$lib/components/SortableTableHeadCell.svelte"; - import { Button, TableBodyCell, TableBodyRow, TableHead, TableHeadCell } from "flowbite-svelte"; + import { Button, Table, TableBody, TableBodyCell, TableBodyRow, TableHead, TableHeadCell } from "flowbite-svelte"; let { data } = $props(); @@ -19,8 +19,8 @@ <SortableTable {items}> <TableHead> - <SortableTableHeadCell sort={(a,b)=>a.name.localeCompare(b.name)}>Name</SortableTableHeadCell> - <SortableTableHeadCell sort={(a,b)=>a.age-b.age} defaultDirection="desc" default>Age</SortableTableHeadCell> + <SortableTableHeadCell sort={(a: typeof items[number], b)=>a.name.localeCompare(b.name)}>Name</SortableTableHeadCell> + <SortableTableHeadCell sort={(a: typeof items[number], b)=>a.age-b.age} defaultDirection="desc">Age</SortableTableHeadCell> <TableHeadCell>Actions</TableHeadCell> </TableHead> {#snippet row({item})}