Compare commits

..

No commits in common. "dev" and "1.12.8" have entirely different histories.
dev ... 1.12.8

43 changed files with 115 additions and 1516 deletions

View File

@ -2,101 +2,8 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC. All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [1.14.2](https://git.odit.services/lfk/frontend/compare/1.14.1...1.14.2)
- feat(GenerateRunnerCertificates): support skipping runners without scans [`5a7bc23`](https://git.odit.services/lfk/frontend/commit/5a7bc239d2f93ced9ebdc5b113fe27d0d8d3899c)
#### [1.14.1](https://git.odit.services/lfk/frontend/compare/1.14.0...1.14.1)
> 26 May 2025
- fix: ensure numeric values are parsed as integers in DocumentServer methods [`1b088b8`](https://git.odit.services/lfk/frontend/commit/1b088b87bf6e67796c2509d9c21f21833cb4df0f)
- chore(release): 1.14.1 [`661a698`](https://git.odit.services/lfk/frontend/commit/661a698fbaeb2432bec758ed632a520676ae86b2)
#### [1.14.0](https://git.odit.services/lfk/frontend/compare/1.13.5...1.14.0)
> 20 May 2025
- wip [`564a971`](https://git.odit.services/lfk/frontend/commit/564a971c63403af2e2eb550db814519576d62023)
- wip [`50b5e4e`](https://git.odit.services/lfk/frontend/commit/50b5e4e455ce705fc5ef7f3d069d88c9ff48a6af)
- wip [`2c91f46`](https://git.odit.services/lfk/frontend/commit/2c91f463758c8452561fbcc5dad8412edba8915d)
- wip [`1386b80`](https://git.odit.services/lfk/frontend/commit/1386b80d0c8569cf127f8235b3dd249c2775594a)
- wip [`6ef6dc0`](https://git.odit.services/lfk/frontend/commit/6ef6dc007837c237273a29ca489ef0cdb92f7c6c)
- wip [`3709881`](https://git.odit.services/lfk/frontend/commit/370988117683ab1fdc149a30f920cc6a66575c7a)
- chore(release): 1.14.0 [`d5fecd3`](https://git.odit.services/lfk/frontend/commit/d5fecd3f31916b80c305d76f37c4600f1d242eba)
- wip [`77413c7`](https://git.odit.services/lfk/frontend/commit/77413c7e5350a1d8643d2baf135b531235f78e64)
- wip [`0cb1193`](https://git.odit.services/lfk/frontend/commit/0cb1193269912b047abfacb6012463093c2adcfa)
- wip [`9ef3435`](https://git.odit.services/lfk/frontend/commit/9ef34359d8ac32674c28825b91b6aa2877e63552)
- wip [`a00af08`](https://git.odit.services/lfk/frontend/commit/a00af08b3f7c8278cfc54af6f593a9dcf4509ab4)
- wip [`286bd61`](https://git.odit.services/lfk/frontend/commit/286bd614976dcf8bcb14cffd092f23ef65393917)
- wip [`b89d4f2`](https://git.odit.services/lfk/frontend/commit/b89d4f248c5575548d77336832c64dc6e395efc3)
- inputElementID param [`4d79589`](https://git.odit.services/lfk/frontend/commit/4d79589903bb0726f6bcb2c0e5089a9e20f7db17)
- wip [`53f5fa3`](https://git.odit.services/lfk/frontend/commit/53f5fa3988e81215e17e41b7dd92e9ddf897610a)
- wip [`444b1f5`](https://git.odit.services/lfk/frontend/commit/444b1f537016b303a57fcaaac4468a749fe4f33c)
- disable autocomplete [`72e5425`](https://git.odit.services/lfk/frontend/commit/72e5425c0847102b0ed3f88abe17dc22ccea0a30)
#### [1.13.5](https://git.odit.services/lfk/frontend/compare/1.13.4...1.13.5)
> 20 May 2025
- add missing cursor-pointer [`6500839`](https://git.odit.services/lfk/frontend/commit/650083965a35cf3b05b6b67389ff8035dc5fa3fa)
- refactor(DonationsOverview): drop checkboxes - they dont do anything [`06d22c9`](https://git.odit.services/lfk/frontend/commit/06d22c929f94587d9bdbcb4abfc0a770cf94a771)
- chore(release): 1.13.5 [`e2a1c9a`](https://git.odit.services/lfk/frontend/commit/e2a1c9a508c6061e55438afefcd641e3d9423aaa)
#### [1.13.4](https://git.odit.services/lfk/frontend/compare/1.13.3...1.13.4)
> 20 May 2025
- feat(donationcreate): improved focus handling [`a827279`](https://git.odit.services/lfk/frontend/commit/a82727916345c7e713d4225c4771ef3f23d1392c)
- chore(release): 1.13.4 [`bbf659e`](https://git.odit.services/lfk/frontend/commit/bbf659e52d249732fadb659fdbd24a89d2e8ec42)
- chore(deps): remove unused [`3842d8b`](https://git.odit.services/lfk/frontend/commit/3842d8b1048ce12f0f70bf3d0530590470f0d200)
- fix(donationcreate): clearing [`9298a0d`](https://git.odit.services/lfk/frontend/commit/9298a0dc922ee5ed5b7c9017c865ad4b68fca3c8)
- feat(donationcreate): autofocus runner input on page load [`b9e2e65`](https://git.odit.services/lfk/frontend/commit/b9e2e653310c686bc06b9f27c38b49e9c6a3eaef)
- fix(DonationCreate): remove duplicate spaces from getRunnerLabel [`30a26ef`](https://git.odit.services/lfk/frontend/commit/30a26ef3ed55d072cd9bf2aea1b200fadc2a05f1)
- fix(donationcreate): improved resetAll [`7d9314f`](https://git.odit.services/lfk/frontend/commit/7d9314f05c58c1b50901f3797c0b461c4c79e5d2)
- fix(DeleteDonationModal): cannot overflow [`ca066aa`](https://git.odit.services/lfk/frontend/commit/ca066aa7a7a8d7c46e0f59370b06636faf5736ca)
- feat(donationcreate): full width [`b0063cd`](https://git.odit.services/lfk/frontend/commit/b0063cdead5f71c334c36e5587a58e957825dbcd)
- feat(donationcreate): add runner id to select [`27e7bbb`](https://git.odit.services/lfk/frontend/commit/27e7bbb9d142fbea659e89fb2335cc6c567d14ce)
#### [1.13.3](https://git.odit.services/lfk/frontend/compare/1.13.2...1.13.3)
> 19 May 2025
- chore(release): 1.13.3 [`2139b19`](https://git.odit.services/lfk/frontend/commit/2139b197ba672275e2a0b5ffbcf7fa43f80874e6)
- Refactor code structure for improved readability and maintainability [`e3c6d5a`](https://git.odit.services/lfk/frontend/commit/e3c6d5a5c0eaac2c91432b0be37d6fa11e57f644)
- refactor(donation): Refactor donor selection and add new donor creation functionality [`8c3f009`](https://git.odit.services/lfk/frontend/commit/8c3f0092d2735b1c85976f4e6955780b1035f68a)
- fix(donation): Ensure all selections are cleared on reset [`4e1a944`](https://git.odit.services/lfk/frontend/commit/4e1a944a2d7d0d0666fb8d2181a9941d0f11957f)
#### [1.13.2](https://git.odit.services/lfk/frontend/compare/1.13.1...1.13.2)
> 16 May 2025
- chore(release): 1.13.2 [`6fad04c`](https://git.odit.services/lfk/frontend/commit/6fad04c86249613dacfe2bc75362cb32d109573d)
- feat(dashboard): Add permission checks for scan and donation creation links [`838dcbf`](https://git.odit.services/lfk/frontend/commit/838dcbfd7e0c09e8cf61a04952475934ad1e3b86)
#### [1.13.1](https://git.odit.services/lfk/frontend/compare/1.13.0...1.13.1)
> 16 May 2025
- chore(release): 1.13.1 [`f5df252`](https://git.odit.services/lfk/frontend/commit/f5df252857f20f8426b8ca566b9bb3ec50331880)
- feat(tools): Remove unnecessary validation display in donation creation [`285fc91`](https://git.odit.services/lfk/frontend/commit/285fc91c66d03aaa861da549cb739c3698beb892)
#### [1.13.0](https://git.odit.services/lfk/frontend/compare/1.12.8...1.13.0)
> 16 May 2025
- feat(tools): Basic mobile scanner [`500886e`](https://git.odit.services/lfk/frontend/commit/500886e4106f4b53fbc40fb0fa15653f574c8328)
- chore(release): 1.13.0 [`d95b6cf`](https://git.odit.services/lfk/frontend/commit/d95b6cf5894dd0b487353f36c9f3a436066fd4ef)
- feat(tools): Added tool for fast sponsoring creation [`51ba1c8`](https://git.odit.services/lfk/frontend/commit/51ba1c852cad6243e935409da1eacecc5dcfa5fa)
- feat(dev): Enable devserver with https-support to circumvent ios https requirements for camera access [`25c38ea`](https://git.odit.services/lfk/frontend/commit/25c38ea3812a529a90294ff8834bdb65c487f8c4)
- feat(tools): Remove requirement for ten-diget codes [`80ca7aa`](https://git.odit.services/lfk/frontend/commit/80ca7aa08bdd44591e2d3efaa8e59dd4db5c864e)
- feat(dashboard): Added scanclient tool to dashboard [`06cfd60`](https://git.odit.services/lfk/frontend/commit/06cfd603cae79e0237bbece43203083f198d03a1)
#### [1.12.8](https://git.odit.services/lfk/frontend/compare/1.12.7...1.12.8) #### [1.12.8](https://git.odit.services/lfk/frontend/compare/1.12.7...1.12.8)
> 1 May 2025
- chore(release): 1.12.8 [`51d9b35`](https://git.odit.services/lfk/frontend/commit/51d9b35dc41fea0d0245fd136556f9fada3559da)
- feat(dasboard): Added section headers to main nav [`3a8533a`](https://git.odit.services/lfk/frontend/commit/3a8533a7baef02f7bc9780ce37be1a350bd92270) - feat(dasboard): Added section headers to main nav [`3a8533a`](https://git.odit.services/lfk/frontend/commit/3a8533a7baef02f7bc9780ce37be1a350bd92270)
- fic(locales): Updated dashboard translations [`5ac6fe3`](https://git.odit.services/lfk/frontend/commit/5ac6fe30b5b9e34043c734d51d5da137fdf7ac38) - fic(locales): Updated dashboard translations [`5ac6fe3`](https://git.odit.services/lfk/frontend/commit/5ac6fe30b5b9e34043c734d51d5da137fdf7ac38)
- feat(runners): Created_via filters can now be set via query params [`14501d3`](https://git.odit.services/lfk/frontend/commit/14501d3828dd0d48ba0baeeddf936ba275f7b9b7) - feat(runners): Created_via filters can now be set via query params [`14501d3`](https://git.odit.services/lfk/frontend/commit/14501d3828dd0d48ba0baeeddf936ba275f7b9b7)

View File

@ -13,7 +13,7 @@
<body> <body>
<span style="display: none; visibility: hidden" id="buildinfo" <span style="display: none; visibility: hidden" id="buildinfo"
>RELEASE_INFO-1.14.2-RELEASE_INFO</span >RELEASE_INFO-1.12.8-RELEASE_INFO</span
> >
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<script src="/env.js"></script> <script src="/env.js"></script>

View File

@ -1,6 +1,6 @@
{ {
"name": "@odit/lfk-frontend", "name": "@odit/lfk-frontend",
"version": "1.14.2", "version": "1.12.8",
"type": "module", "type": "module",
"scripts": { "scripts": {
"i18n-order": "node order.js", "i18n-order": "node order.js",
@ -21,8 +21,7 @@
"prettier-plugin-svelte": "3.3.3", "prettier-plugin-svelte": "3.3.3",
"release-it": "17.10.0", "release-it": "17.10.0",
"svelte-select": "3.17.0", "svelte-select": "3.17.0",
"vite": "6.3.2", "vite": "6.3.2"
"vite-plugin-mkcert": "^1.17.8"
}, },
"release-it": { "release-it": {
"git": { "git": {

174
pnpm-lock.yaml generated
View File

@ -93,9 +93,6 @@ importers:
vite: vite:
specifier: 6.3.2 specifier: 6.3.2
version: 6.3.2(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.29.2) version: 6.3.2(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.29.2)
vite-plugin-mkcert:
specifier: ^1.17.8
version: 1.17.8(vite@6.3.2(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.29.2))
packages: packages:
@ -783,9 +780,6 @@ packages:
async-retry@1.3.3: async-retry@1.3.3:
resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
atomically@2.0.3: atomically@2.0.3:
resolution: {integrity: sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==} resolution: {integrity: sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==}
@ -794,9 +788,6 @@ packages:
engines: {node: '>=8.3'} engines: {node: '>=8.3'}
hasBin: true hasBin: true
axios@1.9.0:
resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==}
balanced-match@1.0.2: balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@ -831,10 +822,6 @@ packages:
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
engines: {node: '>=18'} engines: {node: '>=18'}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
callsites@3.1.0: callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -908,10 +895,6 @@ packages:
color-name@1.1.4: color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
commander@7.2.0: commander@7.2.0:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@ -991,10 +974,6 @@ packages:
resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
deprecation@2.3.1: deprecation@2.3.1:
resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==}
@ -1006,10 +985,6 @@ packages:
resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
emoji-regex@10.4.0: emoji-regex@10.4.0:
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
@ -1027,22 +1002,6 @@ packages:
error-ex@1.3.2: error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.1.0:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
es5-ext@0.10.64: es5-ext@0.10.64:
resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==}
engines: {node: '>=0.10'} engines: {node: '>=0.10'}
@ -1137,19 +1096,6 @@ packages:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'} engines: {node: '>=8'}
follow-redirects@1.15.9:
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
form-data@4.0.2:
resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
engines: {node: '>= 6'}
frac@1.1.2: frac@1.1.2:
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
engines: {node: '>=0.8'} engines: {node: '>=0.8'}
@ -1173,14 +1119,6 @@ packages:
resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
get-stream@6.0.1: get-stream@6.0.1:
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -1221,10 +1159,6 @@ packages:
globrex@0.1.2: globrex@0.1.2:
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
graceful-fs@4.2.10: graceful-fs@4.2.10:
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
@ -1240,14 +1174,6 @@ packages:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
has-tostringtag@1.0.2:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
engines: {node: '>= 0.4'}
hasown@2.0.2: hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1563,10 +1489,6 @@ packages:
magic-string@0.30.17: magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
memoizee@0.4.17: memoizee@0.4.17:
resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==} resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==}
engines: {node: '>=0.12'} engines: {node: '>=0.12'}
@ -2096,12 +2018,6 @@ packages:
resolution: {integrity: sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==} resolution: {integrity: sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==}
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}
vite-plugin-mkcert@1.17.8:
resolution: {integrity: sha512-S+4tNEyGqdZQ3RLAG54ETeO2qyURHWrVjUWKYikLAbmhh/iJ+36gDEja4OWwFyXNuvyXcZwNt5TZZR9itPeG5Q==}
engines: {node: '>=v16.7.0'}
peerDependencies:
vite: '>=3'
vite@6.3.2: vite@6.3.2:
resolution: {integrity: sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==} resolution: {integrity: sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@ -2711,8 +2627,6 @@ snapshots:
dependencies: dependencies:
retry: 0.13.1 retry: 0.13.1
asynckit@0.4.0: {}
atomically@2.0.3: atomically@2.0.3:
dependencies: dependencies:
stubborn-fs: 1.2.5 stubborn-fs: 1.2.5
@ -2729,14 +2643,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- encoding - encoding
axios@1.9.0(debug@4.4.0):
dependencies:
follow-redirects: 1.15.9(debug@4.4.0)
form-data: 4.0.2
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
balanced-match@1.0.2: {} balanced-match@1.0.2: {}
base64-js@1.5.1: {} base64-js@1.5.1: {}
@ -2780,11 +2686,6 @@ snapshots:
dependencies: dependencies:
run-applescript: 7.0.0 run-applescript: 7.0.0
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
callsites@3.1.0: {} callsites@3.1.0: {}
camelcase@8.0.0: {} camelcase@8.0.0: {}
@ -2845,10 +2746,6 @@ snapshots:
color-name@1.1.4: {} color-name@1.1.4: {}
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
commander@7.2.0: {} commander@7.2.0: {}
concat-map@0.0.1: {} concat-map@0.0.1: {}
@ -2916,8 +2813,6 @@ snapshots:
escodegen: 2.1.0 escodegen: 2.1.0
esprima: 4.0.1 esprima: 4.0.1
delayed-stream@1.0.0: {}
deprecation@2.3.1: {} deprecation@2.3.1: {}
detect-libc@2.0.4: {} detect-libc@2.0.4: {}
@ -2926,12 +2821,6 @@ snapshots:
dependencies: dependencies:
type-fest: 4.40.0 type-fest: 4.40.0
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
emoji-regex@10.4.0: {} emoji-regex@10.4.0: {}
emoji-regex@8.0.0: {} emoji-regex@8.0.0: {}
@ -2947,21 +2836,6 @@ snapshots:
dependencies: dependencies:
is-arrayish: 0.2.1 is-arrayish: 0.2.1
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
es-set-tostringtag@2.1.0:
dependencies:
es-errors: 1.3.0
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
hasown: 2.0.2
es5-ext@0.10.64: es5-ext@0.10.64:
dependencies: dependencies:
es6-iterator: 2.0.3 es6-iterator: 2.0.3
@ -3127,17 +3001,6 @@ snapshots:
dependencies: dependencies:
to-regex-range: 5.0.1 to-regex-range: 5.0.1
follow-redirects@1.15.9(debug@4.4.0):
optionalDependencies:
debug: 4.4.0
form-data@4.0.2:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
es-set-tostringtag: 2.1.0
mime-types: 2.1.35
frac@1.1.2: {} frac@1.1.2: {}
fs.realpath@1.0.0: {} fs.realpath@1.0.0: {}
@ -3151,24 +3014,6 @@ snapshots:
get-east-asian-width@1.3.0: {} get-east-asian-width@1.3.0: {}
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
function-bind: 1.1.2
get-proto: 1.0.1
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
math-intrinsics: 1.1.0
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
get-stream@6.0.1: {} get-stream@6.0.1: {}
get-stream@8.0.1: {} get-stream@8.0.1: {}
@ -3220,8 +3065,6 @@ snapshots:
globrex@0.1.2: {} globrex@0.1.2: {}
gopd@1.2.0: {}
graceful-fs@4.2.10: {} graceful-fs@4.2.10: {}
graceful-fs@4.2.11: {} graceful-fs@4.2.11: {}
@ -3237,12 +3080,6 @@ snapshots:
has-flag@4.0.0: {} has-flag@4.0.0: {}
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
dependencies:
has-symbols: 1.1.0
hasown@2.0.2: hasown@2.0.2:
dependencies: dependencies:
function-bind: 1.1.2 function-bind: 1.1.2
@ -3506,8 +3343,6 @@ snapshots:
dependencies: dependencies:
'@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/sourcemap-codec': 1.5.0
math-intrinsics@1.1.0: {}
memoizee@0.4.17: memoizee@0.4.17:
dependencies: dependencies:
d: 1.0.2 d: 1.0.2
@ -4045,15 +3880,6 @@ snapshots:
validator@13.15.0: {} validator@13.15.0: {}
vite-plugin-mkcert@1.17.8(vite@6.3.2(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.29.2)):
dependencies:
axios: 1.9.0(debug@4.4.0)
debug: 4.4.0
picocolors: 1.1.1
vite: 6.3.2(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.29.2)
transitivePeerDependencies:
- supports-color
vite@6.3.2(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.29.2): vite@6.3.2(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.29.2):
dependencies: dependencies:
esbuild: 0.25.3 esbuild: 0.25.3

Binary file not shown.

View File

@ -71,8 +71,6 @@
import StatsClients from "./components/statsclients/StatsClients.svelte"; import StatsClients from "./components/statsclients/StatsClients.svelte";
import StatsClientDetail from "./components/statsclients/StatsClientDetail.svelte"; import StatsClientDetail from "./components/statsclients/StatsClientDetail.svelte";
import CardReplacement from "./components/tools/CardReplacement.svelte"; import CardReplacement from "./components/tools/CardReplacement.svelte";
import ScanClient from "./components/tools/ScanClient.svelte";
import DonationCreate from "./components/tools/DonationCreate.svelte";
store.init(); store.init();
</script> </script>
@ -142,12 +140,6 @@
<Route path="/cardreplacement/"> <Route path="/cardreplacement/">
<CardReplacement /> <CardReplacement />
</Route> </Route>
<Route path="/scanclient/">
<ScanClient />
</Route>
<Route path="/donationcreate/">
<DonationCreate />
</Route>
</Route> </Route>
<Route path="/teams/*"> <Route path="/teams/*">
<Route path="/"> <Route path="/">

View File

@ -180,7 +180,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -189,7 +189,7 @@
edit_modal_open = false; edit_modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -103,7 +103,7 @@
<button <button
on:click={submit} on:click={submit}
type="button" type="button"
class="confirm_deletion_button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
> >
{$_("delete")} {$_("delete")}
</button> </button>
@ -112,7 +112,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -469,7 +469,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -85,55 +85,9 @@
<span>{$_("card-replacement-menu")}</span> <span>{$_("card-replacement-menu")}</span>
</a> </a>
{/if}
{#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:CREATE")}
<a
class:activenav={$router.path.includes("/tools/scanclient/")}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/tools/scanclient/"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600"
>
<path
fill-rule="evenodd"
d="M14.615 1.595a.75.75 0 0 1 .359.852L12.982 9.75h7.268a.75.75 0 0 1 .548 1.262l-10.5 11.25a.75.75 0 0 1-1.272-.71l1.992-7.302H3.75a.75.75 0 0 1-.548-1.262l10.5-11.25a.75.75 0 0 1 .913-.143Z"
clip-rule="evenodd"
/>
</svg>
<span>{$_("scanclient")}</span>
</a>
{/if}
{#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:CREATE")}
<a
class:activenav={$router.path.includes("/tools/donationcreate/")}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/tools/donationcreate/"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600"
>
<path
fill-rule="evenodd"
d="M14.615 1.595a.75.75 0 0 1 .359.852L12.982 9.75h7.268a.75.75 0 0 1 .548 1.262l-10.5 11.25a.75.75 0 0 1-1.272-.71l1.992-7.302H3.75a.75.75 0 0 1-.548-1.262l10.5-11.25a.75.75 0 0 1 .913-.143Z"
clip-rule="evenodd"
/>
</svg>
<span>{$_("donation-quick-add")}</span>
</a>
{/if}
<h2 class="px-4 py-2 text-xs font-semibold text-gray-600 uppercase"> <h2 class="px-4 py-2 text-xs font-semibold text-gray-600 uppercase">
{$_("management")} {$_("management")}
</h2> </h2>
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")}
<a <a
class:activenav={$router.path.includes("/runners/")} class:activenav={$router.path.includes("/runners/")}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"

View File

@ -194,7 +194,7 @@
payment_modal_open = false; payment_modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -81,7 +81,7 @@
/></svg /></svg
> >
</div> </div>
<div class="mt-3 sm:text-left max-h-[75vh]"> <div class="mt-3 sm:text-left max-h-[75vh] overflow-y-auto">
<h3 class="text-lg leading-6 font-medium text-gray-900"> <h3 class="text-lg leading-6 font-medium text-gray-900">
{$_("please-confirm-the-deletion-of-donation")} {$_("please-confirm-the-deletion-of-donation")}
</h3> </h3>
@ -102,7 +102,7 @@
<button <button
on:click={submit} on:click={submit}
type="button" type="button"
class="confirm_deletion_button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
> >
{$_("delete")} {$_("delete")}
</button> </button>
@ -111,7 +111,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -1,28 +1,26 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import TableActions from "../shared/TableActions.svelte"; import TableActions from "../shared/TableActions.svelte";
export let detailsLink; export let detailsLink;
export let detailsAction; export let detailsAction;
export let deleteEnabled; export let deleteEnabled;
export let deleteAction; export let deleteAction;
export let paymentAction; export let paymentAction;
</script> </script>
{#if paymentAction} {#if paymentAction}
<button <button
on:click={paymentAction} on:click={paymentAction}
class="text-[#025a21] cursor-pointer hover:text-green-900 mr-4" class="text-[#025a21] hover:text-green-900 mr-4">{$_("enter-payment")}</button
>{$_("enter-payment")}</button >
>
{:else} {:else}
<span class="inline-block opacity-0 cursor-default mr-4" style="" <span class="inline-block opacity-0 cursor-default mr-4" style="">{$_("enter-payment")}</span>
>{$_("enter-payment")}</span
>
{/if} {/if}
<TableActions <TableActions
bind:detailsAction bind:detailsAction
bind:detailsLink bind:detailsLink
bind:deleteAction bind:deleteAction
bind:deleteEnabled bind:deleteEnabled
/> />

View File

@ -247,6 +247,14 @@
<thead class="border-b border-gray-400"> <thead class="border-b border-gray-400">
{#each $table.getHeaderGroups() as headerGroup} {#each $table.getHeaderGroups() as headerGroup}
<tr class="select-none"> <tr class="select-none">
<th class="inset-y-0 left-0 px-4 py-2 text-left w-px">
<InputElement
type="checkbox"
checked={$table.getIsAllRowsSelected()}
indeterminate={$table.getIsSomeRowsSelected()}
on:change={() => $table.toggleAllRowsSelected()}
/>
</th>
{#each headerGroup.headers as header} {#each headerGroup.headers as header}
<TableHeader {header} /> <TableHeader {header} />
{/each} {/each}
@ -256,6 +264,13 @@
<tbody> <tbody>
{#each $table.getRowModel().rows as row} {#each $table.getRowModel().rows as row}
<tr class="odd:bg-white even:bg-gray-100"> <tr class="odd:bg-white even:bg-gray-100">
<td class="inset-y-0 left-0 px-4 py-2 text-center w-px">
<InputElement
type="checkbox"
checked={row.getIsSelected()}
on:change={() => row.toggleSelected()}
/>
</td>
{#each row.getVisibleCells() as cell} {#each row.getVisibleCells() as cell}
<td> <td>
<svelte:component <svelte:component

View File

@ -433,7 +433,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -72,14 +72,14 @@
<button <button
on:click={deleteDonor} on:click={deleteDonor}
type="button" type="button"
class="confirm_deletion_button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
> >
{$_("confirm-delete-donor-with-all-donations")} {$_("confirm-delete-donor-with-all-donations")}
</button> </button>
<button <button
on:click={cancelDelete} on:click={cancelDelete}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel-keep-donor")} {$_("cancel-keep-donor")}
</button> </button>

View File

@ -174,7 +174,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -294,7 +294,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -86,14 +86,14 @@
<button <button
on:click={deleteOrg} on:click={deleteOrg}
type="button" type="button"
class="confirm_deletion_button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
> >
{$_("confirm-delete-organization-and-associated-teams-runners")} {$_("confirm-delete-organization-and-associated-teams-runners")}
</button> </button>
<button <button
on:click={cancelDelete} on:click={cancelDelete}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel-keep-organization")} {$_("cancel-keep-organization")}
</button> </button>

View File

@ -1,4 +1,3 @@
class DocumentServer { class DocumentServer {
baseUrl: string; baseUrl: string;
apiKey: string; apiKey: string;
@ -13,19 +12,19 @@ class DocumentServer {
for (let i = 0; i < cards.length; i++) { for (let i = 0; i < cards.length; i++) {
const card = { const card = {
id: parseInt(cards[i].id), id: cards[i].id,
enabled: cards[i].enabled, enabled: cards[i].enabled,
code: cards[i].code, code: cards[i].code,
runner: { runner: {
id: parseInt(cards[i]?.runner?.id), id: cards[i]?.runner?.id,
first_name: cards[i]?.runner?.firstname, first_name: cards[i]?.runner?.firstname,
middle_name: cards[i]?.runner?.middlename, middle_name: cards[i]?.runner?.middlename,
last_name: cards[i]?.runner?.lastname, last_name: cards[i]?.runner?.lastname,
group: { group: {
id: parseInt(cards[i]?.runner?.group?.id), id: cards[i]?.runner?.group.id,
name: cards[i]?.runner?.group.name, name: cards[i]?.runner?.group.name,
parent_group: { parent_group: {
id: parseInt(cards[i]?.runner?.group?.parentGroup?.id), id: cards[i]?.runner?.group?.parentGroup?.id,
name: cards[i]?.runner?.group?.parentGroup?.name, name: cards[i]?.runner?.group?.parentGroup?.name,
}, },
}, },
@ -58,15 +57,15 @@ class DocumentServer {
for (let i = 0; i < runners.length; i++) { for (let i = 0; i < runners.length; i++) {
console.log(runners[i]); console.log(runners[i]);
const card = { const card = {
id: parseInt(runners[i].id), id: runners[i].id,
first_name: runners[i].firstname, first_name: runners[i].firstname,
middle_name: runners[i].middlename, middle_name: runners[i].middlename,
last_name: runners[i].lastname, last_name: runners[i].lastname,
group: { group: {
id: parseInt(runners[i].group.id), id: runners[i].group.id,
name: runners[i].group.name, name: runners[i].group.name,
parent_group: { parent_group: {
id: parseInt(runners[i]?.group?.parentGroup?.id), id: runners[i]?.group?.parentGroup?.id,
name: runners[i]?.group?.parentGroup?.name, name: runners[i]?.group?.parentGroup?.name,
}, },
}, },
@ -97,28 +96,28 @@ class DocumentServer {
for (let i = 0; i < runners.length; i++) { for (let i = 0; i < runners.length; i++) {
const certificate = { const certificate = {
id: parseInt(runners[i].id), id: runners[i].id,
first_name: runners[i].firstname, first_name: runners[i].firstname,
middle_name: runners[i].middlename, middle_name: runners[i].middlename,
last_name: runners[i].lastname, last_name: runners[i].lastname,
self_service_link: runners[i].selfserviceLink, self_service_link: runners[i].selfserviceLink,
group: { group: {
id: parseInt(runners[i].group.id), id: runners[i].group.id,
name: runners[i].group.name, name: runners[i].group.name,
parent_group: { parent_group: {
id: parseInt(runners[i]?.group?.parentGroup?.id), id: runners[i]?.group?.parentGroup?.id,
name: runners[i]?.group?.parentGroup?.name, name: runners[i]?.group?.parentGroup?.name,
}, },
}, },
distance: parseInt(runners[i].distance), distance: runners[i].distance,
distance_donations: runners[i].distanceDonations.map( distance_donations: runners[i].distanceDonations.map(
(distanceDonation: any) => { (distanceDonation: any) => {
return { return {
id: distanceDonation.id, id: distanceDonation.id,
amount: parseInt(distanceDonation.amount), amount: distanceDonation.amount,
amount_per_distance: parseInt(distanceDonation.amountPerDistance), amount_per_distance: distanceDonation.amountPerDistance,
donor: { donor: {
id: parseInt(distanceDonation.donor.id), id: distanceDonation.donor.id,
first_name: distanceDonation.donor.firstname, first_name: distanceDonation.donor.firstname,
middle_name: distanceDonation.donor.middlename, middle_name: distanceDonation.donor.middlename,
last_name: distanceDonation.donor.lastname, last_name: distanceDonation.donor.lastname,

View File

@ -20,13 +20,13 @@
export let generate_orgs = []; export let generate_orgs = [];
export let generate_teams = []; export let generate_teams = [];
function generateCertificates(locale, include0runners = false) { function generateCertificates(locale) {
if (generate_orgs.length > 0) { if (generate_orgs.length > 0) {
generateOrgCertificates(locale, include0runners = false); generateOrgCertificates(locale);
} else if (generate_teams.length > 0) { } else if (generate_teams.length > 0) {
generateTeamCertificates(locale, include0runners = false); generateTeamCertificates(locale);
} else { } else {
generateRunnerCertificates(locale, include0runners = false); generateRunnerCertificates(locale);
} }
} }
function download(blob, fileName) { function download(blob, fileName) {
@ -41,7 +41,7 @@
toast.success($_("pdf-successfully-generated")); toast.success($_("pdf-successfully-generated"));
} }
async function generateRunnerCertificates(locale, include0runners = false) { async function generateRunnerCertificates(locale) {
toast.loading($_("generating-pdf")); toast.loading($_("generating-pdf"));
const current_donations = const current_donations =
(await DonationService.donationControllerGetAll()) || []; (await DonationService.donationControllerGetAll()) || [];
@ -50,15 +50,7 @@
const linkRunner = await RunnerService.runnerControllerGetOne(runner.id) const linkRunner = await RunnerService.runnerControllerGetOne(runner.id)
linkRunner.distanceDonations = linkRunner.distanceDonations =
current_donations.filter((d) => d.runner?.id == runner.id) || []; current_donations.filter((d) => d.runner?.id == runner.id) || [];
// check if linkRunner.distance is 0, if so, and include0runners is false, skip this runner certificateRunners.push(linkRunner);
if (
!include0runners &&
(linkRunner.distance === 0 || linkRunner.distance === null)
) {
continue;
} else {
certificateRunners.push(linkRunner);
}
} }
documentServer documentServer
.generateCertificates(certificateRunners, locale) .generateCertificates(certificateRunners, locale)
@ -74,7 +66,7 @@
.catch((err) => {}); .catch((err) => {});
} }
async function generateTeamCertificates(locale, include0runners = false) { async function generateTeamCertificates(locale) {
toast.loading($_("generating-pdfs")); toast.loading($_("generating-pdfs"));
let count = 0; let count = 0;
const current_donations = const current_donations =
@ -88,15 +80,7 @@
for (let runner of runners) { for (let runner of runners) {
runner.distanceDonations = runner.distanceDonations =
current_donations.filter((d) => d.runner?.id == runner.id) || []; current_donations.filter((d) => d.runner?.id == runner.id) || [];
// check if runner.distance is 0, if so, and include0runners is false, skip this runner certificateRunners.push(runner);
if (
!include0runners &&
(runner.distance === 0 || runner.distance === null)
) {
continue;
} else {
certificateRunners.push(runner);
}
} }
documentServer documentServer
.generateCertificates(certificateRunners, locale) .generateCertificates(certificateRunners, locale)
@ -111,7 +95,7 @@
} }
} }
async function generateOrgCertificates(locale, include0runners = false) { async function generateOrgCertificates(locale) {
toast.loading($_("generating-pdfs")); toast.loading($_("generating-pdfs"));
const current_donations = const current_donations =
(await DonationService.donationControllerGetAll()) || []; (await DonationService.donationControllerGetAll()) || [];
@ -130,15 +114,7 @@
for (let runner of runners) { for (let runner of runners) {
runner.distanceDonations = runner.distanceDonations =
current_donations.filter((d) => d.runner?.id == runner.id) || []; current_donations.filter((d) => d.runner?.id == runner.id) || [];
// check if runner.distance is 0, if so, and include0runners is false, skip this runner certificateRunners.push(runner);
if (
!include0runners &&
(runner.distance === 0 || runner.distance === null)
) {
continue;
} else {
certificateRunners.push(runner);
}
} }
await documentServer await documentServer
.generateCertificates(certificateRunners, locale) .generateCertificates(certificateRunners, locale)
@ -185,36 +161,20 @@
</script> </script>
{#if certificates_show} {#if certificates_show}
<button <button
on:click={() => { on:click={() => {
generateCertificates("de", true); generateCertificates("de");
}} }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm mb-1 lg:mb-0" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm mb-1 lg:mb-0"
> >
{$_("generate-runner-certificates")}: DE {$_("generate-runner-certificates")}: DE
</button> </button>
<button <button
on:click={() => { on:click={() => {
generateCertificates("de", false); generateCertificates("en");
}} }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm mb-1 lg:mb-0" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm mb-1 lg:mb-0"
> >
{$_("generate-runner-certificates")}: DE [{$_('exclude_0m_runners_certificate')}] {$_("generate-runner-certificates")}: EN
</button> </button>
<button
on:click={() => {
generateCertificates("en", true);
}}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm mb-1 lg:mb-0"
>
{$_("generate-runner-certificates")}: EN
</button>
<button
on:click={() => {
generateCertificates("en", false);
}}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm mb-1 lg:mb-0"
>
{$_("generate-runner-certificates")}: EN [{$_('exclude_0m_runners_certificate')}]
</button>
{/if} {/if}

View File

@ -338,7 +338,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -90,7 +90,7 @@
<button <button
on:click={submit} on:click={submit}
type="button" type="button"
class="confirm_deletion_button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
> >
{$_("delete")} {$_("delete")}
</button> </button>
@ -99,7 +99,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -261,7 +261,7 @@
cancelModal(); cancelModal();
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>
@ -375,7 +375,7 @@
cancelModal(); cancelModal();
}} }}
type="button" type="button"
class="confirm_deletion_button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -195,7 +195,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -90,7 +90,7 @@
<button <button
on:click={submit} on:click={submit}
type="button" type="button"
class="confirm_deletion_button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
> >
{$_("delete")} {$_("delete")}
</button> </button>
@ -99,7 +99,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -203,7 +203,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -82,14 +82,14 @@
<button <button
on:click={deleteStation} on:click={deleteStation}
type="button" type="button"
class="confirm_deletion_button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
> >
{$_("confirm-delete-station-with-all-scans")} {$_("confirm-delete-station-with-all-scans")}
</button> </button>
<button <button
on:click={cancelDelete} on:click={cancelDelete}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel-keep-station")} {$_("cancel-keep-station")}
</button> </button>

View File

@ -85,14 +85,14 @@
<button <button
on:click={deleteMe} on:click={deleteMe}
type="button" type="button"
class="confirm_deletion_button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
> >
{$_("confirm-delete-my-user-profile")} {$_("confirm-delete-my-user-profile")}
</button> </button>
<button <button
on:click={cancelDelete} on:click={cancelDelete}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel-keep-my-profile")} {$_("cancel-keep-my-profile")}
</button> </button>

View File

@ -148,7 +148,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -81,14 +81,14 @@
<button <button
on:click={deleteClient} on:click={deleteClient}
type="button" type="button"
class="confirm_deletion_button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
> >
{$_("confirm-delete-statsclient")} {$_("confirm-delete-statsclient")}
</button> </button>
<button <button
on:click={cancelDelete} on:click={cancelDelete}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel-keep-statsclient")} {$_("cancel-keep-statsclient")}
</button> </button>

View File

@ -199,7 +199,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -85,14 +85,14 @@
<button <button
on:click={deleteTeam} on:click={deleteTeam}
type="button" type="button"
class="confirm_deletion_button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
> >
{$_("confirm-delete-team-and-associated-runners")} {$_("confirm-delete-team-and-associated-runners")}
</button> </button>
<button <button
on:click={cancelDelete} on:click={cancelDelete}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel-keep-team")} {$_("cancel-keep-team")}
</button> </button>

View File

@ -1,422 +0,0 @@
<script>
import { _ } from "svelte-i18n";
import {
DonationService,
DonorService,
RunnerService,
} from "@odit/lfk-client-js";
import toast from "svelte-french-toast";
import VirtualSelect from "./VirtualSelect.svelte";
import { onMount } from "svelte";
let runners = [];
let donors = [];
let runnerinfo = { id: 0, firstname: "", lastname: "" };
let donorinfo = { id: 0, firstname: "", lastname: "" };
let address = {
address1: "",
address2: "",
city: "",
postalcode: "",
country: "Germany",
};
let amount = null;
let address_checked = false;
let donor_create_new = false;
let last_created = null;
RunnerService.runnerControllerGetAll()
.then((val) => {
runners = val.map((r) => {
return { label: getRunnerLabel(r), value: r };
});
})
.catch((err) => {
console.log("error fetching runners:", err);
});
function loadDonors() {
DonorService.donorControllerGetAll()
.then((val) => {
donors = val.map((r) => {
return { label: getRunnerLabel(r), value: r };
});
console.log("refreshed donors");
setTimeout(() => {
loadDonors;
}, 30000);
})
.catch((err) => {
console.log("error fetching donors:", err);
});
}
loadDonors();
const getRunnerLabel = (option) => {
return (
[option.firstname, option.middlename, option.lastname]
.join(" ")
.replace(" ", " ") +
" [#" +
option.id +
"]"
);
};
let selectRefRunner;
let selectRefDonor;
function resetAll() {
runnerinfo = { id: 0, firstname: "", lastname: "" };
donorinfo = { id: 0, firstname: "", lastname: "" };
amount = null;
address_checked = false;
donor_create_new = false;
selectRefRunner?.reset();
selectRefDonor?.reset();
document.querySelector("#jjqzqicxujrnnh1x3447x18x").focus();
}
onMount(() => {
document.querySelector("#jjqzqicxujrnnh1x3447x18x").focus();
});
</script>
<div class="p-4">
<h3 class="text-3xl font-bold">{$_("fast_donation_create")}</h3>
<!-- -->
<div>
<div class="w-full space-y-4 mb-6">
{#if last_created}
<div class="mt-4 p-3 bg-green-50 border border-green-200 rounded-md">
<p class="text-black">
{$_("last-created-donation")}: #{last_created.id}: {last_created.amountPerDistance /
100}€ für {getRunnerLabel(last_created.runner)} von {getRunnerLabel(
last_created.donor
)}
</p>
</div>
{/if}
<!-- -->
<h4 class="text-xl font-semibold">{$_("runner")}</h4>
<VirtualSelect
inputElementID="jjqzqicxujrnnh1x3447x18x"
bind:this={selectRefRunner}
on:onClear={() => {
console.log("Cleared selection");
}}
options={runners}
filterFn={(item, searchTerm) => {
if (searchTerm.startsWith("#")) {
const id = parseInt(searchTerm.replace("#", ""));
return item.value.id === id;
}
return item.label.toLowerCase().includes(searchTerm.toLowerCase());
}}
bind:selected={runnerinfo}
inputAriaLabel={$_("search-for-runner-by-name-or-id")}
inputPlaceholder={$_("search-for-runner-by-name-or-id")}
noOptionsText={$_("no-runners-found")}
on:onSelected={(data) => {
if (data.detail !== null) {
document.querySelector("#donation_amount_eur").focus();
}
}}
/>
<!-- Amount Input -->
<div>
<h4 class="text-xl font-semibold">{$_("amount-per-kilometer")}</h4>
<div class="mt-1 flex rounded-md shadow-sm">
<input
autocomplete="off"
class:border-red-500={!amount > 0}
class:focus:border-red-500={!amount > 0}
class:focus:ring-red-500={!amount > 0}
bind:value={amount}
on:keydown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
document.querySelector("#button_existing_donor").focus();
}
}}
type="number"
step="0.01"
id="donation_amount_eur"
name="donation_amount_eur"
class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 p-2"
placeholder="z.B. 1,50"
/>
<span
class="inline-flex items-center px-3 rounded-r-md border border-neutral-300 bg-neutral-50 text-neutral-500 text-sm"
>€</span
>
</div>
</div>
<!-- Donor Selection -->
<div>
<h4 class="text-xl font-semibold">{$_("donor")}</h4>
<!-- Donor Type Toggle -->
<div class="mb-2">
<div class="flex border rounded-md overflow-hidden shadow-sm">
<button
on:keydown={(e) => {
if (e.key === "ArrowRight") {
e.preventDefault();
document.querySelector("#button_new_donor").focus();
document.querySelector("#button_new_donor").click();
}
if (e.key === "Enter") {
e.preventDefault();
document.querySelector("#zt12c3udy3bme5bqobmqcif1").focus();
}
}}
id="button_existing_donor"
class:bg-indigo-600={!donor_create_new}
class:text-white={!donor_create_new}
class="py-2 px-4 w-1/2 transition-colors"
on:click={() => {
donor_create_new = false;
donorinfo = { id: 0, firstname: "", lastname: "" };
}}
>
{$_("existing-donor")}
</button>
<button
on:keydown={(e) => {
if (e.key === "ArrowLeft") {
e.preventDefault();
document.querySelector("#button_existing_donor").focus();
document.querySelector("#button_existing_donor").click();
}
if (e.key === "Enter") {
e.preventDefault();
document.querySelector("#button_new_donor").click();
}
}}
id="button_new_donor"
class={`py-2 px-4 w-1/2 transition-colors ${donor_create_new ? "bg-indigo-600 text-white" : "bg-gray-100 text-gray-700"}`}
on:click={() => {
donor_create_new = true;
donorinfo = { id: 0, firstname: "", lastname: "" };
setTimeout(() => {
document.querySelector("#firstname").focus();
}, 50);
}}
>
{$_("new-donor")}
</button>
</div>
</div>
{#if !donor_create_new}
<VirtualSelect
inputElementID="zt12c3udy3bme5bqobmqcif1"
bind:this={selectRefDonor}
on:onClear={() => {
console.log("Cleared selection");
}}
options={donors}
filterFn={(item, searchTerm) => {
return item.label
.toLowerCase()
.includes(searchTerm.toLowerCase());
}}
bind:selected={donorinfo}
inputAriaLabel={$_("search-for-donor")}
inputPlaceholder={$_("search-for-donor")}
noOptionsText={$_("no-donors-found")}
on:onSelected={(data) => {
console.log(data.detail);
if (data.detail !== null) {
document.querySelector("#submit_button").focus();
setTimeout(() => {
document.querySelector("#submit_button").focus();
}, 100);
}
}}
/>
{:else}
<div class="space-y-3">
<!-- First Name -->
<div>
<label
for="firstname"
class="block text-sm font-medium text-gray-700"
>
{$_("first-name")}
</label>
<input
type="text"
id="firstname"
on:keydown={(e) => {
if (e.key === "Enter") {
document.querySelector("#lastname").focus();
}
}}
bind:value={donorinfo.firstname}
class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 p-2"
placeholder={$_("first-name")}
/>
</div>
<!-- Last Name -->
<div>
<label
for="lastname"
class="block text-sm font-medium text-gray-700"
>
{$_("last-name")}
</label>
<input
type="text"
id="lastname"
bind:value={donorinfo.lastname}
class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 p-2"
placeholder={$_("last-name")}
/>
</div>
<!-- Address Checkbox -->
<div class="flex items-start mt-4">
<div class="flex items-center h-5">
<input
id="address_check"
type="checkbox"
bind:checked={address_checked}
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
/>
</div>
<div class="ml-3 text-sm">
<label for="address_check" class="font-medium text-gray-700">
{$_("receipt-needed")}
</label>
</div>
</div>
{#if address_checked}
<!-- Address Fields -->
<div
class="space-y-3 mt-3 p-3 border border-gray-200 rounded-md bg-gray-50"
>
<div>
<label
for="address1"
class="block text-sm font-medium text-gray-700"
>
{$_("address")}
</label>
<input
type="text"
id="address1"
bind:value={address.address1}
class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 p-2"
/>
</div>
<div>
<label
for="address2"
class="block text-sm font-medium text-gray-700"
>
{$_("apartment-suite-etc")}
</label>
<input
type="text"
id="address2"
bind:value={address.address2}
class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 p-2"
/>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label
for="postalcode"
class="block text-sm font-medium text-gray-700"
>
{$_("zip-postal-code")}
</label>
<input
type="text"
id="postalcode"
bind:value={address.postalcode}
class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 p-2"
/>
</div>
<div>
<label
for="city"
class="block text-sm font-medium text-gray-700"
>
{$_("city")}
</label>
<input
type="text"
id="city"
bind:value={address.city}
class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 p-2"
/>
</div>
</div>
</div>
{/if}
</div>
{/if}
</div>
<!-- Submit Button -->
<div class="mt-6">
<button
id="submit_button"
type="button"
class="w-full inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:bg-gray-400 disabled:cursor-not-allowed"
disabled={!amount > 0 ||
!runnerinfo.id ||
(!donorinfo.id && !donor_create_new) ||
(donor_create_new &&
(!donorinfo.firstname || !donorinfo.lastname)) ||
(donor_create_new &&
address_checked &&
(!address.address1 || !address.city || !address.postalcode))}
on:click={async () => {
if (donor_create_new) {
donorinfo = await DonorService.donorControllerPost({
firstname: donorinfo.firstname,
lastname: donorinfo.lastname,
receiptNeeded: address_checked,
...(address_checked ? { address: address } : {}),
});
}
DonationService.donationControllerPostDistance({
donor: donorinfo.id,
runner: runnerinfo.id,
amountPerDistance: amount * 100,
})
.then((data) => {
last_created = data;
toast.success($_("donation-created-successfully"));
resetAll();
loadDonors();
})
.catch((err) => {
console.error("Error creating donation:", err);
toast.error($_("error-creating-donation"));
});
}}
>
{$_("create")}
</button>
</div>
</div>
</div>
</div>
<style>
:global(:root) {
--sv-bg: #ffffff;
}
</style>

View File

@ -1,239 +0,0 @@
<script>
import { _, time } from "svelte-i18n";
import {
RunnerCardService,
RunnerService,
ScanService,
ScanStationService,
TrackService,
} from "@odit/lfk-client-js";
import QrCodeScanner from "./QrCodeScanner.svelte";
import { onMount } from "svelte";
import Select from "svelte-select";
let state = "scan_card";
let scaninfo = {
lapTime: 0,
track: "",
distance: null,
valid: false,
id: 0,
runner: {
id: 0,
firstname: "",
lastname: "",
distance: 0,
},
};
let cardCode = "";
let scannerActive = false;
let barcodeInput;
let stations = [];
let selectedStation = null;
function resetAll() {
state = "scan_card";
scaninfo = {
lapTime: 0,
track: "",
distance: null,
valid: false,
id: 0,
runner: {
id: 0,
firstname: "",
lastname: "",
distance: 0,
},
};
cardCode = "";
scannerActive = true;
setTimeout(() => {
barcodeInput && barcodeInput.focus();
}, 100);
}
onMount(() => {
if (barcodeInput) {
barcodeInput.focus();
}
ScanStationService.scanStationControllerGetAll()
.then((data) => {
stations = data.map((val) => {
return {
label: val.description,
value: val,
};
});
scannerActive = true;
})
.catch(() => {
stations = [];
});
});
function handleInput(input) {
ScanService.scanControllerPostTrackScans({
card: parseInt(input),
station: selectedStation,
})
.then((data) => {
scaninfo = data;
if (scaninfo.valid) {
new Audio("/beep.mp3").play();
state = "scan_card";
} else {
state = "error_invalid";
new Audio("/error.mp3").play();
}
})
.catch((err) => {
console.error(err);
state = "error_card";
new Audio("/error.mp3").play();
});
}
</script>
<div class="p-4">
<h3 class="text-3xl font-bold">{$_("mobile-scanclient")}</h3>
<Select
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2"
items={stations}
showChevron={true}
placeholder={$_("search-for-track")}
noOptionsMessage={$_("no-tracks-found")}
on:select={(selectedValue) => {
selectedStation = selectedValue.detail.value.id;
setTimeout(() => {
barcodeInput && barcodeInput.focus();
}, 100);
}}
on:clear={() => (selectedStation = null)}
/>
{#if state === "error_card"}
<div class="text-center mx-auto">
<svg
class="h-64 mx-auto"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 500 500"
><path
d="M298.37 335.5C382 299.85 469.46 233.1 432.31 135 398.6 46 284.74 25.75 219.62 102.47c-28.09 33.09-23.18 77.05-57.16 106.51s-90.4 45.83-75.13 104c23.67 89.93 156 46.02 211.04 22.52Z"
style="fill:#407bff"
/><path
d="M298.37 335.5C382 299.85 469.46 233.1 432.31 135 398.6 46 284.74 25.75 219.62 102.47c-28.09 33.09-23.18 77.05-57.16 106.51s-90.4 45.83-75.13 104c23.67 89.93 156 46.02 211.04 22.52Z"
style="fill:#fff;opacity:.9"
/><path
d="M360.6 263.05h-.36c-26.64-2.18-45-25-45.74-25.92a4.47 4.47 0 0 1 7-5.55c.21.27 15.9 19.61 37.63 22.37 7-7 13-25.48 12.33-31.07v-.16c-.14-1.8-.48-8 1.29-11.65a4.47 4.47 0 0 1 8 3.88c-.44.92-.65 4.23-.44 7 1 9.2-7 32.42-17 40.19a4.47 4.47 0 0 1-2.71.91ZM148.82 238.82a65.8 65.8 0 0 1-48.56-22.28 4.46 4.46 0 0 1-.26-5.64c7.22-9.71 20-32.64 22-40.11a10.91 10.91 0 0 0-4.14-4.33 4.45 4.45 0 0 1-2.55-3.61l-.72-7.32a4.47 4.47 0 0 1 8.89-.88l.5 5.09a22.34 22.34 0 0 1 6.81 8.65 4.48 4.48 0 0 1 .32 2.26c-.92 7.93-13.79 30.9-21.71 42.51 18.49 18.43 40.59 16.75 41.56 16.66a4.47 4.47 0 0 1 .82 8.9c-.26.02-1.29.1-2.96.1ZM292.87 416.09h-12a4.47 4.47 0 0 1-4.31-5.66c3.13-11.24 4.67-20.39 5.82-34.71-4.24-20-8.23-38.21-8.27-38.39a4.47 4.47 0 0 1 8.73-1.91c0 .18 4.12 18.86 8.41 39.08a4.23 4.23 0 0 1 .08 1.28c-1 12.86-2.31 21.75-4.67 31.38h6.18a4.47 4.47 0 0 1 0 8.93ZM200.32 416.09h-6.76a4.45 4.45 0 0 1-4.42-5.08c1.15-8.2 7-23.13 13.3-38.14 2.23-19.8 4.05-36.8 4.07-37a4.47 4.47 0 1 1 8.88 1c0 .17-1.88 17.56-4.15 37.65a4.31 4.31 0 0 1-.32 1.22c-4.43 10.63-9.49 23.15-11.8 31.44h1.2a4.47 4.47 0 1 1 0 8.93Z"
style="fill:#263238"
/><path
d="m204.21 111-52.06 52.07c-2.62 57.71-2.41 118.33 0 181.18h172.16c-3.41-81.1-3.73-159.17 0-233.25Z"
style="fill:#fff"
/><path
d="M324.31 345.13H152.15a.9.9 0 0 1-.9-.86c-2.49-65.27-2.49-126.27 0-181.27a.9.9 0 0 1 .27-.59l52.06-52.07a.89.89 0 0 1 .63-.26h120.1a.9.9 0 0 1 .65.28.87.87 0 0 1 .24.66c-3.59 71.34-3.59 147.61 0 233.17a.89.89 0 0 1-.25.65.86.86 0 0 1-.64.29ZM153 343.34h170.38c-3.54-84.86-3.55-160.59 0-231.47h-118.8L153 163.43c-2.45 54.64-2.45 115.16 0 179.91Z"
style="fill:#263238"
/><path
d="M214.28 219.19c-.2-4.36-2.67-7.8-5.53-7.7s-5 3.71-4.82 8.07 2.67 7.8 5.53 7.69 5.02-3.71 4.82-8.06ZM274.65 217.82c-.2-4.35-2.67-7.79-5.53-7.69s-5 3.71-4.82 8.07 2.68 7.8 5.53 7.69 5.02-3.71 4.82-8.07ZM229.35 237a36.55 36.55 0 0 1 28.63 1.3 1.27 1.27 0 0 1 .49 1.74 1.3 1.3 0 0 1-1.75.49c-.15-.08-14.4-7.76-31.41 1a1.31 1.31 0 0 1-1.74-.54 1.27 1.27 0 0 1 .55-1.72 41.73 41.73 0 0 1 5.23-2.27ZM205.64 178.34a2.64 2.64 0 0 1 1.26.36 2.58 2.58 0 0 1 .92 3.51A25.29 25.29 0 0 1 188.27 195a2.59 2.59 0 0 1-2.69-2.45 2.55 2.55 0 0 1 2.44-2.66c.39 0 9.62-.58 15.36-10.27a2.52 2.52 0 0 1 2.26-1.28ZM266.05 176.87a2.57 2.57 0 0 1 2.33.72c8 8 17.14 6.39 17.52 6.32a2.6 2.6 0 0 1 3 2 2.54 2.54 0 0 1-2 3c-.5.09-12.14 2.31-22.21-7.75a2.54 2.54 0 0 1 1.31-4.3Z"
style="fill:#407bff"
/><path
d="m321.72 204.86-7.31.68a5.22 5.22 0 0 1-5.58-4.06L298.7 156.1a5.22 5.22 0 0 1 3.77-6.18l19.59-5.14ZM209 167.69c-5.09-13.89-10.18-36.12-4.81-56.71l-52.06 52.07c14.73 4.95 38.19 7.06 56.87 4.64Z"
style="opacity:.2"
/><path
d="M204.21 163.05c-5.71-16.86-3.38-39.78 0-52.07l-52.06 52.07c15.76 2.87 33.37 2.41 52.06 0Z"
style="fill:#fff"
/><path
d="M176 165.92a133.14 133.14 0 0 1-24-2 .88.88 0 0 1-.47-1.5l52.06-52.07a.89.89 0 0 1 1.49.87c-3.14 11.44-5.75 34.6 0 51.54a.93.93 0 0 1-.09.76.87.87 0 0 1-.64.41 221.85 221.85 0 0 1-28.35 1.99Zm-22-3.46c13.84 2.29 29.91 2.24 49-.16-4.71-14.94-3.64-34.71-.48-48.4Z"
style="fill:#263238"
/></svg
>
<p class="text-lg font-semibold">{$_("card_not_found")}</p>
<button
on:click={() => {
resetAll();
}}
type="button"
class="py-3 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-100 text-blue-800 hover:bg-blue-200 focus:outline-hidden focus:bg-blue-200 disabled:opacity-50 disabled:pointer-events-none dark:text-blue-500 dark:bg-blue-800/30 dark:hover:bg-blue-800/20 dark:focus:bg-blue-800/20 mt-2"
>
{$_("try_again")}
</button>
</div>
{:else if state === "error_invalid"}
<div class="text-center mx-auto">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke-width="1.5"
stroke="currentColor"
class="w-64 h-64 text-center mx-auto text-red-600 mt-2"
viewBox="5.25 5.25 13.5 13.5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<p class="text-lg font-semibold">{$_("invalid-scan")}</p>
<button
on:click={() => {
resetAll();
}}
type="button"
class="py-3 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-100 text-blue-800 hover:bg-blue-200 focus:outline-hidden focus:bg-blue-200 disabled:opacity-50 disabled:pointer-events-none dark:text-blue-500 dark:bg-blue-800/30 dark:hover:bg-blue-800/20 dark:focus:bg-blue-800/20 mt-2"
>
{$_("try_again")}
</button>
</div>
{:else}
<p>
<b>{$_("runner")}:</b>
{scaninfo.runner?.firstname}
{scaninfo.runner?.lastname}
</p>
<p>
<b>{$_("laptime")}:</b>
{Math.floor(scaninfo.lapTime / 60) +
"min " +
(Math.floor(scaninfo.lapTime % 60) + "").padStart(2, "0") +
"s"}
</p>
<!-- -->
{/if}
{#if state.includes("scan_")}
{#if scannerActive}
<QrCodeScanner
:paused={!scannerActive}
on:detect={(e) => {
if (scannerActive) {
if (`${e.detail.decodedText}`.length === 13) {
e.detail.decodedText = e.detail.decodedText.substring(
0,
e.detail.decodedText.length - 1
);
}
console.log({ type: "DETECT", code: e.detail.decodedText });
handleInput(e.detail.decodedText);
}
}}
width={320}
height={320}
class="w-full max-w-sm bg-neutral-300 rounded-lg overflow-hidden"
/>
<form
on:submit={(e) => {
handleInput(barcodeInput.value);
barcodeInput.value = "";
e.preventDefault();
}}
class="mt-2"
>
<input
type="text"
placeholder={$_("barcode_scanner")}
class="w-full max-w-sm bg-neutral-300 rounded-lg overflow-hidden mt-2"
bind:this={barcodeInput}
/>
</form>
{/if}
<!-- -->
{/if}
</div>

View File

@ -1,357 +0,0 @@
<script>
import { createEventDispatcher, onMount, tick } from "svelte";
// Generate a default unique ID
function generateDefaultID() {
return "virtual-select-" + Math.random().toString(36).slice(2);
}
// Props
export let options = [];
export let selected = null;
export let inputPlaceholder = "Search options...";
export let noOptionsText = "No options found";
export let inputAriaLabel = "Search and select an option";
export let toggleAriaLabel = "Toggle dropdown";
export let clearAriaLabel = "Clear selection";
export let filterFn = null; // Custom filter function
export let autofocus = false; // Autofocus input
export let inputElementID = generateDefaultID(); // Input element ID
// Internal state
let searchTerm = "";
let filteredOptions = options;
let isOpen = false;
let container;
let visibleItems = [];
let startIndex = 0;
let itemHeight = 40; // Fixed height for each option (in pixels)
let visibleCount = 10; // Default number of items to render
let focusedIndex = -1; // Track the focused option index (-1 means no focus)
let inputElement; // Reference to input element
const dispatch = createEventDispatcher();
// Filter options based on search term
$: {
filteredOptions = searchTerm
? filterFn
? options.filter((option) => filterFn(option, searchTerm))
: options.filter((option) =>
option.label.toLowerCase().includes(searchTerm.toLowerCase())
)
: options;
// Reset scroll and focus when filtered options change
startIndex = 0;
focusedIndex = -1;
updateVisibleItems();
}
// Update visible items based on scroll position
function updateVisibleItems() {
if (!container) return;
const scrollTop = container.scrollTop;
startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + visibleCount,
filteredOptions.length
);
visibleItems = filteredOptions.slice(startIndex, endIndex);
}
// Handle scroll event
function handleScroll() {
updateVisibleItems();
}
// Calculate visible item count based on container height
async function updateVisibleCount() {
if (container) {
await tick(); // Wait for DOM to render
visibleCount = Math.ceil(container.clientHeight / itemHeight) + 2; // Buffer of 2 items
updateVisibleItems();
}
}
// Handle option selection
function selectOption(option) {
selected = option.value;
isOpen = false;
searchTerm = option.label; // Set searchTerm to the selected option's label
focusedIndex = -1;
dispatch("onSelected", option.value);
}
// Handle clear selection
function clearSelection() {
selected = null;
searchTerm = "";
focusedIndex = -1;
dispatch("onSelected", null);
dispatch("onClear");
}
// Reset component state
export function reset() {
selected = null;
searchTerm = "";
isOpen = false;
focusedIndex = -1;
startIndex = 0;
updateVisibleItems();
dispatch("onSelected", null);
dispatch("onClear");
}
// Toggle dropdown
async function toggleDropdown() {
isOpen = !isOpen;
if (isOpen) {
forceVisibleItems();
}
}
// Handle click outside to close dropdown
function handleClickOutside(event) {
if (!event.target.closest(".select-container")) {
isOpen = false;
focusedIndex = -1;
}
}
// Handle input focus to open dropdown
async function handleInputFocus() {
// forceVisibleItems();
}
// Handle input typing to open dropdown
async function handleInput() {
forceVisibleItems();
}
async function forceVisibleItems() {
isOpen = true;
await updateVisibleCount(); // Ensure items render on focus
// these 2 timeouts are a more or less tmp fix for rendering items when dropdown opens
setTimeout(async () => {
await updateVisibleCount(); // Ensure items render on focus
}, 25);
setTimeout(async () => {
await updateVisibleCount(); // Ensure items render on focus
}, 50);
}
// Handle keyboard navigation
function handleKeydown(event, index) {
if (!isOpen) return;
if (event.key === "ArrowDown") {
event.preventDefault();
if (focusedIndex < filteredOptions.length - 1) {
focusedIndex += 1;
scrollToFocusedItem();
}
} else if (event.key === "ArrowUp") {
event.preventDefault();
if (focusedIndex > 0) {
focusedIndex -= 1;
scrollToFocusedItem();
} else if (focusedIndex === -1 && filteredOptions.length > 0) {
focusedIndex = 0;
scrollToFocusedItem();
}
} else if (event.key === "Enter" && index >= 0) {
event.preventDefault();
selectOption(filteredOptions[index]);
} else if (event.key === "Escape") {
event.preventDefault();
isOpen = false;
focusedIndex = -1;
}
}
// Scroll to the focused item
function scrollToFocusedItem() {
if (!container || focusedIndex < 0) return;
const itemTop = focusedIndex * itemHeight;
const itemBottom = itemTop + itemHeight;
const containerTop = container.scrollTop;
const containerBottom = containerTop + container.clientHeight;
if (itemTop < containerTop) {
container.scrollTop = itemTop;
} else if (itemBottom > containerBottom) {
container.scrollTop = itemBottom - container.clientHeight;
}
updateVisibleItems();
}
// Initialize container size observer and autofocus fallback
onMount(async () => {
if (container) {
const resizeObserver = new ResizeObserver(updateVisibleCount);
resizeObserver.observe(container);
return () => resizeObserver.disconnect();
}
// Fallback autofocus with tick to ensure inputElement is bound
if (autofocus && inputElement) {
await tick();
inputElement.focus();
}
});
// Get display text for the input
function getDisplayText() {
if (!selected) return inputPlaceholder;
const selectedOption = options.find((option) => option.value === selected);
return selectedOption ? selectedOption.label : inputPlaceholder;
}
</script>
<svelte:window on:click={handleClickOutside} />
<div class="select-container relative w-full">
<!-- Select element with inline search -->
<div
class="border rounded-md px-3 py-2 bg-white shadow-sm flex items-center gap-2"
role="combobox"
aria-expanded={isOpen}
>
<input
autocomplete="off"
type="text"
id={inputElementID}
bind:value={searchTerm}
bind:this={inputElement}
placeholder={getDisplayText()}
class="w-full bg-transparent focus:outline-none {selected
? 'text-black'
: 'text-gray-700'}"
{autofocus}
on:focus={handleInputFocus}
on:input={handleInput}
on:keydown={(e) => {
if (e.key === "Enter" && !isOpen) {
toggleDropdown();
} else {
handleKeydown(e, focusedIndex);
}
}}
aria-label={inputAriaLabel}
/>
{#if selected}
<button
type="button"
class="w-5 h-5 flex items-center justify-center text-gray-500 hover:text-gray-700"
on:click={clearSelection}
on:keydown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
clearSelection();
} else if (e.key === "Escape") {
e.preventDefault();
isOpen = false;
focusedIndex = -1;
}
}}
role="button"
tabindex="0"
aria-label={clearAriaLabel}
>
<svg
class="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
{/if}
<svg
class="w-4 h-4 text-gray-500 transform {isOpen ? 'rotate-180' : ''}"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
on:click={toggleDropdown}
role="button"
tabindex="0"
on:keydown={(e) => {
if (e.key === "Enter") toggleDropdown();
else if (e.key === "Escape") {
e.preventDefault();
isOpen = false;
focusedIndex = -1;
}
}}
aria-label={toggleAriaLabel}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke
Politeness="round"
stroke-width="2"
d="M19 9l-7 7-7-7"
/>
</svg>
</div>
<!-- Dropdown -->
{#if isOpen}
<div
class="absolute z-10 w-full mt-1 bg-white border rounded-md shadow-lg max-h-80 overflow-auto"
bind:this={container}
on:scroll={handleScroll}
role="listbox"
>
{#if filteredOptions.length > 0}
<!-- Virtualized list container -->
<div style="height: {filteredOptions.length * itemHeight}px;">
<div style="transform: translateY({startIndex * itemHeight}px);">
{#each visibleItems as item, i (item.label + "-" + (startIndex + i))}
<div
class="px-3 py-2 hover:bg-blue-100 cursor-pointer {selected ===
item.value
? 'bg-blue-50'
: ''} {focusedIndex === startIndex + i
? 'bg-blue-200 outline outline-2 outline-blue-500'
: ''}"
on:click={() => selectOption(item)}
on:keydown={(e) => handleKeydown(e, startIndex + i)}
role="option"
tabindex="0"
aria-selected={selected === item.value}
>
{item.label}
</div>
{/each}
</div>
</div>
{:else}
<div class="px-3 py-2 text-gray-500">{noOptionsText}</div>
{/if}
</div>
{/if}
</div>
<style>
/* Ensure Tailwind classes handle additional styling */
:global(.select-container input:focus) {
border-color: #3b82f6; /* Tailwind's blue-500 */
}
:global([role="option"]:focus) {
outline: 2px solid #3b82f6;
outline-offset: -2px;
}
:global([role="button"]:focus) {
outline: 2px solid #3b82f6;
outline-offset: -2px;
}
</style>

View File

@ -230,7 +230,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -287,7 +287,7 @@
modal_open = false; modal_open = false;
}} }}
type="button" type="button"
class="cancel_modal_button" class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
> >
{$_("cancel")} {$_("cancel")}
</button> </button>

View File

@ -134,7 +134,6 @@
"created-blanco-cards": "Blankokarten wurden erstellt", "created-blanco-cards": "Blankokarten wurden erstellt",
"created_via": "Erstellt von", "created_via": "Erstellt von",
"creating-blanco-cards": "Erstelle Blankokarten", "creating-blanco-cards": "Erstelle Blankokarten",
"creating-donation": "Sponsoring wird erstellt...",
"credits": "Credits", "credits": "Credits",
"csv_import__class": "Klasse", "csv_import__class": "Klasse",
"csv_import__firstname": "Vorname", "csv_import__firstname": "Vorname",
@ -198,16 +197,13 @@
"documentation": "Dokumentation", "documentation": "Dokumentation",
"donation-amount": "Sponsoringbetrag", "donation-amount": "Sponsoringbetrag",
"donation-amount-must-be-greater-that-0-00eur": "Der Sponsoringbetrag muss größer als 0.00€ sein.", "donation-amount-must-be-greater-that-0-00eur": "Der Sponsoringbetrag muss größer als 0.00€ sein.",
"donation-created": "Sponsoring erstellt",
"donation-created-successfully": "Sponsoring erstellt",
"donation-deleted": "Sponsoring gelöscht", "donation-deleted": "Sponsoring gelöscht",
"donation-quick-add": "Sponsoringschnelleingabe",
"donation-updated": "Sponsoring wurde aktualisiert", "donation-updated": "Sponsoring wurde aktualisiert",
"donation_added": "Sponsoring hinzugefügt", "donation_added": "Sponsoring hinzugefügt",
"donations": "Sponsorings", "donations": "Sponsorings",
"donations-are-being-loaded": "Sponsorings werden geladen...", "donations-are-being-loaded": "Sponsorings werden geladen...",
"done": "✅ Fertig", "done": "✅ Fertig",
"donor": "Sponsor:in", "donor": "Sponsor",
"donor-added": "Sponsor hinzugefügt", "donor-added": "Sponsor hinzugefügt",
"donor-deleted": "Sponsor gelöscht", "donor-deleted": "Sponsor gelöscht",
"donor-has-no-associated-donations": "Keine Sponsorings", "donor-has-no-associated-donations": "Keine Sponsorings",
@ -227,16 +223,12 @@
"enabled_large": "Aktiviert", "enabled_large": "Aktiviert",
"english": "Englisch", "english": "Englisch",
"enter-payment": "Zahlung eingeben", "enter-payment": "Zahlung eingeben",
"error-creating-donation": "Fehler beim Erstellen des Sponsorings",
"error-during-import": "Fehler beim Importieren", "error-during-import": "Fehler beim Importieren",
"error-whyile-copying-to-clipboard": "Fehler beim Kopieren in die Zwischenablage", "error-whyile-copying-to-clipboard": "Fehler beim Kopieren in die Zwischenablage",
"error_on_login": "😢Fehler beim Login", "error_on_login": "😢Fehler beim Login",
"everything-concerning-your-profile": "Alles zu deinem Profil", "everything-concerning-your-profile": "Alles zu deinem Profil",
"exclude_0m_runners_certificate": "ohne 0m Läufer",
"existing-donor": "Existierende Sponsor:in",
"faq": "FAQ", "faq": "FAQ",
"fast_card_replacement": "Karten-Schnellzusweisung (Mit Mobilgeräteunterstützung)", "fast_card_replacement": "Karten-Schnellzusweisung (Mit Mobilgeräteunterstützung)",
"fast_donation_create": "Sponsoring-Schnellanlage",
"festbetrag": "Festbetrag", "festbetrag": "Festbetrag",
"filename_sponsoringquittungsliste": "SponsoringQuittungsListe", "filename_sponsoringquittungsliste": "SponsoringQuittungsListe",
"filter-by-organization-team": "Filtern nach Organisation / Team", "filter-by-organization-team": "Filtern nach Organisation / Team",
@ -283,7 +275,6 @@
"key": "Schlüssel", "key": "Schlüssel",
"laeufer-hinzufuegen": "Läufer hinzufügen", "laeufer-hinzufuegen": "Läufer hinzufügen",
"laptime": "Rundenzeit", "laptime": "Rundenzeit",
"last-created-donation": "Zuletzt erstellt",
"last-name": "Nachname", "last-name": "Nachname",
"last-name-is-required": "Nachname muss angegeben werden", "last-name-is-required": "Nachname muss angegeben werden",
"lfk-is-os": "Das \"Lauf für Kaya!\" Frontend ist (wie alle anderen Projekte für den \"LfK!\" auch) ein OpenSource Projekt.", "lfk-is-os": "Das \"Lauf für Kaya!\" Frontend ist (wie alle anderen Projekte für den \"LfK!\" auch) ein OpenSource Projekt.",
@ -309,14 +300,12 @@
"middle-name": "Mittelname", "middle-name": "Mittelname",
"minimum-lap-time-in-s": "Minimale Rundenzeit (in Sekunden)", "minimum-lap-time-in-s": "Minimale Rundenzeit (in Sekunden)",
"minimum-lap-time-must-be-a-positive-number-or-0": "Die minimale Rundenzeit muss eine positive Zahl oder 0 sein", "minimum-lap-time-must-be-a-positive-number-or-0": "Die minimale Rundenzeit muss eine positive Zahl oder 0 sein",
"mobile-scanclient": "Mobiler Scanclient",
"must-be-at-least-10-characters-long": "Passwort muss mindestens 10 Zeichen lang sein!", "must-be-at-least-10-characters-long": "Passwort muss mindestens 10 Zeichen lang sein!",
"must-contain-a-lowercase-letter": "Passwort muss einen Großbuchstaben enthalten!", "must-contain-a-lowercase-letter": "Passwort muss einen Großbuchstaben enthalten!",
"must-contain-a-number": "Passwort muss eine Zahl enthalten!", "must-contain-a-number": "Passwort muss eine Zahl enthalten!",
"must-contain-a-uppercase-letter": "Passwort muss einen Kleinbuchstaben enthalten!", "must-contain-a-uppercase-letter": "Passwort muss einen Kleinbuchstaben enthalten!",
"name": "Name", "name": "Name",
"name-is-required": "Der Gruppenname muss angegeben werden", "name-is-required": "Der Gruppenname muss angegeben werden",
"new-donor": "Neue Sponsor:in",
"new-password": "Neues Passwort", "new-password": "Neues Passwort",
"next_runner": "Nächster Läufer", "next_runner": "Nächster Läufer",
"no-address": "Keine Adresse hinterlegt", "no-address": "Keine Adresse hinterlegt",
@ -394,7 +383,7 @@
"request-a-new-reset-mail": "Neue Reset-Mail anfordern", "request-a-new-reset-mail": "Neue Reset-Mail anfordern",
"reset-my-password": "Passwort zurücksetzen", "reset-my-password": "Passwort zurücksetzen",
"reset-password": "Passwort zurücksetzen", "reset-password": "Passwort zurücksetzen",
"runner": "Läufer:in", "runner": "Läufer",
"runner-added": "Läufer hinzugefügt", "runner-added": "Läufer hinzugefügt",
"runner-deleted": "Läufer gelöscht", "runner-deleted": "Läufer gelöscht",
"runner-import": "Läufer Import", "runner-import": "Läufer Import",
@ -416,7 +405,6 @@
"scan-with-fixed-distance": "Scan mit Festdistanz", "scan-with-fixed-distance": "Scan mit Festdistanz",
"scan_card": "Läuferkarte scannen", "scan_card": "Läuferkarte scannen",
"scan_runner": "Läufer scannen", "scan_runner": "Läufer scannen",
"scanclient": "Scanclient",
"scans": "Scans", "scans": "Scans",
"scans-are-being-loaded": "Scans werden geladen", "scans-are-being-loaded": "Scans werden geladen",
"scanstation": "Scanner Station", "scanstation": "Scanner Station",
@ -426,7 +414,6 @@
"scanstations-are-being-loaded": "Scannerstationen werden geladen...", "scanstations-are-being-loaded": "Scannerstationen werden geladen...",
"search-for-an-organization-by-name-or-id": "Suche eine Organisation (via Name oder #ID)", "search-for-an-organization-by-name-or-id": "Suche eine Organisation (via Name oder #ID)",
"search-for-an-organization-or-team-by-name-or-id": "Suche eine Organisation oder ein Team (via Name oder #ID)", "search-for-an-organization-or-team-by-name-or-id": "Suche eine Organisation oder ein Team (via Name oder #ID)",
"search-for-donor": "Nach Sponsor:in suchen",
"search-for-donor-name-or-id": "Suche eine Sponsor (via Name oder #ID)", "search-for-donor-name-or-id": "Suche eine Sponsor (via Name oder #ID)",
"search-for-permission": "Berechtigungen durchsuchen", "search-for-permission": "Berechtigungen durchsuchen",
"search-for-runner-by-name-or-id": "Suche einen Läufer (via Name oder #ID)", "search-for-runner-by-name-or-id": "Suche einen Läufer (via Name oder #ID)",

View File

@ -134,7 +134,6 @@
"created-blanco-cards": "Created blanco cards", "created-blanco-cards": "Created blanco cards",
"created_via": "Erstellt über", "created_via": "Erstellt über",
"creating-blanco-cards": "Creating blanco cards", "creating-blanco-cards": "Creating blanco cards",
"creating-donation": "Creating donation...",
"credits": "Credits", "credits": "Credits",
"csv_import__class": "Class", "csv_import__class": "Class",
"csv_import__firstname": "Firstname", "csv_import__firstname": "Firstname",
@ -198,10 +197,7 @@
"documentation": "Documentation", "documentation": "Documentation",
"donation-amount": "Donation amount", "donation-amount": "Donation amount",
"donation-amount-must-be-greater-that-0-00eur": "Donation amount must be greater that 0.00€", "donation-amount-must-be-greater-that-0-00eur": "Donation amount must be greater that 0.00€",
"donation-created": "Created sponsoring",
"donation-created-successfully": "Donation created",
"donation-deleted": "Donation deleted", "donation-deleted": "Donation deleted",
"donation-quick-add": "Mass sponsoring creation",
"donation-updated": "Donation updated", "donation-updated": "Donation updated",
"donation_added": "Donation_added", "donation_added": "Donation_added",
"donations": "Donations", "donations": "Donations",
@ -227,16 +223,12 @@
"enabled_large": "Enabled", "enabled_large": "Enabled",
"english": "English", "english": "English",
"enter-payment": "Enter payment", "enter-payment": "Enter payment",
"error-creating-donation": "error creating the sponsoring",
"error-during-import": "Error during import", "error-during-import": "Error during import",
"error-whyile-copying-to-clipboard": "Error while copying to clipboard", "error-whyile-copying-to-clipboard": "Error while copying to clipboard",
"error_on_login": "Error on login", "error_on_login": "Error on login",
"everything-concerning-your-profile": "Everything concerning your profile", "everything-concerning-your-profile": "Everything concerning your profile",
"exclude_0m_runners_certificate": "exclude runners without scans",
"existing-donor": "Existing Donor",
"faq": "FAQ", "faq": "FAQ",
"fast_card_replacement": "Fast card replacement (with mobile support)", "fast_card_replacement": "Fast card replacement (with mobile support)",
"fast_donation_create": "Mass donation creator",
"festbetrag": "Fixed amount", "festbetrag": "Fixed amount",
"filename_sponsoringquittungsliste": "DonorReceiptList", "filename_sponsoringquittungsliste": "DonorReceiptList",
"filter-by-organization-team": "Filter by Organization/ Team", "filter-by-organization-team": "Filter by Organization/ Team",
@ -283,7 +275,6 @@
"key": "Key", "key": "Key",
"laeufer-hinzufuegen": "Add runner", "laeufer-hinzufuegen": "Add runner",
"laptime": "Laptime", "laptime": "Laptime",
"last-created-donation": "Last created",
"last-name": "Last name", "last-name": "Last name",
"last-name-is-required": "Last Name is required", "last-name-is-required": "Last Name is required",
"lfk-is-os": "The \"Lauf für Kaya!\" Frontend is (like all other projects for the \"LfK!\" Also) an open source project.", "lfk-is-os": "The \"Lauf für Kaya!\" Frontend is (like all other projects for the \"LfK!\" Also) an open source project.",
@ -308,14 +299,12 @@
"middle-name": "Middle name", "middle-name": "Middle name",
"minimum-lap-time-in-s": "minimum lap time in s", "minimum-lap-time-in-s": "minimum lap time in s",
"minimum-lap-time-must-be-a-positive-number-or-0": "minimum lap time must be a positive number or 0", "minimum-lap-time-must-be-a-positive-number-or-0": "minimum lap time must be a positive number or 0",
"mobile-scanclient": "Mobile scanclient",
"must-be-at-least-10-characters-long": "Must be at least 10 characters long!", "must-be-at-least-10-characters-long": "Must be at least 10 characters long!",
"must-contain-a-lowercase-letter": "Must contain a lowercase letter!", "must-contain-a-lowercase-letter": "Must contain a lowercase letter!",
"must-contain-a-number": "Must contain a number!", "must-contain-a-number": "Must contain a number!",
"must-contain-a-uppercase-letter": "Must contain a uppercase letter!", "must-contain-a-uppercase-letter": "Must contain a uppercase letter!",
"name": "Name", "name": "Name",
"name-is-required": "Name is required", "name-is-required": "Name is required",
"new-donor": "New donor",
"new-password": "New password", "new-password": "New password",
"next_runner": "Next Runner", "next_runner": "Next Runner",
"no-address": "no address", "no-address": "no address",
@ -415,7 +404,6 @@
"scan-with-fixed-distance": "Scan with fixed distance", "scan-with-fixed-distance": "Scan with fixed distance",
"scan_card": "Scan Card", "scan_card": "Scan Card",
"scan_runner": "Scan Runner", "scan_runner": "Scan Runner",
"scanclient": "Scanclient",
"scans": "Scans", "scans": "Scans",
"scans-are-being-loaded": "Scans are being loaded", "scans-are-being-loaded": "Scans are being loaded",
"scanstation": "Scanstation", "scanstation": "Scanstation",
@ -425,7 +413,6 @@
"scanstations-are-being-loaded": "Loading scanstations...", "scanstations-are-being-loaded": "Loading scanstations...",
"search-for-an-organization-by-name-or-id": "Search for an organization (by name or #ID)", "search-for-an-organization-by-name-or-id": "Search for an organization (by name or #ID)",
"search-for-an-organization-or-team-by-name-or-id": "Search for an organization or team (by name or #ID)", "search-for-an-organization-or-team-by-name-or-id": "Search for an organization or team (by name or #ID)",
"search-for-donor": "Search for donor",
"search-for-donor-name-or-id": "Search for donor (by name or #ID)", "search-for-donor-name-or-id": "Search for donor (by name or #ID)",
"search-for-permission": "Search for permission", "search-for-permission": "Search for permission",
"search-for-runner-by-name-or-id": "Search for runner (by name or #ID)", "search-for-runner-by-name-or-id": "Search for runner (by name or #ID)",

View File

@ -31,9 +31,3 @@
.donation_active_tab { .donation_active_tab {
@apply min-w-0 flex-1 bg-blue-400 text-white first:border-s-0 border-s border-b-2 border-neutral-200 py-4 px-4 text-sm font-medium text-center overflow-hidden cursor-pointer focus:outline-hidden; @apply min-w-0 flex-1 bg-blue-400 text-white first:border-s-0 border-s border-b-2 border-neutral-200 py-4 px-4 text-sm font-medium text-center overflow-hidden cursor-pointer focus:outline-hidden;
} }
.confirm_deletion_button {
@apply w-full cursor-pointer inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500;
}
.cancel_modal_button {
@apply w-full cursor-pointer justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block;
}

View File

@ -2,8 +2,7 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte"; import { svelte } from "@sveltejs/vite-plugin-svelte";
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
import mkcert from 'vite-plugin-mkcert'
export default defineConfig({ export default defineConfig({
plugins: [svelte(), tailwindcss(), mkcert()], plugins: [svelte(), tailwindcss()],
}); });