Compare commits

..

13 Commits

Author SHA1 Message Date
68bf3717f9 chore(release): 1.10.4
All checks were successful
Build release images / build-container (push) Successful in 1m0s
2025-04-22 17:27:18 +02:00
f88c6e0dba fix(runners): Fix runner overview sort 2025-04-22 17:26:18 +02:00
12050cdda9 chore(release): 1.10.3
All checks were successful
Build release images / build-container (push) Successful in 1m24s
2025-04-17 21:15:58 +02:00
01e77a97f3 feat(pdf): Send selfservicelink for generation 2025-04-17 21:13:15 +02:00
10824b5d9b chore(deps): Bump @odit/lfk-client-js 2025-04-17 21:03:28 +02:00
d9870e03bc chore(release): 1.10.2
All checks were successful
Build release images / build-container (push) Successful in 1m0s
2025-04-11 12:24:49 +02:00
785b9e0b60 refactor(runners): filter table for created_via 2025-04-11 12:24:02 +02:00
fce2bc645e chore(release): 1.10.1
All checks were successful
Build release images / build-container (push) Successful in 1m1s
2025-04-09 11:57:16 +02:00
991716a7f5 feat: runner list filtered by created_via 2025-04-09 11:56:59 +02:00
aa720f2460 chore(release): 1.10.0
All checks were successful
Build release images / build-container (push) Successful in 1m1s
2025-04-09 10:39:06 +02:00
ac4ef8fb6d feat: working CardAssignment 2025-04-09 10:38:45 +02:00
eae0afda23 chore(release): 1.9.11
All checks were successful
Build release images / build-container (push) Successful in 57s
2025-04-08 22:17:04 +02:00
5291f8a4d1 feat(dash): add runnersViaKiosk 2025-04-08 22:16:46 +02:00
17 changed files with 366 additions and 197 deletions

View File

