Compare commits

..

6 Commits

12 changed files with 808 additions and 6 deletions

View File

@ -2,8 +2,19 @@
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.13.0](https://git.odit.services/lfk/frontend/compare/1.12.8...1.13.0)
- feat(tools): Basic mobile scanner [`500886e`](https://git.odit.services/lfk/frontend/commit/500886e4106f4b53fbc40fb0fa15653f574c8328)
- feat(tools): Added tool for fast sponsoring creation [`51ba1c8`](https://git.odit.services/lfk/frontend/commit/51ba1c852cad6243e935409da1eacecc5dcfa5fa)
- feat(dev): Enable devserver with https-support to circumvent ios https requirements for camera access [`25c38ea`](https://git.odit.services/lfk/frontend/commit/25c38ea3812a529a90294ff8834bdb65c487f8c4)
- feat(tools): Remove requirement for ten-diget codes [`80ca7aa`](https://git.odit.services/lfk/frontend/commit/80ca7aa08bdd44591e2d3efaa8e59dd4db5c864e)
- feat(dashboard): Added scanclient tool to dashboard [`06cfd60`](https://git.odit.services/lfk/frontend/commit/06cfd603cae79e0237bbece43203083f198d03a1)
#### [1.12.8](https://git.odit.services/lfk/frontend/compare/1.12.7...1.12.8) #### [1.12.8](https://git.odit.services/lfk/frontend/compare/1.12.7...1.12.8)
> 1 May 2025
- chore(release): 1.12.8 [`51d9b35`](https://git.odit.services/lfk/frontend/commit/51d9b35dc41fea0d0245fd136556f9fada3559da)
- feat(dasboard): Added section headers to main nav [`3a8533a`](https://git.odit.services/lfk/frontend/commit/3a8533a7baef02f7bc9780ce37be1a350bd92270) - feat(dasboard): Added section headers to main nav [`3a8533a`](https://git.odit.services/lfk/frontend/commit/3a8533a7baef02f7bc9780ce37be1a350bd92270)
- fic(locales): Updated dashboard translations [`5ac6fe3`](https://git.odit.services/lfk/frontend/commit/5ac6fe30b5b9e34043c734d51d5da137fdf7ac38) - fic(locales): Updated dashboard translations [`5ac6fe3`](https://git.odit.services/lfk/frontend/commit/5ac6fe30b5b9e34043c734d51d5da137fdf7ac38)
- feat(runners): Created_via filters can now be set via query params [`14501d3`](https://git.odit.services/lfk/frontend/commit/14501d3828dd0d48ba0baeeddf936ba275f7b9b7) - feat(runners): Created_via filters can now be set via query params [`14501d3`](https://git.odit.services/lfk/frontend/commit/14501d3828dd0d48ba0baeeddf936ba275f7b9b7)

View File

@ -13,7 +13,7 @@
<body> <body>
<span style="display: none; visibility: hidden" id="buildinfo" <span style="display: none; visibility: hidden" id="buildinfo"
>RELEASE_INFO-1.12.8-RELEASE_INFO</span >RELEASE_INFO-1.13.0-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.12.8", "version": "1.13.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"i18n-order": "node order.js", "i18n-order": "node order.js",
@ -21,7 +21,8 @@
"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": {
@ -51,6 +52,7 @@
"html5-qrcode": "^2.3.8", "html5-qrcode": "^2.3.8",
"localforage": "1.10.0", "localforage": "1.10.0",
"papaparse": "^5.5.2", "papaparse": "^5.5.2",
"svelecte": "3",
"svelte": "3.58.0", "svelte": "3.58.0",
"svelte-french-toast": "1.2.0", "svelte-french-toast": "1.2.0",
"svelte-i18n": "4.0.1", "svelte-i18n": "4.0.1",

189
pnpm-lock.yaml generated
View File

@ -38,6 +38,9 @@ importers:
papaparse: papaparse:
specifier: ^5.5.2 specifier: ^5.5.2
version: 5.5.2 version: 5.5.2
svelecte:
specifier: '3'
version: 3.17.3
svelte: svelte:
specifier: 3.58.0 specifier: 3.58.0
version: 3.58.0 version: 3.58.0
@ -93,6 +96,9 @@ 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:
@ -780,6 +786,9 @@ 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==}
@ -788,6 +797,9 @@ 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==}
@ -822,6 +834,10 @@ 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'}
@ -895,6 +911,10 @@ 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'}
@ -974,6 +994,10 @@ 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==}
@ -985,6 +1009,10 @@ 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==}
@ -1002,6 +1030,22 @@ 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'}
@ -1096,6 +1140,19 @@ 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'}
@ -1119,6 +1176,14 @@ 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'}
@ -1159,6 +1224,10 @@ 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==}
@ -1174,6 +1243,14 @@ 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'}
@ -1489,6 +1566,10 @@ 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'}
@ -1905,6 +1986,9 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
svelecte@3.17.3:
resolution: {integrity: sha512-wnvoRxJIFFkm+CmXgjL4R3i/TcuYUIBkE+jDJSBD7AdSOzk1K6u3+nW4zwxaGT29zyZpiZkWeiy7lO62r5F+tg==}
svelte-french-toast@1.2.0: svelte-french-toast@1.2.0:
resolution: {integrity: sha512-5PW+6RFX3xQPbR44CngYAP1Sd9oCq9P2FOox4FZffzJuZI2mHOB7q5gJBVnOiLF5y3moVGZ7u2bYt7+yPAgcEQ==} resolution: {integrity: sha512-5PW+6RFX3xQPbR44CngYAP1Sd9oCq9P2FOox4FZffzJuZI2mHOB7q5gJBVnOiLF5y3moVGZ7u2bYt7+yPAgcEQ==}
peerDependencies: peerDependencies:
@ -1926,6 +2010,9 @@ packages:
svelte-select@3.17.0: svelte-select@3.17.0:
resolution: {integrity: sha512-ITmX/XUiSdkaILmsTviKRkZPaXckM5/FA7Y8BhiUPoamaZG/ZDyOo6ydjFu9fDVFTbwoAUGUi6HBjs+ZdK2AwA==} resolution: {integrity: sha512-ITmX/XUiSdkaILmsTviKRkZPaXckM5/FA7Y8BhiUPoamaZG/ZDyOo6ydjFu9fDVFTbwoAUGUi6HBjs+ZdK2AwA==}
svelte-tiny-virtual-list@2.1.2:
resolution: {integrity: sha512-jeP/WMvgFUR4mYXHGPiCexjX5DuzSO+3xzHNhxfcsFyy+uYPtnqI5UGb383swpzQAyXB0OBqYfzpYihD/5gxnA==}
svelte-writable-derived@3.1.1: svelte-writable-derived@3.1.1:
resolution: {integrity: sha512-w4LR6/bYZEuCs7SGr+M54oipk/UQKtiMadyOhW0PTwAtJ/Ai12QS77sLngEcfBx2q4H8ZBQucc9ktSA5sUGZWw==} resolution: {integrity: sha512-w4LR6/bYZEuCs7SGr+M54oipk/UQKtiMadyOhW0PTwAtJ/Ai12QS77sLngEcfBx2q4H8ZBQucc9ktSA5sUGZWw==}
peerDependencies: peerDependencies:
@ -2018,6 +2105,12 @@ 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}
@ -2627,6 +2720,8 @@ 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
@ -2643,6 +2738,14 @@ 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: {}
@ -2686,6 +2789,11 @@ 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: {}
@ -2746,6 +2854,10 @@ 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: {}
@ -2813,6 +2925,8 @@ 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: {}
@ -2821,6 +2935,12 @@ 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: {}
@ -2836,6 +2956,21 @@ 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
@ -3001,6 +3136,17 @@ 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: {}
@ -3014,6 +3160,24 @@ 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: {}
@ -3065,6 +3229,8 @@ 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: {}
@ -3080,6 +3246,12 @@ 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
@ -3343,6 +3515,8 @@ 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
@ -3781,6 +3955,10 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {} supports-preserve-symlinks-flag@1.0.0: {}
svelecte@3.17.3:
dependencies:
svelte-tiny-virtual-list: 2.1.2
svelte-french-toast@1.2.0(svelte@3.58.0): svelte-french-toast@1.2.0(svelte@3.58.0):
dependencies: dependencies:
svelte: 3.58.0 svelte: 3.58.0
@ -3803,6 +3981,8 @@ snapshots:
svelte-select@3.17.0: {} svelte-select@3.17.0: {}
svelte-tiny-virtual-list@2.1.2: {}
svelte-writable-derived@3.1.1(svelte@3.58.0): svelte-writable-derived@3.1.1(svelte@3.58.0):
dependencies: dependencies:
svelte: 3.58.0 svelte: 3.58.0
@ -3880,6 +4060,15 @@ 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

BIN
public/error.mp3 Normal file

Binary file not shown.

View File

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

View File

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

View File

@ -0,0 +1,300 @@
<script>
import { _ } from "svelte-i18n";
import {
DonationService,
DonorService,
RunnerService,
} from "@odit/lfk-client-js";
import Svelecte from "svelecte";
import Select from "svelte-select";
import toast from "svelte-french-toast";
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 = 0;
let lastname = "";
let address_checked = false;
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: getDonorlabel(r), value: r };
});
console.log("refreshed donors");
setTimeout(() => {
loadDonors;
}, 30000);
})
.catch((err) => {
console.log("error fetching donors:", err);
});
}
loadDonors();
const getRunnerLabel = (option) =>
option.firstname + " " + (option.middlename || "") + " " + option.lastname;
const getDonorlabel = (option) => `${option.firstname} (${option.lastname})`;
const filterRunners = (label, filterText, option) => {
if (filterText.startsWith("#")) {
return option.value.id == parseInt(filterText.replace("#", ""));
}
return (
label.toLowerCase().includes(filterText.toLowerCase()) ||
option.value.toString().startsWith(filterText.toLowerCase())
);
};
function resetAll() {
runnerinfo = { id: 0, firstname: "", lastname: "" };
donorinfo = { id: 0, firstname: "", lastname: "" };
amount = 0;
}
</script>
<div class="p-4">
<h3 class="text-3xl font-bold">{$_("fast_donation_create")}</h3>
<!-- -->
<div class="grid grid-cols-6 gap-4">
<div class="col-span-2">
<h4 class="text-xl font-semibold">
{$_("runner")}
</h4>
<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"
itemFilter={(label, filterText, option) =>
filterRunners(label, filterText, option)}
items={runners}
showChevron={true}
placeholder={$_("search-for-runner-by-name-or-id")}
noOptionsMessage={$_("no-runners-found")}
on:select={(selectedValue) => {
runnerinfo = selectedValue.detail.value;
}}
on:clear={() => (runnerinfo.runner = null)}
/>
</div>
<div class="col-span-2">
<h4 class="text-xl font-semibold">
{$_("donor")}
</h4>
<div class="mb-2">
<Svelecte
name="donor_fistname"
placeholder={$_("first-name")}
clearable={true}
options={donors}
keepCreated={false}
creatable={true}
labelField="label"
on:change={(e) => {
if (!e.detail?.value) {
donorinfo = { id: 0, firstname: "", lastname: "" };
return;
}
if (!e.detail?.$created) {
donorinfo = e.detail.value;
lastname = e.detail.value.lastname;
} else {
console.log("created option", e);
donorinfo.firstname = e.detail.value;
}
}}
class="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-0.5"
/>
<input
autocomplete="off"
placeholder={$_("last-name")}
class:border-red-500={donorinfo.lastname?.length == 0}
class:focus:border-red-500={donorinfo.lastname?.length == 0}
class:focus:ring-red-500={donorinfo.lastname?.length == 0}
bind:value={lastname}
on:input={e => {
donorinfo.lastname = e.target.value;
}}
type="text"
name="lastname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2"
/>
</div>
<div class="flex items-start">
<div class="flex items-center h-5">
{#if donorinfo.id == 0}
<input
bind:checked={address_checked}
id="comments"
name="comments"
type="checkbox"
class="focus:ring-indigo-500 size-4 text-indigo-600 border-gray-300 rounded"
/>
{:else}
<input
checked={true}
disabled
id="comments"
name="comments"
type="checkbox"
class="focus:ring-indigo-500 size-4 text-indigo-600 border-gray-300 rounded"
/>
{/if}
</div>
<div class="ml-3 text-sm">
<label for="comments" class="font-semibold text-gray-700"
>{$_("receipt-needed")}</label
>
</div>
</div>
{#if address_checked}
<div class="col-span-6">
<label for="address1" class="block text-sm font-medium text-gray-700"
>{$_("address")}</label
>
<input
autocomplete="off"
placeholder="Address"
class:border-red-500={address.address1.length == 0}
class:focus:border-red-500={address.address1.length == 0}
class:focus:ring-red-500={address.address1.length == 0}
bind:value={address.address1}
type="text"
name="address1"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2"
/>
</div>
<div class="col-span-6">
<label for="address2" class="block text-sm font-medium text-gray-700"
>{$_("apartment-suite-etc")}</label
>
<input
autocomplete="off"
placeholder={$_("apartment-suite-etc")}
bind:value={address.address2}
type="text"
name="address2"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2"
/>
</div>
<div class="col-span-6">
<label for="zipcode" class="block text-sm font-medium text-gray-700"
>{$_("zip-postal-code")}</label
>
<input
autocomplete="off"
placeholder={$_("zip-postal-code")}
class:border-red-500={address.postalcode.length == 0}
class:focus:border-red-500={address.postalcode.length == 0}
class:focus:ring-red-500={address.postalcode.length == 0}
bind:value={address.postalcode}
type="text"
name="zipcode"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2"
/>
</div>
<div class="col-span-6">
<label for="city" class="block text-sm font-medium text-gray-700"
>City</label
>
<input
autocomplete="off"
placeholder="City"
class:border-red-500={address.city.length == 0}
class:focus:border-red-500={address.city.length == 0}
class:focus:ring-red-500={address.city.length == 0}
bind:value={address.city}
type="text"
name="city"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2"
/>
</div>
{/if}
</div>
<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}
type="number"
step="0.01"
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="2.00"
/>
<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>
<div>
<h4 class="text-xl font-semibold">
{$_("confirm")}
</h4>
<button
disabled={amount <= 0 ||
runnerinfo.id == 0 ||
(donorinfo.firstname.length == 0 || donorinfo.lastname.length == 0)}
class="py-2 px-4 text-center inline-flex items-center text-md 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"
on:click={async () => {
toast.loading($_("creating-donation"));
if (donorinfo.id == 0) {
if (!address_checked) {
address = null
}
donorinfo = await DonorService.donorControllerPost({
firstname: donorinfo.firstname,
lastname: lastname,
receiptNeeded: address_checked,
address: address,
});
loadDonors();
}
await DonationService.donationControllerPostDistance({
amountPerDistance: amount*100,
runner: runnerinfo.id,
donor: donorinfo.id,
});
toast.dismiss();
toast.success($_("donation-created"));
resetAll();
}}>{$_("create")}</button
>
{amount <= 0 ||
runnerinfo.id == 0 ||
(donorinfo.firstname.length == 0 && donorinfo.lastname.length == 0)}
{amount} - {runnerinfo.id} - {donorinfo.id} - {donorinfo.firstname} - {donorinfo.lastname}
</div>
</div>
</div>
<style>
:global(:root) {
--sv-bg: #ffffff;
}
</style>

View File

@ -0,0 +1,239 @@
<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

@ -134,6 +134,7 @@
"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",
@ -197,13 +198,15 @@
"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-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", "donor": "Sponsor:in",
"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",
@ -229,6 +232,7 @@
"everything-concerning-your-profile": "Alles zu deinem Profil", "everything-concerning-your-profile": "Alles zu deinem Profil",
"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",
@ -300,6 +304,7 @@
"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!",
@ -383,7 +388,7 @@
"request-a-new-reset-mail": "Neue Reset-Mail anfordern", "request-a-new-reset-mail": "Neue Reset-Mail anfordern",
"reset-my-password": "Passwort zurücksetzen", "reset-my-password": "Passwort zurücksetzen",
"reset-password": "Passwort zurücksetzen", "reset-password": "Passwort zurücksetzen",
"runner": "Läufer", "runner": "Läufer:in",
"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",
@ -405,6 +410,7 @@
"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",

View File

@ -134,6 +134,7 @@
"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",
@ -197,7 +198,9 @@
"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-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",
@ -229,6 +232,7 @@
"everything-concerning-your-profile": "Everything concerning your profile", "everything-concerning-your-profile": "Everything concerning your profile",
"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",
@ -299,6 +303,7 @@
"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!",
@ -404,6 +409,7 @@
"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",

View File

@ -2,7 +2,8 @@
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()], plugins: [svelte(), tailwindcss(), mkcert()],
}); });