Compare commits

..

5 Commits

Author SHA1 Message Date
c3226c37c9 wip 2025-04-02 22:03:06 +02:00
210140fd67 wip 2025-04-02 21:58:07 +02:00
35e58d233e wip 2025-04-02 21:55:21 +02:00
a09bf31e22 wip 2025-04-02 21:48:51 +02:00
7c31fba83a wip 2025-04-02 21:48:37 +02:00
18 changed files with 189 additions and 544 deletions

View File

@@ -2,66 +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.10.3](https://git.odit.services/lfk/frontend/compare/1.10.2...1.10.3)
- feat(pdf): Send selfservicelink for generation [`01e77a9`](https://git.odit.services/lfk/frontend/commit/01e77a97f3f28700e0249d35afd9641b56d9c55d)
- chore(deps): Bump @odit/lfk-client-js [`10824b5`](https://git.odit.services/lfk/frontend/commit/10824b5d9b207e14a37fa23e90d54337d76e60a9)
#### [1.10.2](https://git.odit.services/lfk/frontend/compare/1.10.1...1.10.2)
> 11 April 2025
- refactor(runners): filter table for created_via [`785b9e0`](https://git.odit.services/lfk/frontend/commit/785b9e0b60a9961f99d0c519d6bb12dc735ac605)
- chore(release): 1.10.2 [`d9870e0`](https://git.odit.services/lfk/frontend/commit/d9870e03bc3175ee9b299174a19f257d6046a718)
#### [1.10.1](https://git.odit.services/lfk/frontend/compare/1.10.0...1.10.1)
> 9 April 2025
- feat: runner list filtered by created_via [`991716a`](https://git.odit.services/lfk/frontend/commit/991716a7f55d0414111ad264ad1e93de9e82971a)
- chore(release): 1.10.1 [`fce2bc6`](https://git.odit.services/lfk/frontend/commit/fce2bc645e040322f4d1b98a1ed1ab5df7227b6d)
#### [1.10.0](https://git.odit.services/lfk/frontend/compare/1.9.11...1.10.0)
> 9 April 2025
- feat: working CardAssignment [`ac4ef8f`](https://git.odit.services/lfk/frontend/commit/ac4ef8fb6ded5c5d5678a651420e356b3ef45399)
- chore(release): 1.10.0 [`aa720f2`](https://git.odit.services/lfk/frontend/commit/aa720f2460079e35eed9d87a2ac580a3305efbd5)
#### [1.9.11](https://git.odit.services/lfk/frontend/compare/1.9.10...1.9.11)
> 8 April 2025
- feat(dash): add runnersViaKiosk [`5291f8a`](https://git.odit.services/lfk/frontend/commit/5291f8a4d1721cd0c745191ebc85f221c34a23c8)
- chore(release): 1.9.11 [`eae0afd`](https://git.odit.services/lfk/frontend/commit/eae0afda23a54020e25821c0188d8cbec3139593)
#### [1.9.10](https://git.odit.services/lfk/frontend/compare/1.9.9...1.9.10)
> 8 April 2025
- feat: add experimental ui for mobile card assignment [`d7c9c27`](https://git.odit.services/lfk/frontend/commit/d7c9c27ec7a1fea1cbaf26914843d044bbae32fe)
- chore(release): 1.9.10 [`e2d7de1`](https://git.odit.services/lfk/frontend/commit/e2d7de1e9e1fd134f54876fa80f19f94fbea3672)
#### [1.9.9](https://git.odit.services/lfk/frontend/compare/1.9.8...1.9.9)
> 4 April 2025
- chore(release): 1.9.9 [`153b1b3`](https://git.odit.services/lfk/frontend/commit/153b1b3c2badee4826be614c3dbaafc10e1fbfea)
- fix(CopyScanStationTokenModal): code sizes [`ec63c7c`](https://git.odit.services/lfk/frontend/commit/ec63c7c1c51ccaf25bdd1eacffda66c820003a4c)
#### [1.9.8](https://git.odit.services/lfk/frontend/compare/1.9.7...1.9.8)
> 2 April 2025
- feat(GenerateSponsoringContracts): show download progress [`e261d5e`](https://git.odit.services/lfk/frontend/commit/e261d5e345f3175672bf86646ed838dd23400e50)
- chore(release): 1.9.8 [`05c2535`](https://git.odit.services/lfk/frontend/commit/05c253569877a45f3c4759262255ca70aa9ee4a3)
#### [1.9.7](https://git.odit.services/lfk/frontend/compare/1.9.6...1.9.7) #### [1.9.7](https://git.odit.services/lfk/frontend/compare/1.9.6...1.9.7)
> 2 April 2025
- fix: ImportRunnerModal scrolling & team select [`766eeab`](https://git.odit.services/lfk/frontend/commit/766eeab49fb3ca5715c19dbf9bc53cb71124d3df) - fix: ImportRunnerModal scrolling & team select [`766eeab`](https://git.odit.services/lfk/frontend/commit/766eeab49fb3ca5715c19dbf9bc53cb71124d3df)
- chore(release): 1.9.7 [`c00497d`](https://git.odit.services/lfk/frontend/commit/c00497d7760a935965cc83213f72f35999a3c168)
#### [1.9.6](https://git.odit.services/lfk/frontend/compare/1.9.5...1.9.6) #### [1.9.6](https://git.odit.services/lfk/frontend/compare/1.9.5...1.9.6)

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.10.3-RELEASE_INFO</span >RELEASE_INFO-1.9.7-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.10.3", "version": "1.9.7",
"type": "module", "type": "module",
"scripts": { "scripts": {
"i18n-order": "node order.js", "i18n-order": "node order.js",
@@ -43,13 +43,12 @@
}, },
"dependencies": { "dependencies": {
"@fontsource/athiti": "^5.2.5", "@fontsource/athiti": "^5.2.5",
"@odit/lfk-client-js": "1.2.4", "@odit/lfk-client-js": "1.2.0",
"@paralleldrive/cuid2": "2.2.2", "@paralleldrive/cuid2": "2.2.2",
"@tanstack/svelte-table": "8.9.1", "@tanstack/svelte-table": "8.9.1",
"bwip-js": "3.4.0", "bwip-js": "3.4.0",
"check-password-strength": "2.0.10", "check-password-strength": "2.0.10",
"csvtojson": "2.0.10", "csvtojson": "2.0.10",
"html5-qrcode": "^2.3.8",
"localforage": "1.10.0", "localforage": "1.10.0",
"marked": "4.3.0", "marked": "4.3.0",
"svelte": "3.58.0", "svelte": "3.58.0",

36
pnpm-lock.yaml generated
View File

@@ -12,8 +12,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.4 specifier: 1.2.0
version: 1.2.4 version: 1.2.0
'@paralleldrive/cuid2': '@paralleldrive/cuid2':
specifier: 2.2.2 specifier: 2.2.2
version: 2.2.2 version: 2.2.2
@@ -29,9 +29,6 @@ importers:
csvtojson: csvtojson:
specifier: 2.0.10 specifier: 2.0.10
version: 2.0.10 version: 2.0.10
html5-qrcode:
specifier: ^2.3.8
version: 2.3.8
localforage: localforage:
specifier: 1.10.0 specifier: 1.10.0
version: 1.10.0 version: 1.10.0
@@ -80,7 +77,7 @@ importers:
version: 3.3.3(prettier@3.5.3)(svelte@3.58.0) version: 3.3.3(prettier@3.5.3)(svelte@3.58.0)
release-it: release-it:
specifier: 17.10.0 specifier: 17.10.0
version: 17.10.0(typescript@5.8.3) version: 17.10.0
svelte-select: svelte-select:
specifier: 3.17.0 specifier: 3.17.0
version: 3.17.0 version: 3.17.0
@@ -355,8 +352,8 @@ packages:
'@octokit/types@13.6.1': '@octokit/types@13.6.1':
resolution: {integrity: sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==} resolution: {integrity: sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==}
'@odit/lfk-client-js@1.2.4': '@odit/lfk-client-js@1.2.0':
resolution: {integrity: sha512-eJRsjtpMm/VsQ1v2I+inMWCZmzL+WoOvsA+hj8IGsyCVn0td+z/HAwQ0SuXXNZpLPL3qSlENHXjFNrgztExEgA==} resolution: {integrity: sha512-RR/Ij3PDMF840VJtphO51k+3voJcTlvRIxzTFBkvrwriBmLJwchBQxq40K4/kyVIFH7lLwO3uJy0PaxgEoTbFQ==}
'@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==}
@@ -927,9 +924,6 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
html5-qrcode@2.3.8:
resolution: {integrity: sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ==}
http-proxy-agent@7.0.2: http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
@@ -1800,11 +1794,6 @@ packages:
type@2.7.2: type@2.7.2:
resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==}
typescript@5.8.3:
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
engines: {node: '>=14.17'}
hasBin: true
uglify-js@3.19.3: uglify-js@3.19.3:
resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==}
engines: {node: '>=0.8.0'} engines: {node: '>=0.8.0'}
@@ -2176,7 +2165,7 @@ snapshots:
dependencies: dependencies:
'@octokit/openapi-types': 22.2.0 '@octokit/openapi-types': 22.2.0
'@odit/lfk-client-js@1.2.4': {} '@odit/lfk-client-js@1.2.0': {}
'@odit/license-exporter@0.2.0': '@odit/license-exporter@0.2.0':
dependencies: dependencies:
@@ -2450,14 +2439,12 @@ snapshots:
graceful-fs: 4.2.11 graceful-fs: 4.2.11
xdg-basedir: 5.1.0 xdg-basedir: 5.1.0
cosmiconfig@9.0.0(typescript@5.8.3): cosmiconfig@9.0.0:
dependencies: dependencies:
env-paths: 2.2.1 env-paths: 2.2.1
import-fresh: 3.3.0 import-fresh: 3.3.0
js-yaml: 4.1.0 js-yaml: 4.1.0
parse-json: 5.2.0 parse-json: 5.2.0
optionalDependencies:
typescript: 5.8.3
crc-32@1.2.2: {} crc-32@1.2.2: {}
@@ -2778,8 +2765,6 @@ snapshots:
dependencies: dependencies:
function-bind: 1.1.2 function-bind: 1.1.2
html5-qrcode@2.3.8: {}
http-proxy-agent@7.0.2: http-proxy-agent@7.0.2:
dependencies: dependencies:
agent-base: 7.1.1 agent-base: 7.1.1
@@ -3337,14 +3322,14 @@ snapshots:
dependencies: dependencies:
rc: 1.2.8 rc: 1.2.8
release-it@17.10.0(typescript@5.8.3): release-it@17.10.0:
dependencies: dependencies:
'@iarna/toml': 2.2.5 '@iarna/toml': 2.2.5
'@octokit/rest': 20.1.1 '@octokit/rest': 20.1.1
async-retry: 1.3.3 async-retry: 1.3.3
chalk: 5.3.0 chalk: 5.3.0
ci-info: 4.1.0 ci-info: 4.1.0
cosmiconfig: 9.0.0(typescript@5.8.3) cosmiconfig: 9.0.0
execa: 8.0.0 execa: 8.0.0
git-url-parse: 14.0.0 git-url-parse: 14.0.0
globby: 14.0.2 globby: 14.0.2
@@ -3622,9 +3607,6 @@ snapshots:
type@2.7.2: {} type@2.7.2: {}
typescript@5.8.3:
optional: true
uglify-js@3.19.3: uglify-js@3.19.3:
optional: true optional: true

Binary file not shown.

View File

@@ -41,7 +41,6 @@
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/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";
@@ -136,17 +135,12 @@
</Route> </Route>
<Route path="/runners/*"> <Route path="/runners/*">
<Route path="/"> <Route path="/">
<Runners created_via="all" /> <Runners />
</Route> </Route>
<Route path="/:runnerid" let:params> <Route path="/:runnerid" let:params>
<RunnerDetail {params} /> <RunnerDetail {params} />
</Route> </Route>
</Route> </Route>
<Route path="/cardassignment/*">
<Route path="/">
<CardAssignment />
</Route>
</Route>
<Route path="/teams/*"> <Route path="/teams/*">
<Route path="/"> <Route path="/">
<Teams /> <Teams />

View File

@@ -41,27 +41,7 @@
</svg> </svg>
<span>{$_("dashboard-title")}</span> <span>{$_("dashboard-title")}</span>
</a> </a>
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET") && store.state.jwtinfo.userdetails.permissions.includes("CARD:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")}
<a
class:activenav={$router.path.includes("/cardassignment/")}
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="/cardassignment/"
>
<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>Card Assignment</span>
</a>
<a <a
class:activenav={$router.path.includes("/runners/")} class:activenav={$router.path.includes("/runners/")}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"

View File

@@ -234,23 +234,6 @@
/></svg /></svg
> >
</StatCard> </StatCard>
<StatCard
title={$_('runners_via_kiosk')}
value={stats.runnersViaKiosk}
href="/runners/"
>
<svg
height="24"
width="24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path d="M0 0h24v24H0z" fill="none" />
<path
d="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1l-5.2 2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z"
/></svg
>
</StatCard>
</div> </div>
{:catch error} {:catch error}
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> <div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">

View File

@@ -1,123 +0,0 @@
<script>
import { RunnerCardService, RunnerService } from "@odit/lfk-client-js";
import QrCodeScanner from "./QrCodeScanner.svelte";
let state = "scan_runner";
let runnerinfo = { id: 0, firstname: "", lastname: "" };
let cardCode = "";
$: scannerActive = state.includes("scan");
</script>
<div class="p-4">
<h3 class="text-3xl font-bold">Card Assignment for Mobile</h3>
<!-- <p>state</p>
<p>{state}</p>
<p>scannerActive</p>
<p>{scannerActive}</p> -->
{#if state.includes("scan_")}
<!-- -->
{#if state === "scan_runner"}
<h3 class="text-xl font-bold">Scan Runner (Selfservice QR)</h3>
{/if}
{#if state === "scan_card"}
<h3 class="text-xl font-bold">Runner Scanned</h3>
<p>{runnerinfo.firstname} {runnerinfo.lastname} [#{runnerinfo.id}]</p>
<h3 class="text-xl font-bold">Scan Card (Code 128 Barcode)</h3>
{/if}
<QrCodeScanner
paused={!scannerActive}
on:detect={(e) => {
console.log({ type: "DETECT", code: e.detail.decodedText });
if (state === "scan_runner") {
if (
e.detail.decodedText.includes(
"https://portal.lauf-fuer-kaya.de/profile/"
)
) {
const runnerID = JSON.parse(
atob(
e.detail.decodedText
.replace("https://portal.lauf-fuer-kaya.de/profile/", "")
.split(".")[1]
)
).id;
new Audio("/beep.mp3").play();
RunnerService.runnerControllerGetOne(runnerID).then((runner) => {
console.log(runner);
runnerinfo = runner;
});
state = "scan_card";
}
}
if (state === "scan_card") {
if (
!e.detail.decodedText.includes(
"https://portal.lauf-fuer-kaya.de/profile/"
)
) {
cardCode = e.detail.decodedText;
new Audio("/beep.mp3").play();
state = "assigning";
RunnerCardService.runnerCardControllerGetAll().then((cards) => {
console.log(cards);
const card = cards.find((c) => c.code === cardCode);
if (card) {
console.log("card found", card);
RunnerCardService.runnerCardControllerPut(card.id, {
enabled: true,
id: card.id,
runner: runnerinfo.id,
}).then(() => {
state = "done";
});
} else {
state = "error_card_404";
}
});
}
}
}}
width={320}
height={320}
class="w-full max-w-sm bg-neutral-300 rounded-lg overflow-hidden"
/>
{#if state === "scan_card"}
<button
on:click={() => {
state = "scan_runner";
runnerinfo = { id: 0, firstname: "", lastname: "" };
cardCode = "";
}}
type="button"
class="py-3 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-red-100 text-red-800 hover:bg-red-200 focus:outline-hidden focus:bg-red-200 disabled:opacity-50 disabled:pointer-events-none dark:text-red-500 dark:bg-red-800/30 dark:hover:bg-red-800/20 dark:focus:bg-red-800/20 w-full mt-2"
>
Cancel
</button>
{/if}
<!-- -->
{:else}
<!-- -->
{#if state === "assigning"}
<p>Assigning Card {cardCode}</p>
<p>Please wait a moment while we assign the card...</p>
{/if}
{#if state === "done"}
<p>
Assigned Card {cardCode} to {runnerinfo.firstname}
{runnerinfo.lastname} [#{runnerinfo.id}] ✅
</p>
<button
on:click={() => {
// state = "scan_runner";
// runnerinfo = { id: 0, firstname: "", lastname: "" };
// cardCode = "";
location.reload();
}}
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 w-full mt-2"
>
Done
</button>
{/if}
<!-- -->
{/if}
</div>

View File

@@ -1,84 +0,0 @@
<script>
import { onMount, createEventDispatcher } from "svelte";
import {
Html5QrcodeScanner,
Html5QrcodeScanType,
Html5QrcodeSupportedFormats,
Html5QrcodeScannerState,
} from "html5-qrcode";
export let width;
export let height;
export let paused = false;
const dispatch = createEventDispatcher();
function onScanSuccess(decodedText, decodedResult) {
if (!paused) {
dispatch("detect", { decodedText });
}
}
// usually better to ignore and keep scanning
function onScanFailure(message) {
if (!paused) {
dispatch("error", { message });
}
}
let scanner;
onMount(() => {
scanner = new Html5QrcodeScanner(
"qr-scanner",
{
fps: 10,
rememberLastUsedCamera: true,
qrbox: { width, height },
aspectRatio: 1,
supportedScanTypes: [Html5QrcodeScanType.SCAN_TYPE_CAMERA],
formatsToSupport: [
Html5QrcodeSupportedFormats.CODE_39,
Html5QrcodeSupportedFormats.EAN_8,
Html5QrcodeSupportedFormats.EAN_13,
Html5QrcodeSupportedFormats.QR_CODE,
Html5QrcodeSupportedFormats.CODE_128,
],
},
false // non-verbose
);
scanner.render(onScanSuccess, onScanFailure);
});
// pause/resume scanner to avoid unintended scans
$: togglePause(paused);
function togglePause(paused) {
if (paused && scanner?.getState() === Html5QrcodeScannerState.SCANNING) {
scanner?.pause();
} else if (scanner?.getState() === Html5QrcodeScannerState.PAUSED) {
scanner?.resume();
}
}
</script>
<div id="qr-scanner" class={$$props.class} />
<style>
/* Hide unwanted icons */
#qr-scanner :global(img[alt="Info icon"]),
#qr-scanner :global(img[alt="Camera based scan"]) {
display: none;
}
/* Change camera permission button text */
#qr-scanner :global(#html5-qrcode-button-camera-permission) {
visibility: hidden;
}
#qr-scanner :global(#html5-qrcode-button-camera-permission::after) {
position: absolute;
inset: auto 0 0;
display: block;
content: "Allow camera access";
visibility: visible;
padding: 10px 0;
}
</style>

View File

@@ -100,7 +100,6 @@ class DocumentServer {
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,
group: { group: {
id: runners[i].group.id, id: runners[i].group.id,
name: runners[i].group.name, name: runners[i].group.name,

View File

@@ -1,180 +1,175 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { import {
DonationService, DonationService,
RunnerTeamService, RunnerTeamService,
RunnerOrganizationService, RunnerOrganizationService,
RunnerService } from "@odit/lfk-client-js";
} from "@odit/lfk-client-js"; import { init } from "@paralleldrive/cuid2";
import { init } from "@paralleldrive/cuid2"; import toast from "svelte-french-toast";
import toast from "svelte-french-toast"; import DocumentServer from "./DocumentServer";
import DocumentServer from "./DocumentServer"; const createId = init({ length: 10, fingerprint: "lfk-frontend" });
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); const documentServer = new DocumentServer(
const documentServer = new DocumentServer( config.baseurl_documentserver,
config.baseurl_documentserver, config.documentserver_key
config.documentserver_key );
);
export let certificates_show = false; export let certificates_show = false;
export let generate_runners = []; export let generate_runners = [];
export let generate_orgs = []; export let generate_orgs = [];
export let generate_teams = []; export let generate_teams = [];
function generateCertificates(locale) { function generateCertificates(locale) {
if (generate_orgs.length > 0) { if (generate_orgs.length > 0) {
generateOrgCertificates(locale); generateOrgCertificates(locale);
} else if (generate_teams.length > 0) { } else if (generate_teams.length > 0) {
generateTeamCertificates(locale); generateTeamCertificates(locale);
} else { } else {
generateRunnerCertificates(locale); generateRunnerCertificates(locale);
} }
} }
function download(blob, fileName) { function download(blob, fileName) {
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
let a = document.createElement("a"); let a = document.createElement("a");
a.href = url; a.href = url;
a.download = fileName; a.download = fileName;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
a.remove(); a.remove();
toast.dismiss(); toast.dismiss();
toast.success($_("pdf-successfully-generated")); toast.success($_("pdf-successfully-generated"));
} }
async function generateRunnerCertificates(locale) { async function generateRunnerCertificates(locale) {
toast.loading($_("generating-pdf")); toast.loading($_("generating-pdf"));
const current_donations = const current_donations =
(await DonationService.donationControllerGetAll()) || []; (await DonationService.donationControllerGetAll()) || [];
let certificateRunners = []; let certificateRunners = [];
for (let runner of generate_runners) { for (let runner of generate_runners) {
const linkRunner = await RunnerService.runnerControllerGetOne(runner.id) runner.distanceDonations =
linkRunner.distanceDonations = current_donations.filter((d) => d.runner?.id == runner.id) || [];
current_donations.filter((d) => d.runner?.id == runner.id) || []; certificateRunners.push(runner);
certificateRunners.push(linkRunner); }
} documentServer
documentServer .generateCertificates(certificateRunners, locale)
.generateCertificates(certificateRunners, locale) .then((blob) => {
.then((blob) => { let fileName = `${$_("certificates")}-${locale}.pdf`;
let fileName = `${$_("certificates")}-${locale}.pdf`; if (generate_runners.length == 1) {
if (generate_runners.length == 1) { fileName = `${$_("certificates")}_${
fileName = `${$_("certificates")}_${ generate_runners[0].firstname
generate_runners[0].firstname }_${generate_runners[0].lastname}-${locale}-${createId()}.pdf`;
}_${generate_runners[0].lastname}-${locale}-${createId()}.pdf`; }
} download(blob, fileName);
download(blob, fileName); })
}) .catch((err) => {});
.catch((err) => {}); }
}
async function generateTeamCertificates(locale) { async function generateTeamCertificates(locale) {
toast.loading($_("generating-pdfs")); toast.loading($_("generating-pdfs"));
let count = 0; let count = 0;
const current_donations = const current_donations =
(await DonationService.donationControllerGetAll()) || []; (await DonationService.donationControllerGetAll()) || [];
for (const t of generate_teams) { for (const t of generate_teams) {
const runners = await RunnerTeamService.runnerTeamControllerGetRunners( const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id, t.id
true );
); let certificateRunners = [];
let certificateRunners = []; 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) || []; certificateRunners.push(runner);
certificateRunners.push(runner); }
} documentServer
documentServer .generateCertificates(certificateRunners, locale)
.generateCertificates(certificateRunners, locale) .then((blob) => {
.then((blob) => { count++;
count++; download(
download( blob,
blob, `${$_("certificates")}_${t.name}-${locale}-${createId()}.pdf`
`${$_("certificates")}_${t.name}-${locale}-${createId()}.pdf` );
); })
}) .catch((err) => {});
.catch((err) => {}); }
} }
}
async function generateOrgCertificates(locale) { async function generateOrgCertificates(locale) {
toast.loading($_("generating-pdfs")); toast.loading($_("generating-pdfs"));
const current_donations = const current_donations =
(await DonationService.donationControllerGetAll()) || []; (await DonationService.donationControllerGetAll()) || [];
let count = 0; let count = 0;
let count_orgs = 0; let count_orgs = 0;
for (const o of generate_orgs) { for (const o of generate_orgs) {
count_orgs++; count_orgs++;
let count = 0; let count = 0;
let runners = let runners =
await RunnerOrganizationService.runnerOrganizationControllerGetRunners( await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
o.id, o.id,
true, true
true );
); let certificateRunners = [];
let certificateRunners = []; 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) || []; certificateRunners.push(runner);
certificateRunners.push(runner); }
} await documentServer
await documentServer .generateCertificates(certificateRunners, locale)
.generateCertificates(certificateRunners, locale) .then((blob) => {
.then((blob) => { download(
download( blob,
blob, `${$_("certificates")}_${o.name}-${locale}-${createId()}.pdf`
`${$_("certificates")}_${o.name}-${locale}-${createId()}.pdf` );
); })
}) .catch((err) => {});
.catch((err) => {}); for (const t of o.teams) {
for (const t of o.teams) { count++;
count++; let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
let runners = await RunnerTeamService.runnerTeamControllerGetRunners( t.id
t.id, );
true let certificateRunners = [];
); for (let runner of runners) {
let certificateRunners = []; runner.distanceDonations =
for (let runner of runners) { current_donations.filter((d) => d.runner?.id == runner.id) || [];
runner.distanceDonations = certificateRunners.push(runner);
current_donations.filter((d) => d.runner?.id == runner.id) || []; }
certificateRunners.push(runner); await documentServer
} .generateCertificates(certificateRunners, locale)
await documentServer .then((blob) => {
.generateCertificates(certificateRunners, locale) download(
.then((blob) => { blob,
download( `${$_("certificates")}_${o.name}_${
blob, t.name
`${$_("certificates")}_${o.name}_${ }-${locale}-${createId()}.pdf`
t.name );
}-${locale}-${createId()}.pdf` if (
); count === o.teams.length &&
if ( count_orgs === generate_orgs.length
count === o.teams.length && ) {
count_orgs === generate_orgs.length toast.dismiss();
) { toast.success($_("pdfs-successfully-generated"));
toast.dismiss(); }
toast.success($_("pdfs-successfully-generated")); })
} .catch((err) => {});
}) }
.catch((err) => {}); }
} }
}
}
</script> </script>
{#if certificates_show} {#if certificates_show}
<button <button
on:click={() => { on:click={() => {
generateCertificates("de"); 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("en"); 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")}: EN {$_("generate-runner-certificates")}: EN
</button> </button>
{/if} {/if}

View File

@@ -100,7 +100,7 @@
<nav class="w-full flex"> <nav class="w-full flex">
<ol class="list-none flex flex-row items-center justify-start"> <ol class="list-none flex flex-row items-center justify-start">
<li class="flex items-center"> <li class="flex items-center">
<a class="mr-2" href="/runners/" <a class="mr-2" href="./"
><svg ><svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"

View File

@@ -32,7 +32,6 @@
$: active_delete = undefined; $: active_delete = undefined;
let dataLoaded = false; let dataLoaded = false;
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;
@@ -76,11 +75,6 @@
header: () => $_("last-name"), header: () => $_("last-name"),
filterFn: `includesString`, filterFn: `includesString`,
}, },
{
accessorKey: "created_via",
header: () => "created_via",
filterFn: `includesString`,
},
{ {
accessorKey: "group", accessorKey: "group",
header: () => $_("group"), header: () => $_("group"),
@@ -109,7 +103,7 @@
header: () => $_("action"), header: () => $_("action"),
cell: (info) => { cell: (info) => {
return renderComponent(TableActions, { return renderComponent(TableActions, {
detailsLink: `/runners/${info.row.original.id}`, detailsLink: `./${info.row.original.id}`,
deleteAction: () => { deleteAction: () => {
active_delete = active_delete =
current_runners[ current_runners[
@@ -167,11 +161,7 @@
let page = 0; let page = 0;
while (page >= 0) { while (page >= 0) {
const runners = await RunnerService.runnerControllerGetAll( const runners = await RunnerService.runnerControllerGetAll(page, 500);
page,
500,
created_via
);
if (runners.length == 0) { if (runners.length == 0) {
page = -2; page = -2;
} }
@@ -200,9 +190,6 @@
<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 created_via !== "all"}
<p>created_via={created_via}</p>
{/if}
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:CREATE")} {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:CREATE")}
<button <button
on:click={() => { on:click={() => {

View File

@@ -170,7 +170,7 @@
<img <img
class:w-[50%]={is_qrcode} class:w-[50%]={is_qrcode}
class:w-full={!is_qrcode} class:w-full={!is_qrcode}
class="w-full lg:max-w-[50vw] lg:max-h-[10rem] object-contain mb-2 mx-auto" class="md:w-auto mb-2 mx-auto"
alt="Registrierungscode" alt="Registrierungscode"
src={textToBase64Barcode(window.config.baseurl, is_qrcode)} src={textToBase64Barcode(window.config.baseurl, is_qrcode)}
/> />
@@ -178,7 +178,7 @@
<img <img
class:w-[50%]={is_qrcode} class:w-[50%]={is_qrcode}
class:w-full={!is_qrcode} class:w-full={!is_qrcode}
class="w-full lg:max-w-[50vw] lg:max-h-[10rem] object-contain mb-2 mx-auto" class="md:w-auto mb-2 mx-auto"
alt="Registrierungscode" alt="Registrierungscode"
src={barcode} src={barcode}
/> />

View File

@@ -382,7 +382,6 @@
"runners": "Läufer", "runners": "Läufer",
"runners-are-being-imported": "Läufer werden importiert ...", "runners-are-being-imported": "Läufer werden importiert ...",
"runners-are-being-loaded": "Läufer werden geladen ...", "runners-are-being-loaded": "Läufer werden geladen ...",
"runners_via_kiosk": "Läufer via Kiosk",
"save": "Speichern", "save": "Speichern",
"save-changes": "Änderungen speichern", "save-changes": "Änderungen speichern",
"scan-added": "Scan hinzugefügt", "scan-added": "Scan hinzugefügt",

View File

@@ -382,7 +382,6 @@
"runners": "Runners", "runners": "Runners",
"runners-are-being-imported": "Runners are being imported...", "runners-are-being-imported": "Runners are being imported...",
"runners-are-being-loaded": "runners are being loaded...", "runners-are-being-loaded": "runners are being loaded...",
"runners_via_kiosk": "Runners via Kiosk",
"save": "Save", "save": "Save",
"save-changes": "Save Changes", "save-changes": "Save Changes",
"scan-added": "Scan added", "scan-added": "Scan added",

View File

@@ -491,10 +491,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@odit/lfk-client-js@npm:1.2.4": "@odit/lfk-client-js@npm:1.1.3":
version: 1.2.4 version: 1.1.3
resolution: "@odit/lfk-client-js@npm:1.2.4" resolution: "@odit/lfk-client-js@npm:1.1.3"
checksum: 10c0/503b3eec7fe66f8d42b137660fd5a7cda2f067c6c79fbe3c15613fb7a06284adb3e186a645f8e48c6de015fb2c8c0a42074551201fd0244215e8ba444dfe17e2 checksum: 10c0/c5108400dc40b6eb5d4c467238c178779454bf46301559034fbfd2d7666e863af5d5df8208b501ec6dad1b97994e2005d879f2d5b5f3e0439f01f34ecd97f895
languageName: node languageName: node
linkType: hard linkType: hard
@@ -503,7 +503,7 @@ __metadata:
resolution: "@odit/lfk-frontend@workspace:." resolution: "@odit/lfk-frontend@workspace:."
dependencies: dependencies:
"@fontsource/athiti": "npm:^5.2.5" "@fontsource/athiti": "npm:^5.2.5"
"@odit/lfk-client-js": "npm:1.2.4" "@odit/lfk-client-js": "npm:1.1.3"
"@odit/license-exporter": "npm:0.2.0" "@odit/license-exporter": "npm:0.2.0"
"@paralleldrive/cuid2": "npm:2.2.2" "@paralleldrive/cuid2": "npm:2.2.2"
"@sveltejs/vite-plugin-svelte": "npm:2.1.1" "@sveltejs/vite-plugin-svelte": "npm:2.1.1"
@@ -513,7 +513,6 @@ __metadata:
bwip-js: "npm:3.4.0" bwip-js: "npm:3.4.0"
check-password-strength: "npm:2.0.10" check-password-strength: "npm:2.0.10"
csvtojson: "npm:2.0.10" csvtojson: "npm:2.0.10"
html5-qrcode: "npm:^2.3.8"
localforage: "npm:1.10.0" localforage: "npm:1.10.0"
marked: "npm:4.3.0" marked: "npm:4.3.0"
postcss: "npm:8.5.3" postcss: "npm:8.5.3"
@@ -2033,13 +2032,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"html5-qrcode@npm:^2.3.8":
version: 2.3.8
resolution: "html5-qrcode@npm:2.3.8"
checksum: 10c0/3d7d0b3687e41a6fc0a06345f67e89ad3c7c00a3d0d8846d6fd31985e1ed2ac1c310e625f0b650dbc689f6b83469e3378417e7431ae5a9194178f1172bf6a93a
languageName: node
linkType: hard
"http-cache-semantics@npm:^4.1.1": "http-cache-semantics@npm:^4.1.1":
version: 4.1.1 version: 4.1.1
resolution: "http-cache-semantics@npm:4.1.1" resolution: "http-cache-semantics@npm:4.1.1"