@@ -2,9 +2,52 @@
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.4](https://git.odit.services/lfk/frontend/compare/1.10.3...1.10.4)
- fix(runners): Fix runner overview sort [`f88c6e0`](https://git.odit.services/lfk/frontend/commit/f88c6e0dbab620887e180de0d3eab03b8215a477)
#### [1.10.3](https://git.odit.services/lfk/frontend/compare/1.10.2...1.10.3)
> 17 April 2025
- feat(pdf): Send selfservicelink for generation [`01e77a9`](https://git.odit.services/lfk/frontend/commit/01e77a97f3f28700e0249d35afd9641b56d9c55d)
- chore(release): 1.10.3 [`12050cd`](https://git.odit.services/lfk/frontend/commit/12050cdda95b5f8eb5b414b81655d06faae3ef1e)
- 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) #### [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) - 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) #### [1.9.9](https://git.odit.services/lfk/frontend/compare/1.9.8...1.9.9)

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.9.10-RELEASE_INFO</span >RELEASE_INFO-1.10.4-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.9.10", "version": "1.10.4",
"type": "module", "type": "module",
"scripts": { "scripts": {
"i18n-order": "node order.js", "i18n-order": "node order.js",
@@ -43,7 +43,7 @@
}, },
"dependencies": { "dependencies": {
"@fontsource/athiti": "^5.2.5", "@fontsource/athiti": "^5.2.5",
"@odit/lfk-client-js": "1.2.0", "@odit/lfk-client-js": "1.2.4",
"@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",

10
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.0 specifier: 1.2.4
version: 1.2.0 version: 1.2.4
'@paralleldrive/cuid2': '@paralleldrive/cuid2':
specifier: 2.2.2 specifier: 2.2.2
version: 2.2.2 version: 2.2.2
@@ -355,8 +355,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.0': '@odit/lfk-client-js@1.2.4':
resolution: {integrity: sha512-RR/Ij3PDMF840VJtphO51k+3voJcTlvRIxzTFBkvrwriBmLJwchBQxq40K4/kyVIFH7lLwO3uJy0PaxgEoTbFQ==} resolution: {integrity: sha512-eJRsjtpMm/VsQ1v2I+inMWCZmzL+WoOvsA+hj8IGsyCVn0td+z/HAwQ0SuXXNZpLPL3qSlENHXjFNrgztExEgA==}
'@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==}
@@ -2176,7 +2176,7 @@ snapshots:
dependencies: dependencies:
'@octokit/openapi-types': 22.2.0 '@octokit/openapi-types': 22.2.0
'@odit/lfk-client-js@1.2.0': {} '@odit/lfk-client-js@1.2.4': {}
'@odit/license-exporter@0.2.0': '@odit/license-exporter@0.2.0':
dependencies: dependencies:

BIN
public/beep.mp3 Normal file

Binary file not shown.

View File

@@ -136,7 +136,7 @@
</Route> </Route>
<Route path="/runners/*"> <Route path="/runners/*">
<Route path="/"> <Route path="/">
<Runners /> <Runners created_via="all" />
</Route> </Route>
<Route path="/:runnerid" let:params> <Route path="/:runnerid" let:params>
<RunnerDetail {params} /> <RunnerDetail {params} />

View File

@@ -41,7 +41,27 @@
</svg> </svg>
<span>{$_("dashboard-title")}</span> <span>{$_("dashboard-title")}</span>
</a> </a>
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET") && store.state.jwtinfo.userdetails.permissions.includes("CARD: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,6 +234,23 @@
/></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,26 +1,30 @@
<script> <script>
import { RunnerCardService, RunnerService } from "@odit/lfk-client-js";
import QrCodeScanner from "./QrCodeScanner.svelte"; import QrCodeScanner from "./QrCodeScanner.svelte";
let state = "scan_runner"; let state = "scan_runner";
let runnerID = undefined; let runnerinfo = { id: 0, firstname: "", lastname: "" };
let cardInfo = ""; let cardCode = "";
$: scannerActive = state.includes("scan");
</script> </script>
<div class="p-4"> <div class="p-4">
<h3 class="text-3xl font-bold">Card Assignment for Mobile</h3> <h3 class="text-3xl font-bold">Card Assignment for Mobile</h3>
{#if state === "done"} <!-- <p>state</p>
<p>Assigned Card {cardInfo}</p> <p>{state}</p>
<p>(not really, needs to be implemented)</p> <p>scannerActive</p>
{:else} <p>{scannerActive}</p> -->
{#if state.includes("scan_")}
<!-- --> <!-- -->
{#if state === "scan_runner"} {#if state === "scan_runner"}
<h3 class="text-xl font-bold">Scan Runner (Selfservice QR)</h3> <h3 class="text-xl font-bold">Scan Runner (Selfservice QR)</h3>
{/if} {/if}
{#if state === "scan_card"} {#if state === "scan_card"}
<h3 class="text-xl font-bold">Runner Scanned</h3> <h3 class="text-xl font-bold">Runner Scanned</h3>
<p>{runnerID}</p> <p>{runnerinfo.firstname} {runnerinfo.lastname} [#{runnerinfo.id}]</p>
<h3 class="text-xl font-bold">Scan Card (Code 128 Barcode)</h3> <h3 class="text-xl font-bold">Scan Card (Code 128 Barcode)</h3>
{/if} {/if}
<QrCodeScanner <QrCodeScanner
paused={!scannerActive}
on:detect={(e) => { on:detect={(e) => {
console.log({ type: "DETECT", code: e.detail.decodedText }); console.log({ type: "DETECT", code: e.detail.decodedText });
if (state === "scan_runner") { if (state === "scan_runner") {
@@ -29,13 +33,18 @@
"https://portal.lauf-fuer-kaya.de/profile/" "https://portal.lauf-fuer-kaya.de/profile/"
) )
) { ) {
runnerID = JSON.parse( const runnerID = JSON.parse(
atob( atob(
e.detail.decodedText e.detail.decodedText
.replace("https://portal.lauf-fuer-kaya.de/profile/", "") .replace("https://portal.lauf-fuer-kaya.de/profile/", "")
.split(".")[1] .split(".")[1]
) )
).id; ).id;
new Audio("/beep.mp3").play();
RunnerService.runnerControllerGetOne(runnerID).then((runner) => {
console.log(runner);
runnerinfo = runner;
});
state = "scan_card"; state = "scan_card";
} }
} }
@@ -45,8 +54,25 @@
"https://portal.lauf-fuer-kaya.de/profile/" "https://portal.lauf-fuer-kaya.de/profile/"
) )
) { ) {
cardInfo = e.detail.decodedText; cardCode = e.detail.decodedText;
state = "done"; 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";
}
});
} }
} }
}} }}
@@ -58,8 +84,8 @@
<button <button
on:click={() => { on:click={() => {
state = "scan_runner"; state = "scan_runner";
runnerID = undefined; runnerinfo = { id: 0, firstname: "", lastname: "" };
cardInfo = ""; cardCode = "";
}} }}
type="button" 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" 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"
@@ -68,5 +94,30 @@
</button> </button>
{/if} {/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} {/if}
</div> </div>

View File

@@ -14,12 +14,16 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
function onScanSuccess(decodedText, decodedResult) { function onScanSuccess(decodedText, decodedResult) {
dispatch("detect", { decodedText }); if (!paused) {
dispatch("detect", { decodedText });
}
} }
// usually better to ignore and keep scanning // usually better to ignore and keep scanning
function onScanFailure(message) { function onScanFailure(message) {
dispatch("error", { message }); if (!paused) {
dispatch("error", { message });
}
} }
let scanner; let scanner;

View File

