This commit is contained in:
Philipp Dormann 2025-04-22 18:40:26 +02:00
parent 87df34bb56
commit 2a4fe2b28f
Signed by: philipp
GPG Key ID: 3BB9ADD52DCA4314
2 changed files with 161 additions and 108 deletions

View File

@ -4,30 +4,69 @@
let state = "scan_runner"; let state = "scan_runner";
let runnerinfo = { id: 0, firstname: "", lastname: "" }; let runnerinfo = { id: 0, firstname: "", lastname: "" };
let cardCode = ""; let cardCode = "";
$: scannerActive = state.includes("scan"); let scannerActive = true;
function resetAll() {
state = "scan_runner";
runnerinfo = { id: 0, firstname: "", lastname: "" };
cardCode = "";
scannerActive = true;
}
</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>
<!-- <p>state</p> <!-- <p class="p-4 bg-sky-200 rounded-lg mt-2 mb-2">state: {state}</p> -->
<p>{state}</p> {#if state === "done"}
<p>scannerActive</p> <h3 class="text-2xl font-bold">✅ Done</h3>
<p>{scannerActive}</p> --> <h4 class="text-xl font-semibold">
{#if state.includes("scan_")} Assigned Card {cardCode} to {runnerinfo.firstname}
{runnerinfo.lastname} [#{runnerinfo.id}]
</h4>
<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 w-full mt-2"
>
Next Runner
</button>
{:else if state === "assigning"}
<p>Assigning Card {cardCode}</p>
<p>Please wait a moment while we assign the card...</p>
{:else}
<!-- -->
{#if runnerinfo.id === 0}
<h3 class="text-2xl font-bold">Scan Runner</h3>
<h4 class="text-xl font-semibold">
Selfservice QR/ Registration Barcode
</h4>
{:else}
<h3 class="text-2xl font-bold">
{runnerinfo.firstname}
{runnerinfo.lastname}
</h3>
<p>
ID: #{runnerinfo.id}<br />created_via: {runnerinfo.created_via}<br
/>{runnerinfo.group.name}
</p>
{/if}
<!-- --> <!-- -->
{#if state === "scan_runner"}
<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-2xl font-bold">Scan Card</h3>
<p>{runnerinfo.firstname} {runnerinfo.lastname} [#{runnerinfo.id}]</p> <h4 class="text-xl font-semibold">Code 128 Barcode</h4>
<h3 class="text-xl font-bold">Scan Card (Code 128 Barcode)</h3>
{/if} {/if}
{#if state.includes("scan_")}
{#if scannerActive}
<QrCodeScanner <QrCodeScanner
paused={!scannerActive} :paused={!scannerActive}
on:detect={(e) => { on:detect={(e) => {
if (scannerActive) {
scannerActive = false;
console.log({ type: "DETECT", code: e.detail.decodedText }); console.log({ type: "DETECT", code: e.detail.decodedText });
if (state === "scan_runner") { if (runnerinfo.id === 0) {
new Audio("/beep.mp3").play();
if ( if (
e.detail.decodedText.includes( e.detail.decodedText.includes(
"https://portal.lauf-fuer-kaya.de/profile/" "https://portal.lauf-fuer-kaya.de/profile/"
@ -40,24 +79,34 @@
.split(".")[1] .split(".")[1]
) )
).id; ).id;
new Audio("/beep.mp3").play(); RunnerService.runnerControllerGetOne(runnerID)
RunnerService.runnerControllerGetOne(runnerID).then((runner) => { .then((runner) => {
console.log(runner);
runnerinfo = runner; runnerinfo = runner;
})
.catch((e) => {
console.error(e);
state = "scan_error_runner_404";
resetAll();
});
} else {
const runnerID = parseInt(e.detail.decodedText);
RunnerService.runnerControllerGetOne(runnerID)
.then((runner) => {
runnerinfo = runner;
})
.catch((e) => {
console.error(e);
state = "scan_error_runner_404";
resetAll();
}); });
state = "scan_card";
} }
} } else {
if (state === "scan_card") { if (`${e.detail.decodedText}`.length > 10) {
if (
!e.detail.decodedText.includes(
"https://portal.lauf-fuer-kaya.de/profile/"
)
) {
cardCode = e.detail.decodedText; cardCode = e.detail.decodedText;
new Audio("/beep.mp3").play(); new Audio("/beep.mp3").play();
state = "assigning"; state = "assigning";
RunnerCardService.runnerCardControllerGetAll().then((cards) => { RunnerCardService.runnerCardControllerGetAll()
.then((cards) => {
console.log(cards); console.log(cards);
const card = cards.find((c) => c.code === cardCode); const card = cards.find((c) => c.code === cardCode);
if (card) { if (card) {
@ -66,24 +115,46 @@
enabled: true, enabled: true,
id: card.id, id: card.id,
runner: runnerinfo.id, runner: runnerinfo.id,
}).then(() => { })
.then(() => {
state = "done"; state = "done";
})
.catch(() => {
scannerActive = true;
}); });
} else { } else {
state = "error_card_404"; scannerActive = true;
} }
})
.catch(() => {
scannerActive = true;
}); });
} }
} }
}
}} }}
width={320} width={320}
height={320} height={320}
class="w-full max-w-sm bg-neutral-300 rounded-lg overflow-hidden" class="w-full max-w-sm bg-neutral-300 rounded-lg overflow-hidden"
/> />
{#if state === "scan_card"} {/if}
{#if runnerinfo.id !== 0 && state !== "scan_card"}
<button
on:click={() => {
state = "scan_card";
scannerActive = true;
}}
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"
>
Scan Card
</button>
{/if}
{#if state === "scan_card" || runnerinfo.id !== 0}
<button <button
on:click={() => { on:click={() => {
state = "scan_runner"; state = "scan_runner";
scannerActive = true;
runnerinfo = { id: 0, firstname: "", lastname: "" }; runnerinfo = { id: 0, firstname: "", lastname: "" };
cardCode = ""; cardCode = "";
}} }}
@ -94,30 +165,5 @@
</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

@ -13,9 +13,25 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const debounceFunc = (func, delay) => {
let timer;
return function (...args) {
const context = this;
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
};
const debouncedDispatch = debounceFunc(function (decodedText) {
console.log("dispatchEvent");
dispatch("detect", { decodedText });
}, 500);
function onScanSuccess(decodedText, decodedResult) { function onScanSuccess(decodedText, decodedResult) {
if (!paused) { if (!paused) {
dispatch("detect", { decodedText }); debouncedDispatch(decodedText);
} }
} }
@ -32,6 +48,7 @@
"qr-scanner", "qr-scanner",
{ {
fps: 10, fps: 10,
showTorchButtonIfSupported: true,
rememberLastUsedCamera: true, rememberLastUsedCamera: true,
qrbox: { width, height }, qrbox: { width, height },
aspectRatio: 1, aspectRatio: 1,
@ -48,16 +65,6 @@
); );
scanner.render(onScanSuccess, onScanFailure); 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> </script>
<div id="qr-scanner" class={$$props.class} /> <div id="qr-scanner" class={$$props.class} />