Compare commits

..

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

49 changed files with 927 additions and 2364 deletions

View File

@ -2,121 +2,9 @@
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 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)
- 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)
- refactor(tools): Move tools to tools route [`16dc789`](https://git.odit.services/lfk/frontend/commit/16dc789db5d9ea41774c77622a579cc0d9bd95f2)
- refactor(tools): Move tools into shared directory instead of the non-descript "general" [`e4f9b1a`](https://git.odit.services/lfk/frontend/commit/e4f9b1a60551d7955def4d068d534cf17b1ea640)
#### [1.12.7](https://git.odit.services/lfk/frontend/compare/1.12.6...1.12.7)
> 1 May 2025
- chore(release): 1.12.7 [`c78bdfa`](https://git.odit.services/lfk/frontend/commit/c78bdfa5e24ada4909455064dd6b05cf34fc6df3)
- fix(deps): fresh lockfile [`b2ed2af`](https://git.odit.services/lfk/frontend/commit/b2ed2afd8a45a1a01ac6118b27941e3b4b3b611f)
- refactor(store): update refresh interval from 2min to 60min [`00d1988`](https://git.odit.services/lfk/frontend/commit/00d198895e15174b70a8d229974b4baa7d0ed8fc)
#### [1.12.6](https://git.odit.services/lfk/frontend/compare/1.12.5...1.12.6) #### [1.12.6](https://git.odit.services/lfk/frontend/compare/1.12.5...1.12.6)
> 1 May 2025
- feat(pdfs): Experimental generation of large runner card files [`93422b9`](https://git.odit.services/lfk/frontend/commit/93422b97799c5e45c89acadd34f33b1a11b04617) - feat(pdfs): Experimental generation of large runner card files [`93422b9`](https://git.odit.services/lfk/frontend/commit/93422b97799c5e45c89acadd34f33b1a11b04617)
- chore(release): 1.12.6 [`b5c079d`](https://git.odit.services/lfk/frontend/commit/b5c079da9a0545d146e9f3029a543e04c907add3)
#### [1.12.5](https://git.odit.services/lfk/frontend/compare/1.12.4...1.12.5) #### [1.12.5](https://git.odit.services/lfk/frontend/compare/1.12.4...1.12.5)

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.6-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.6",
"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": {

184
pnpm-lock.yaml generated
View File

@ -15,8 +15,8 @@ importers:
specifier: ^5.2.5 specifier: ^5.2.5
version: 5.2.5 version: 5.2.5
'@odit/lfk-client-js': '@odit/lfk-client-js':
specifier: 1.2.7 specifier: 1.2.5
version: 1.2.7 version: 1.2.5
'@paralleldrive/cuid2': '@paralleldrive/cuid2':
specifier: 2.2.2 specifier: 2.2.2
version: 2.2.2 version: 2.2.2
@ -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:
@ -494,8 +491,8 @@ packages:
'@octokit/types@13.10.0': '@octokit/types@13.10.0':
resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==}
'@odit/lfk-client-js@1.2.7': '@odit/lfk-client-js@1.2.5':
resolution: {integrity: sha512-sqbbTjGlalN32VPshXClR3qM0+TFgWCX9+2UCo7u/tABEIs7hsYTVXKSZ+fJNfAUCK6ZJiZV0ND6+Dcnk7s29A==} resolution: {integrity: sha512-a5vwqpjFXB5cVOCmjC/tZVi9OXJS8aMesNidSqwK2cwA/oC5yTJAqxKXGDhq9k/JLLipVGDJdaKMYmYVzRWkgA==}
'@odit/license-exporter@0.2.0': '@odit/license-exporter@0.2.0':
resolution: {integrity: sha512-RRyfQzDLoyLQlGSd8ThJQ3h0fiCe4tkmm935AUvSVQWP+p88FcnI4iaktKBJJVBnIpDhkv/7sDSA5dFc/QMM5w==} resolution: {integrity: sha512-RRyfQzDLoyLQlGSd8ThJQ3h0fiCe4tkmm935AUvSVQWP+p88FcnI4iaktKBJJVBnIpDhkv/7sDSA5dFc/QMM5w==}
@ -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}
@ -2496,7 +2412,7 @@ snapshots:
dependencies: dependencies:
'@octokit/openapi-types': 24.2.0 '@octokit/openapi-types': 24.2.0
'@odit/lfk-client-js@1.2.7': {} '@odit/lfk-client-js@1.2.5': {}
'@odit/license-exporter@0.2.0': '@odit/license-exporter@0.2.0':
dependencies: dependencies:
@ -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

@ -41,7 +41,7 @@
import Settings from "./components/settings/Settings.svelte"; import Settings from "./components/settings/Settings.svelte";
import Transition from "./components/base/Transition.svelte"; import Transition from "./components/base/Transition.svelte";
import Orgs from "./components/orgs/Orgs.svelte"; import Orgs from "./components/orgs/Orgs.svelte";
import CardAssignment from "./components/tools/CardAssignment.svelte"; import CardAssignment from "./components/general/CardAssignment.svelte";
import Runners from "./components/runners/Runners.svelte"; import Runners from "./components/runners/Runners.svelte";
import Footer from "./components/general/Footer.svelte"; import Footer from "./components/general/Footer.svelte";
import TracksOverview from "./components/tracks/TracksOverview.svelte"; import TracksOverview from "./components/tracks/TracksOverview.svelte";
@ -70,9 +70,7 @@
import Cards from "./components/cards/Cards.svelte"; import Cards from "./components/cards/Cards.svelte";
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/general/CardReplacement.svelte";
import ScanClient from "./components/tools/ScanClient.svelte";
import DonationCreate from "./components/tools/DonationCreate.svelte";
store.init(); store.init();
</script> </script>
@ -128,25 +126,21 @@
<Route path="/:trackid" let:params /> <Route path="/:trackid" let:params />
</Route> </Route>
<Route path="/runners/*"> <Route path="/runners/*">
<Route path="/" let:meta> <Route path="/">
<Runners created_via={meta.query.created_via} /> <Runners created_via="all" />
</Route> </Route>
<Route path="/:runnerid" let:params> <Route path="/:runnerid" let:params>
<RunnerDetail {params} /> <RunnerDetail {params} />
</Route> </Route>
</Route> </Route>
<Route path="/tools/*"> <Route path="/cardassignment/*">
<Route path="/cardassignment/"> <Route path="/">
<CardAssignment /> <CardAssignment />
</Route> </Route>
<Route path="/cardreplacement/"> </Route>
<CardReplacement /> <Route path="/cardreplacement/*">
</Route> <Route path="/">
<Route path="/scanclient/"> <CardReplacement />
<ScanClient />
</Route>
<Route path="/donationcreate/">
<DonationCreate />
</Route> </Route>
</Route> </Route>
<Route path="/teams/*"> <Route path="/teams/*">

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>

File diff suppressed because it is too large Load Diff

View File

@ -220,7 +220,7 @@
<StatCard <StatCard
title={$_("runner_via_selfservice")} title={$_("runner_via_selfservice")}
value={stats.runnersViaSelfservice} value={stats.runnersViaSelfservice}
href="/runners/?created_via=selfservice" href="/runners/"
> >
<svg <svg
height="24" height="24"
@ -237,7 +237,7 @@
<StatCard <StatCard
title={$_('runners_via_kiosk')} title={$_('runners_via_kiosk')}
value={stats.runnersViaKiosk} value={stats.runnersViaKiosk}
href="/runners/?created_via=kiosk" href="/runners/"
> >
<svg <svg
height="24" height="24"

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

@ -1,342 +1,337 @@
<script> <script>
import { import {
RunnerOrganizationService, RunnerOrganizationService,
RunnerService, RunnerService,
RunnerTeamService, RunnerTeamService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import { import {
createSvelteTable, createSvelteTable,
flexRender, flexRender,
getCoreRowModel, getCoreRowModel,
getFilteredRowModel, getFilteredRowModel,
getPaginationRowModel, getPaginationRowModel,
getSortedRowModel, getSortedRowModel,
renderComponent, renderComponent,
} from "@tanstack/svelte-table"; } from "@tanstack/svelte-table";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte"; import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte"; import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte";
import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte"; import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte";
import InputElement from "../shared/InputElement.svelte"; import InputElement from "../shared/InputElement.svelte";
import TableActions from "../shared/TableActions.svelte"; import TableActions from "../shared/TableActions.svelte";
import { groupFilter } from "../shared/tablefilters"; import { groupFilter } from "../shared/tablefilters";
import DeleteRunnerModal from "./DeleteRunnerModal.svelte"; import DeleteRunnerModal from "./DeleteRunnerModal.svelte";
import TableBottom from "../shared/TableBottom.svelte"; import TableBottom from "../shared/TableBottom.svelte";
import TableHeader from "../shared/TableHeader.svelte"; import TableHeader from "../shared/TableHeader.svelte";
$: selectedRunners = $: selectedRunners =
$table?.getSelectedRowModel().rows.map((row) => row.original) || []; $table?.getSelectedRowModel().rows.map((row) => row.original) || [];
$: selected = $: selected =
$table?.getSelectedRowModel().rows.map((row) => row.index) || []; $table?.getSelectedRowModel().rows.map((row) => row.index) || [];
$: active_delete = undefined; $: active_delete = undefined;
let dataLoaded = false; let dataLoaded = false;
export let created_via = "all"; export let created_via = "all";
export let current_runners = []; export let current_runners = [];
$: sponsoring_contracts_show = selected.length > 0; $: sponsoring_contracts_show = selected.length > 0;
$: cards_show = selected.length > 0; $: cards_show = selected.length > 0;
$: certificates_show = selected.length > 0; $: certificates_show = selected.length > 0;
$: teams = []; $: teams = [];
$: orgs = []; $: orgs = [];
export const addRunners = (runners) => { export const addRunners = (runners) => {
current_runners = current_runners.concat(...runners); current_runners = current_runners.concat(...runners);
options.update((options) => ({ options.update((options) => ({
...options, ...options,
data: current_runners, data: current_runners,
})); }));
}; };
//Section table //Section table
const columns = [ const columns = [
{ {
accessorKey: "id", accessorKey: "id",
header: () => "id", header: () => "id",
filterFn: `equalsString`, filterFn: `equalsString`,
}, },
{ {
accessorKey: "firstname", accessorKey: "firstname",
header: () => $_("first-name"), header: () => $_("first-name"),
filterFn: `includesString`, filterFn: `includesString`,
}, },
{ {
accessorKey: "middlename", accessorKey: "middlename",
header: () => $_("middle-name"), header: () => $_("middle-name"),
cell: (info) => { cell: (info) => {
if (!info || !info.getValue()) { if (!info || !info.getValue()) {
return ""; return "";
} }
return info.getValue(); return info.getValue();
}, },
filterFn: `includesString`, filterFn: `includesString`,
}, },
{ {
accessorKey: "lastname", accessorKey: "lastname",
header: () => $_("last-name"), header: () => $_("last-name"),
filterFn: `includesString`, filterFn: `includesString`,
}, },
{ {
accessorKey: "created_via", accessorKey: "created_via",
header: () => "created_via", header: () => "created_via",
filterFn: `includesString`, filterFn: `includesString`,
}, },
{ {
accessorKey: "group", accessorKey: "group",
header: () => $_("group"), header: () => $_("group"),
cell: (info) => { cell: (info) => {
const group = info.getValue(); const group = info.getValue();
if (group.responseType === "RUNNERORGANIZATION") { if (group.responseType === "RUNNERORGANIZATION") {
return group.name; return group.name;
} }
return `${group.parentGroup.name} > ${group.name}`; return `${group.parentGroup.name} > ${group.name}`;
}, },
filterFn: `group`, filterFn: `group`,
sortingFn: (rowA, rowB, col) => { sortingFn: (rowA, rowB, col) => {
return rowA.original.group.name.localeCompare(rowB.original.group.name); return rowA.original.group.name.localeCompare(rowB.original.group.name);
}, },
}, },
{ {
accessorKey: "distance", accessorKey: "distance",
header: () => $_("distance"), header: () => $_("distance"),
sortingFn: (rowA, rowB, col) => { sortingFn: (rowA, rowB, col) => {
return rowA.original.distance > rowB.original.distance; return rowA.original.distance > rowB.original.distance;
}, },
cell: (info) => { cell: (info) => {
if (info.getValue() < 1000) { if (info.getValue() < 1000) {
return `${info.getValue()} m`; return `${info.getValue()} m`;
} }
return `${(info.getValue() / 1000).toFixed(1)} km`; return `${(info.getValue() / 1000).toFixed(1)} km`;
}, },
enableColumnFilter: false, enableColumnFilter: false,
}, },
{ {
accessorKey: "actions", accessorKey: "actions",
header: () => $_("action"), header: () => $_("action"),
cell: (info) => { cell: (info) => {
return renderComponent(TableActions, { return renderComponent(TableActions, {
detailsLink: `/runners/${info.row.original.id}`, detailsLink: `/runners/${info.row.original.id}`,
deleteAction: () => { deleteAction: () => {
active_delete = active_delete =
current_runners[ current_runners[
current_runners.findIndex((r) => r.id == info.row.original.id) current_runners.findIndex((r) => r.id == info.row.original.id)
]; ];
}, },
deleteEnabled: deleteEnabled:
store.state.jwtinfo.userdetails.permissions.includes( store.state.jwtinfo.userdetails.permissions.includes(
"RUNNER:DELETE" "RUNNER:DELETE"
), ),
}); });
}, },
enableColumnFilter: false, enableColumnFilter: false,
enableSorting: false, enableSorting: false,
}, },
]; ];
const options = writable({ const options = writable({
data: [], data: [],
columns: columns, columns: columns,
filterFns: { filterFns: {
group: groupFilter, group: groupFilter,
}, },
initialState: { initialState: {
pagination: { pagination: {
pageSize: 50, pageSize: 50,
}, },
}, },
enableRowSelection: true, enableRowSelection: true,
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(), getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(), getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(), getSortedRowModel: getSortedRowModel(),
}); });
const table = createSvelteTable(options); const table = createSvelteTable(options);
async function deleteRunner(delete_runner_id) { async function deleteRunner(delete_runner_id) {
await RunnerService.runnerControllerRemove(delete_runner_id, true); await RunnerService.runnerControllerRemove(delete_runner_id, true);
current_runners = current_runners.filter((r) => r.id !== delete_runner_id); current_runners = current_runners.filter((r) => r.id !== delete_runner_id);
options.update((options) => ({ options.update((options) => ({
...options, ...options,
data: current_runners, data: current_runners,
})); }));
toast.success($_("runner-deleted")); toast.success($_("runner-deleted"));
} }
onMount(async () => { onMount(async () => {
RunnerTeamService.runnerTeamControllerGetAll().then((val) => { RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
teams = val; teams = val;
}); });
RunnerOrganizationService.runnerOrganizationControllerGetAll().then( RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
(val) => { (val) => {
orgs = val; orgs = val;
} }
); );
let page = 0; let page = 0;
while (page >= 0) { while (page >= 0) {
const runners = await RunnerService.runnerControllerGetAll( const runners = await RunnerService.runnerControllerGetAll(
page, page,
500, 500,
); created_via
if (runners.length == 0) { );
page = -2; if (runners.length == 0) {
} page = -2;
}
current_runners = current_runners.concat(...runners); current_runners = current_runners.concat(...runners);
options.update((options) => ({ options.update((options) => ({
...options, ...options,
data: current_runners, data: current_runners,
})); }));
dataLoaded = true; dataLoaded = true;
page++; page++;
} }
}); });
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import store from "../../store"; import store from "../../store";
import AddRunnerModal from "./AddRunnerModal.svelte"; import AddRunnerModal from "./AddRunnerModal.svelte";
import ImportRunnerModal from "./ImportRunnerModal.svelte"; import ImportRunnerModal from "./ImportRunnerModal.svelte";
import toast from "svelte-french-toast"; import toast from "svelte-french-toast";
$: current_runners = []; $: current_runners = [];
export let modal_open = false; export let modal_open = false;
export let import_modal_open = false; export let import_modal_open = false;
if (created_via != "all") {
$table.setColumnFilters([
{
id: "created_via",
value: created_via,
},
]);
}
</script> </script>
<section class="container p-5"> <section class="container p-5">
<h4 class="mb-1 text-3xl font-extrabold leading-tight"> <h4 class="mb-1 text-3xl font-extrabold leading-tight">
{$_("runners")} {$_("runners")}
</h4> </h4>
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:CREATE")} {#if created_via !== "all"}
<button <p>created_via={created_via}</p>
on:click={() => { {/if}
modal_open = true; {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:CREATE")}
}} <button
type="button" on:click={() => {
class="w-full inline-flex 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" modal_open = true;
> }}
{$_("laeufer-hinzufuegen")} type="button"
</button> class="w-full inline-flex 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"
<button >
on:click={() => { {$_("laeufer-hinzufuegen")}
import_modal_open = true; </button>
}} <button
type="button" on:click={() => {
class="w-full inline-flex 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" import_modal_open = true;
> }}
{$_("import-runners")} type="button"
</button> class="w-full inline-flex 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"
{/if} >
<DeleteRunnerModal {$_("import-runners")}
delete_runner={active_delete} </button>
modal_open={active_delete != undefined} {/if}
on:delete={(event) => { <DeleteRunnerModal
deleteRunner(event.detail.id); delete_runner={active_delete}
}} modal_open={active_delete != undefined}
/> on:delete={(event) => {
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")} deleteRunner(event.detail.id);
{#if !dataLoaded} }}
<div />
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")}
role="alert" {#if !dataLoaded}
> <div
<p class="font-bold">{$_("runners-are-being-loaded")}</p> class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
<p class="text-sm">{$_("this-might-take-a-moment")}</p> role="alert"
</div> >
{:else} <p class="font-bold">{$_("runners-are-being-loaded")}</p>
<GenerateSponsoringContracts <p class="text-sm">{$_("this-might-take-a-moment")}</p>
bind:sponsoring_contracts_show </div>
bind:generate_runners={selectedRunners} {:else}
/> <GenerateSponsoringContracts
<GenerateRunnerCards bind:sponsoring_contracts_show
bind:cards_show bind:generate_runners={selectedRunners}
bind:generate_runners={selectedRunners} />
/> <GenerateRunnerCards
<GenerateRunnerCertificates bind:cards_show
bind:certificates_show bind:generate_runners={selectedRunners}
bind:generate_runners={selectedRunners} />
/> <GenerateRunnerCertificates
<div class="overflow-x-auto"> bind:certificates_show
<table class="w-full"> bind:generate_runners={selectedRunners}
<thead class="border-b border-gray-400"> />
{#each $table.getHeaderGroups() as headerGroup} <div class="overflow-x-auto">
<tr class="select-none"> <table class="w-full">
<th class="inset-y-0 left-0 px-4 py-2 text-left w-px"> <thead class="border-b border-gray-400">
<InputElement {#each $table.getHeaderGroups() as headerGroup}
type="checkbox" <tr class="select-none">
checked={$table.getIsAllRowsSelected()} <th class="inset-y-0 left-0 px-4 py-2 text-left w-px">
indeterminate={$table.getIsSomeRowsSelected()} <InputElement
on:change={() => $table.toggleAllRowsSelected()} type="checkbox"
/> checked={$table.getIsAllRowsSelected()}
</th> indeterminate={$table.getIsSomeRowsSelected()}
{#each headerGroup.headers as header} on:change={() => $table.toggleAllRowsSelected()}
<TableHeader {header} /> />
{/each} </th>
</tr> {#each headerGroup.headers as header}
{/each} <TableHeader {header} />
</thead> {/each}
<tbody> </tr>
{#each $table.getRowModel().rows as row} {/each}
<tr class="odd:bg-white even:bg-gray-100"> </thead>
<td class="inset-y-0 left-0 px-4 py-2 text-center w-px"> <tbody>
<InputElement {#each $table.getRowModel().rows as row}
type="checkbox" <tr class="odd:bg-white even:bg-gray-100">
checked={row.getIsSelected()} <td class="inset-y-0 left-0 px-4 py-2 text-center w-px">
on:change={() => row.toggleSelected()} <InputElement
/> type="checkbox"
</td> checked={row.getIsSelected()}
{#each row.getVisibleCells() as cell} on:change={() => row.toggleSelected()}
<td> />
<svelte:component </td>
this={flexRender( {#each row.getVisibleCells() as cell}
cell.column.columnDef.cell, <td>
cell.getContext() <svelte:component
)} this={flexRender(
/> cell.column.columnDef.cell,
</td> cell.getContext()
{/each} )}
</tr> />
{/each} </td>
</tbody> {/each}
</table> </tr>
</div> {/each}
<div class="h-2" /> </tbody>
{/if} </table>
{/if} </div>
<TableBottom {table} {selected} /> <div class="h-2" />
{/if}
{/if}
<TableBottom {table} {selected} />
</section> </section>
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:CREATE")} {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:CREATE")}
<AddRunnerModal <AddRunnerModal
bind:modal_open bind:modal_open
on:created={(event) => { on:created={(event) => {
addRunners(event.detail.runners); addRunners(event.detail.runners);
}} }}
/> />
<ImportRunnerModal <ImportRunnerModal
on:cancelDelete={(event) => { on:cancelDelete={(event) => {
import_modal_open = false; import_modal_open = false;
}} }}
passed_team={{}} passed_team={{}}
passed_orgs={[]} passed_orgs={[]}
passed_org={{}} passed_org={{}}
opened_from="RunnerOverview" opened_from="RunnerOverview"
bind:import_modal_open bind:import_modal_open
on:created={(event) => { on:created={(event) => {
addRunners(event.detail.runners); addRunners(event.detail.runners);
}} }}
/> />
{/if} {/if}
<style> <style>
table tbody tr td:nth-child(2) { table tbody tr td:nth-child(2) {
font-family: monospace; font-family: monospace;
} }
</style> </style>

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.",
@ -305,18 +296,15 @@
"logout": "Abmelden", "logout": "Abmelden",
"mail-validation-in-progress": "E-Mail Verifizierung läuft... ", "mail-validation-in-progress": "E-Mail Verifizierung läuft... ",
"manage-admin-users": "Nutzer verwalten", "manage-admin-users": "Nutzer verwalten",
"management": "Verwaltung",
"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",
@ -387,14 +375,13 @@
"profile-deleted": "Profil gelöscht!", "profile-deleted": "Profil gelöscht!",
"profile-picture": "Profilbild", "profile-picture": "Profilbild",
"profile-updated": "Profil wurde aktualisiert!", "profile-updated": "Profil wurde aktualisiert!",
"quick-tools": "Werkzeuge",
"read-license": "Lizenz-Text lesen", "read-license": "Lizenz-Text lesen",
"receipt-needed": "Spendenquittung benötigt", "receipt-needed": "Spendenquittung benötigt",
"repo_link": "Link", "repo_link": "Link",
"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 +403,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 +412,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)",
@ -454,7 +439,6 @@
"status": "Status", "status": "Status",
"stuff-that-could-harm-your-profile": "Einstellungen, die deinem Profil nachhaltig schaden können", "stuff-that-could-harm-your-profile": "Einstellungen, die deinem Profil nachhaltig schaden können",
"successful-password-reset": "Passwort erfolgreich zurückgesetzt!", "successful-password-reset": "Passwort erfolgreich zurückgesetzt!",
"system": "System",
"team": "Team", "team": "Team",
"team-added": "Team wurde erstellt", "team-added": "Team wurde erstellt",
"team-deleted": "Team gelöscht", "team-deleted": "Team gelöscht",

View File

@ -51,7 +51,7 @@
"author": "Author", "author": "Author",
"available-permissions": "available", "available-permissions": "available",
"average-distance": "∅ distance", "average-distance": "∅ distance",
"average-donation": "∅ Donation", "average-donation": "∅ donation",
"barcode_scanner": "Scan via barcode scanner", "barcode_scanner": "Scan via barcode scanner",
"by": "by", "by": "by",
"cancel": "Cancel", "cancel": "Cancel",
@ -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",
@ -386,7 +375,6 @@
"profile-deleted": "Profile deleted!", "profile-deleted": "Profile deleted!",
"profile-picture": "Profile Picture", "profile-picture": "Profile Picture",
"profile-updated": "Profile updated!", "profile-updated": "Profile updated!",
"quick-tools": "Tools",
"read-license": "Read License", "read-license": "Read License",
"receipt-needed": "Receipt needed", "receipt-needed": "Receipt needed",
"repo_link": "Link", "repo_link": "Link",
@ -400,7 +388,7 @@
"runner-is-being-added": "Runner is being added...", "runner-is-being-added": "Runner is being added...",
"runner-updated": "Runner updated!", "runner-updated": "Runner updated!",
"runner_not_found": "Runner not found...", "runner_not_found": "Runner not found...",
"runner_via_selfservice": "Runners via Selfservice", "runner_via_selfservice": "Runner via Selfservice",
"runnercards": "Runnercards", "runnercards": "Runnercards",
"runnerimport_verify_runners_org": "Please confirm these runners for import into the organization \"{org_name}\"", "runnerimport_verify_runners_org": "Please confirm these runners for import into the organization \"{org_name}\"",
"runners": "Runners", "runners": "Runners",
@ -415,7 +403,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 +412,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)",
@ -453,7 +439,6 @@
"status": "Status", "status": "Status",
"stuff-that-could-harm-your-profile": "Stuff that could harm your profile", "stuff-that-could-harm-your-profile": "Stuff that could harm your profile",
"successful-password-reset": "Successful password reset!", "successful-password-reset": "Successful password reset!",
"system": "System",
"team": "Team", "team": "Team",
"team-added": "Team added", "team-added": "Team added",
"team-deleted": "Team deleted", "team-deleted": "Team deleted",
@ -484,11 +469,11 @@
"token": "Token", "token": "Token",
"total-distance": "total distance", "total-distance": "total distance",
"total-donation-amount": "Total donations", "total-donation-amount": "Total donations",
"total-donation-count": "Donations (count)", "total-donation-count": "total donations (count)",
"total-donations": "Donations (amount)", "total-donations": "total donations",
"total-donors": "Donors", "total-donors": "total donors",
"total-paid-amount": "Paid", "total-paid-amount": "Paid",
"total-scans": "Scans", "total-scans": "total scans",
"total_donation_amount_in_eur": "Total donation amount in €", "total_donation_amount_in_eur": "Total donation amount in €",
"track": "Track", "track": "Track",
"track-added": "Track added", "track-added": "Track added",

View File

@ -43,8 +43,8 @@ const store = () => {
// //
state.refreshInterval = setInterval(() => { state.refreshInterval = setInterval(() => {
this.refreshAuth(); this.refreshAuth();
// 60min // 2min
}, 60 * 60000); }, 2 * 60000);
// //
return state; return state;
}); });

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()],
}); });