@@ -100,6 +100,7 @@ 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,175 +1,180 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { import {
DonationService, DonationService,
RunnerTeamService, RunnerTeamService,
RunnerOrganizationService, RunnerOrganizationService,
} from "@odit/lfk-client-js"; RunnerService
import { init } from "@paralleldrive/cuid2"; } from "@odit/lfk-client-js";
import toast from "svelte-french-toast"; import { init } from "@paralleldrive/cuid2";
import DocumentServer from "./DocumentServer"; import toast from "svelte-french-toast";
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); import DocumentServer from "./DocumentServer";
const documentServer = new DocumentServer( const createId = init({ length: 10, fingerprint: "lfk-frontend" });
config.baseurl_documentserver, const documentServer = new DocumentServer(
config.documentserver_key config.baseurl_documentserver,
); 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) {
runner.distanceDonations = const linkRunner = await RunnerService.runnerControllerGetOne(runner.id)
current_donations.filter((d) => d.runner?.id == runner.id) || []; linkRunner.distanceDonations =
certificateRunners.push(runner); current_donations.filter((d) => d.runner?.id == runner.id) || [];
} certificateRunners.push(linkRunner);
documentServer }
.generateCertificates(certificateRunners, locale) documentServer
.then((blob) => { .generateCertificates(certificateRunners, locale)
let fileName = `${$_("certificates")}-${locale}.pdf`; .then((blob) => {
if (generate_runners.length == 1) { let fileName = `${$_("certificates")}-${locale}.pdf`;
fileName = `${$_("certificates")}_${ if (generate_runners.length == 1) {
generate_runners[0].firstname fileName = `${$_("certificates")}_${
}_${generate_runners[0].lastname}-${locale}-${createId()}.pdf`; generate_runners[0].firstname
} }_${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 = []; );
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);
documentServer }
.generateCertificates(certificateRunners, locale) documentServer
.then((blob) => { .generateCertificates(certificateRunners, locale)
count++; .then((blob) => {
download( count++;
blob, download(
`${$_("certificates")}_${t.name}-${locale}-${createId()}.pdf` blob,
); `${$_("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 = []; );
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}-${locale}-${createId()}.pdf` blob,
); `${$_("certificates")}_${o.name}-${locale}-${createId()}.pdf`
}) );
.catch((err) => {}); })
for (const t of o.teams) { .catch((err) => {});
count++; for (const t of o.teams) {
let runners = await RunnerTeamService.runnerTeamControllerGetRunners( count++;
t.id let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
); t.id,
let certificateRunners = []; true
for (let runner of runners) { );
runner.distanceDonations = let certificateRunners = [];
current_donations.filter((d) => d.runner?.id == runner.id) || []; for (let runner of runners) {
certificateRunners.push(runner); runner.distanceDonations =
} current_donations.filter((d) => d.runner?.id == runner.id) || [];
await documentServer certificateRunners.push(runner);
.generateCertificates(certificateRunners, locale) }
.then((blob) => { await documentServer
download( .generateCertificates(certificateRunners, locale)
blob, .then((blob) => {
`${$_("certificates")}_${o.name}_${ download(
t.name blob,
}-${locale}-${createId()}.pdf` `${$_("certificates")}_${o.name}_${
); t.name
if ( }-${locale}-${createId()}.pdf`
count === o.teams.length && );
count_orgs === generate_orgs.length if (
) { count === o.teams.length &&
toast.dismiss(); count_orgs === generate_orgs.length
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="./" <a class="mr-2" href="/runners/"
><svg ><svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"

View File

@@ -32,6 +32,7 @@
$: 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;
@@ -75,6 +76,11 @@
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"),
@@ -86,6 +92,11 @@
return `${group.parentGroup.name} > ${group.name}`; return `${group.parentGroup.name} > ${group.name}`;
}, },
filterFn: `group`, filterFn: `group`,
sortingFn: (rowA, rowB, col)=> {
return rowA.original.group.name.localeCompare(
rowB.original.group.name,
)
},
}, },
{ {
accessorKey: "distance", accessorKey: "distance",
@@ -103,7 +114,7 @@
header: () => $_("action"), header: () => $_("action"),
cell: (info) => { cell: (info) => {
return renderComponent(TableActions, { return renderComponent(TableActions, {
detailsLink: `./${info.row.original.id}`, detailsLink: `/runners/${info.row.original.id}`,
deleteAction: () => { deleteAction: () => {
active_delete = active_delete =
current_runners[ current_runners[
@@ -161,7 +172,11 @@
let page = 0; let page = 0;
while (page >= 0) { while (page >= 0) {
const runners = await RunnerService.runnerControllerGetAll(page, 500); const runners = await RunnerService.runnerControllerGetAll(
page,
500,
created_via
);
if (runners.length == 0) { if (runners.length == 0) {
page = -2; page = -2;
} }
@@ -190,6 +205,9 @@
<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

@@ -382,6 +382,7 @@
"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,6 +382,7 @@
"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.1.3": "@odit/lfk-client-js@npm:1.2.4":
version: 1.1.3 version: 1.2.4
resolution: "@odit/lfk-client-js@npm:1.1.3" resolution: "@odit/lfk-client-js@npm:1.2.4"
checksum: 10c0/c5108400dc40b6eb5d4c467238c178779454bf46301559034fbfd2d7666e863af5d5df8208b501ec6dad1b97994e2005d879f2d5b5f3e0439f01f34ecd97f895 checksum: 10c0/503b3eec7fe66f8d42b137660fd5a7cda2f067c6c79fbe3c15613fb7a06284adb3e186a645f8e48c6de015fb2c8c0a42074551201fd0244215e8ba444dfe17e2
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.1.3" "@odit/lfk-client-js": "npm:1.2.4"
"@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,6 +513,7 @@ __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"
@@ -2032,6 +2033,13 @@ __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"