chore: init
This commit is contained in:
135
web/src/routes/(manage)/room/[slug]/+page.svelte
Normal file
135
web/src/routes/(manage)/room/[slug]/+page.svelte
Normal file
@@ -0,0 +1,135 @@
|
||||
<script lang="ts">
|
||||
import { page } from "$app/state";
|
||||
import Button from "$lib/components/ui/button/button.svelte";
|
||||
import * as InputOTP from "$lib/components/ui/input-otp";
|
||||
import { eventTarget } from "$lib/ws.svelte";
|
||||
import { toast } from "svelte-sonner";
|
||||
|
||||
const slug = $derived(page.params.slug);
|
||||
|
||||
let nextTicket = $state("");
|
||||
|
||||
let currentTicket = $state("");
|
||||
|
||||
let isInvalid = $state(false);
|
||||
|
||||
const TICKET_INPUT_REGEX = "^[A-B]{0,1}[0-9]{0,2}$";
|
||||
const TICKET_REGEX = /^[A-B]\d{2}$/;
|
||||
|
||||
async function submit() {
|
||||
if(!TICKET_REGEX.test(nextTicket)) {
|
||||
isInvalid = true;
|
||||
return;
|
||||
}
|
||||
if(currentTicket !== "") {
|
||||
eventTarget.dispatchEvent(new CustomEvent("send", { detail: { type: "complete-ticket", ticket: currentTicket } }));
|
||||
await new Promise(r => {
|
||||
eventTarget.addEventListener("complete-ticket", (e) => {
|
||||
const detail = (e as CustomEvent).detail;
|
||||
if(detail.status === "completed" || detail.status === "no-show") {
|
||||
r(true);
|
||||
}
|
||||
}, { once: true });
|
||||
})
|
||||
}
|
||||
eventTarget.dispatchEvent(new CustomEvent("send", { detail: { type: "call-ticket", ticket: nextTicket, room: slug } }));
|
||||
}
|
||||
|
||||
async function stopCurrent() {
|
||||
if(!currentTicket) return;
|
||||
eventTarget.dispatchEvent(new CustomEvent("send", { detail: { type: "complete-ticket", ticket: currentTicket } }));
|
||||
}
|
||||
|
||||
async function noShow() {
|
||||
if(!currentTicket) return;
|
||||
eventTarget.dispatchEvent(new CustomEvent("send", { detail: { type: "no-show", ticket: currentTicket } }));
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
eventTarget.addEventListener("error", (e) => {
|
||||
const detail = (e as CustomEvent).detail;
|
||||
if(detail.code === -3) {
|
||||
toast.error(`Ticket ${detail.num} existiert nicht oder ist bereits abgeschlossen / nicht erschienen`);
|
||||
isInvalid = true;
|
||||
}
|
||||
});
|
||||
eventTarget.addEventListener("current-ticket", (e) => {
|
||||
const detail = (e as CustomEvent).detail;
|
||||
if(detail.room === slug) {
|
||||
currentTicket = detail.num ?? "";
|
||||
}
|
||||
})
|
||||
eventTarget.addEventListener("call-ticket", (e) => {
|
||||
const detail = (e as CustomEvent).detail;
|
||||
if(detail.status === "called" && detail.room === slug) {
|
||||
currentTicket = detail.num;
|
||||
nextTicket = "";
|
||||
}
|
||||
})
|
||||
eventTarget.addEventListener("complete-ticket", (e) => {
|
||||
const detail = (e as CustomEvent).detail;
|
||||
if(detail.status === "completed" || detail.status === "no-show") {
|
||||
currentTicket = "";
|
||||
}
|
||||
if(detail.status === "completed") {
|
||||
toast.success(`Ticket ${detail.num} abgeschlossen`);
|
||||
} else if(detail.status === "no-show") {
|
||||
toast.success(`Ticket ${detail.num} nicht erschienen`);
|
||||
}
|
||||
})
|
||||
|
||||
eventTarget.dispatchEvent(new CustomEvent("send", { detail: { type: "get-current-ticket", room: slug } }));
|
||||
})
|
||||
</script>
|
||||
|
||||
<h1 class="text-2xl font-bold">Raum {slug}</h1>
|
||||
<div style="display: flex; align-items: center; gap: 1rem;">
|
||||
<InputOTP.Root maxlength={3} pattern={TICKET_INPUT_REGEX} bind:value={nextTicket} onkeypress={(e) => {
|
||||
isInvalid = false;
|
||||
if (e.key === "Enter") {
|
||||
submit();
|
||||
}
|
||||
}}>
|
||||
{#snippet children({ cells })}
|
||||
<InputOTP.Group>
|
||||
{#each cells.slice(0, 3) as cell}
|
||||
<InputOTP.Slot {cell} aria-invalid={isInvalid} />
|
||||
{/each}
|
||||
</InputOTP.Group>
|
||||
{/snippet}
|
||||
</InputOTP.Root>
|
||||
<Button onclick={() => {
|
||||
submit();
|
||||
}}>Aufrufen</Button>
|
||||
<Button variant="destructive" onclick={() => {
|
||||
noShow();
|
||||
}}>Nicht erschienen</Button>
|
||||
<Button variant="secondary" onclick={() => {
|
||||
stopCurrent();
|
||||
}}>Erledigen</Button>
|
||||
</div>
|
||||
|
||||
{#if currentTicket !== ""}
|
||||
<div class="flex flex-col items-center mt-4 gap-2">
|
||||
<span>Jetzt Aufgerufen:</span>
|
||||
<span id="new-ticket">{currentTicket}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
#new-ticket {
|
||||
font-size: 2rem;
|
||||
font-family: monospace;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 0.5rem;
|
||||
background: var(--background);
|
||||
box-shadow: var(--shadow);
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#ticket {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user