(add): Sheet printing form and compiler corrections

This commit is contained in:
2026-01-19 23:40:04 -05:00
parent ec0396b065
commit 287e89efa0
10 changed files with 746 additions and 325 deletions

View File

@@ -26,9 +26,9 @@
<div id="formheader" class="{prefix.color}" bind:offsetHeight={headerHeight}> <div id="formheader" class="{prefix.color}" bind:offsetHeight={headerHeight}>
<div class="flex-row-space tb-margin"> <div class="flex-row-space tb-margin">
<div class="flex-row"> <div class="flex-row">
<input type="number" bind:value={pagerForm.id_from}> <input type="number" onfocus={(e) => e.target.select()} bind:value={pagerForm.id_from}>
<div style="font-size: 22pt">-</div> <div style="font-size: 22pt">-</div>
<input type="number" bind:value={pagerForm.id_to}> <input type="number" onfocus={(e) => e.target.select()} bind:value={pagerForm.id_to}>
<button class="styled" onclick={() => { <button class="styled" onclick={() => {
if (Math.abs(pagerForm.id_to - pagerForm.id_from) > 800) { if (Math.abs(pagerForm.id_to - pagerForm.id_from) > 800) {
pagerForm.id_to = pagerForm.id_from + 799; pagerForm.id_to = pagerForm.id_from + 799;

View File

@@ -1,43 +1,60 @@
<script> <script>
import { browser } from "$app/environment"; import { browser } from "$app/environment";
import { env } from "$env/dynamic/public"; import { env } from "$env/dynamic/public";
import { setContext } from "svelte";
import hotkeys from "hotkeys-js"; import hotkeys from "hotkeys-js";
import favicon from "$lib/assets/favicon.svg" import favicon from "$lib/assets/favicon.svg";
let { data } = $props(); let { data } = $props();
const all_prefixes = [...data.prefixes]; const all_prefixes = $derived(data.prefixes);
let prefix_name = $state(""); let prefix_name = $state("");
let current_prefix = $state({ name: "", color: "", weight: 0 }); let current_prefix = $state({ name: "", color: "", weight: 0 });
let admin_mode = $state(false); let admin_mode = $state(false);
const venue = env.PUBLIC_TAM3_VENUE || "TAM3"; const venue = env.PUBLIC_TAM3_VENUE || "TAM3";
$effect(() => { $effect(() => {
const new_prefix = all_prefixes.find((prefix) => prefix.name === prefix_name); const new_prefix = all_prefixes.find(
(prefix) => prefix.name === prefix_name,
);
if (new_prefix) { if (new_prefix) {
current_prefix = { ...new_prefix }; current_prefix = { ...new_prefix };
} }
}) });
if (browser) { if (browser) {
document.title = `${venue} - Main Menu`; hotkeys.filter = function (event) {
hotkeys.filter = function(event) {return true}; return true;
hotkeys('alt+a', function(event) {event.preventDefault(); admin_mode = !admin_mode; return false;}); };
hotkeys("alt+a", function (event) {
event.preventDefault();
admin_mode = !admin_mode;
return false;
});
setTimeout(() => { setTimeout(() => {
if (all_prefixes[0]) { if (all_prefixes[0]) {
prefix_name = all_prefixes[0].name; prefix_name = all_prefixes[0].name;
} }
}, 100); }, 100);
}; }
</script> </script>
<svelte:head>
<title>{venue} - Main Menu</title>
</svelte:head>
<div class="main-menu"> <div class="main-menu">
<div class="flex-row"> <div class="flex-row">
<img src="{favicon}" alt="TAM3 Icon - Red ticket with TAM3 on it" class="icon"> <img
src={favicon}
alt="TAM3 Icon - Red ticket with TAM3 on it"
class="icon"
/>
<h1>{venue} - Main Menu</h1> <h1>{venue} - Main Menu</h1>
</div> </div>
{#if all_prefixes.length > 0} {#if all_prefixes.length > 0}
<div class="universal-reports flex-row tb-margin"> <div class="universal-reports flex-row tb-margin">
<a href="/counts" target="_blank" class="styled">Counts</a> <a href="/counts" target="_blank" class="styled">Counts</a>
<a href="/sheets" target="_blank" class="styled">Print Sheets</a>
</div> </div>
<div> <div>
<h2>Current Prefix: {current_prefix.name}</h2> <h2>Current Prefix: {current_prefix.name}</h2>
@@ -47,23 +64,50 @@
</div> </div>
<div class="prefix-selector flex-row"> <div class="prefix-selector flex-row">
{#each all_prefixes as prefix} {#each all_prefixes as prefix}
<div class="{prefix.color} p025{prefix.name === prefix_name ? " active" : ""}"> <div
<button class="styled" onclick={() => { class="{prefix.color} p025{prefix.name === prefix_name
? ' active'
: ''}"
>
<button
class="styled"
onclick={() => {
prefix_name = prefix.name; prefix_name = prefix.name;
}}>{prefix.name}</button> }}>{prefix.name}</button
>
</div> </div>
{/each} {/each}
</div> </div>
<div><h2>Forms:</h2></div> <div><h2>Forms:</h2></div>
<div class="flex-row {current_prefix.color}"> <div class="flex-row {current_prefix.color}">
<a href="/tickets/{current_prefix.name}/" target="_blank" class="styled">Tickets</a> <a
<a href="/baskets/{current_prefix.name}/" target="_blank" class="styled">Baskets</a> href="/tickets/{current_prefix.name}/"
<a href="/drawing/{current_prefix.name}/" target="_blank" class="styled">Drawing</a> target="_blank"
class="styled">Tickets</a
>
<a
href="/baskets/{current_prefix.name}/"
target="_blank"
class="styled">Baskets</a
>
<a
href="/drawing/{current_prefix.name}/"
target="_blank"
class="styled">Drawing</a
>
</div> </div>
<div><h2>Reports:</h2></div> <div><h2>Reports:</h2></div>
<div class="flex-row {current_prefix.color}"> <div class="flex-row {current_prefix.color}">
<a href="/reports/byname/{current_prefix.name}/" target="_blank" class="styled">By Name</a> <a
<a href="/reports/bybasket/{current_prefix.name}/" target="_blank" class="styled">By Basket ID</a> href="/reports/byname/{current_prefix.name}/"
target="_blank"
class="styled">By Name</a
>
<a
href="/reports/bybasket/{current_prefix.name}/"
target="_blank"
class="styled">By Basket ID</a
>
</div> </div>
{:else} {:else}
<p>There aren't any prefixes available, please create them.</p> <p>There aren't any prefixes available, please create them.</p>
@@ -73,8 +117,12 @@
<div><h2>Admin Mode:</h2></div> <div><h2>Admin Mode:</h2></div>
<div class="flex-row"> <div class="flex-row">
<a href="/prefixes" target="_blank" class="styled">Prefix Editor</a> <a href="/prefixes" target="_blank" class="styled">Prefix Editor</a>
<a href="/search/tickets" target="_blank" class="styled">Search Tickets</a> <a href="/search/tickets" target="_blank" class="styled"
<a href="/backuprestore" target="_blank" class="styled">Backup/Restore</a> >Search Tickets</a
>
<a href="/backuprestore" target="_blank" class="styled"
>Backup/Restore</a
>
<a href="/settings" target="_blank" class="styled">Settings</a> <a href="/settings" target="_blank" class="styled">Settings</a>
</div> </div>
{/if} {/if}

View File

@@ -1,17 +1,17 @@
<script> <script>
import { browser } from '$app/environment'; import { browser } from "$app/environment";
import FormHeader from '$lib/components/FormHeader.svelte'; import FormHeader from "$lib/components/FormHeader.svelte";
import hotkeys from 'hotkeys-js'; import hotkeys from "hotkeys-js";
const { data } = $props(); const { data } = $props();
const prefix = {...data.prefix}; const prefix = $derived(data.prefix);
let pagerForm = $state({ id_from: 0, id_to: 0 }); let pagerForm = $state({ id_from: 0, id_to: 0 });
let current_idx = $state(0); let current_idx = $state(0);
let next_idx = $derived(current_idx + 1); let next_idx = $derived(current_idx + 1);
let prev_idx = $derived(current_idx-1) let prev_idx = $derived(current_idx - 1);
let current_baskets = $state([]); let current_baskets = $state([]);
let copy_buffer = $state({prefix: prefix.name, b_id: 0, description: "", donors: "", winning_ticket: 0}); let copy_buffer = $state({});
let headerHeight = $state() let headerHeight = $state();
function changeFocus(idx) { function changeFocus(idx) {
const focusDe = document.getElementById(`${idx}_de`); const focusDe = document.getElementById(`${idx}_de`);
@@ -23,14 +23,19 @@
const functions = { const functions = {
refreshPage: async () => { refreshPage: async () => {
if (current_baskets.filter(basket => basket.changed === true).length > 0) { if (
functions.saveAll() current_baskets.filter((basket) => basket.changed === true)
.length > 0
) {
functions.saveAll();
} }
const res = await fetch(`/api/baskets/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`); const res = await fetch(
`/api/baskets/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`,
);
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
current_baskets = [...data]; current_baskets = [...data];
setTimeout(() => changeFocus(0), 100) setTimeout(() => changeFocus(0), 100);
} }
}, },
prevPage: () => { prevPage: () => {
@@ -47,7 +52,12 @@
}, },
duplicateDown: () => { duplicateDown: () => {
if (current_baskets[next_idx]) { if (current_baskets[next_idx]) {
current_baskets[next_idx] = {...current_baskets[current_idx], b_id: current_baskets[next_idx].b_id, winning_ticket: current_baskets[next_idx].winning_ticket, changed: true}; current_baskets[next_idx] = {
...current_baskets[current_idx],
b_id: current_baskets[next_idx].b_id,
winning_ticket: current_baskets[next_idx].winning_ticket,
changed: true,
};
changeFocus(next_idx); changeFocus(next_idx);
} else { } else {
changeFocus(next_idx); changeFocus(next_idx);
@@ -55,7 +65,12 @@
}, },
duplicateUp: () => { duplicateUp: () => {
if (prev_idx >= 0) { if (prev_idx >= 0) {
current_baskets[prev_idx] = {...current_baskets[current_idx], b_id: current_baskets[prev_idx].b_id, winning_ticket: current_baskets[prev_idx].winning_ticket, changed: true}; current_baskets[prev_idx] = {
...current_baskets[current_idx],
b_id: current_baskets[prev_idx].b_id,
winning_ticket: current_baskets[prev_idx].winning_ticket,
changed: true,
};
changeFocus(prev_idx); changeFocus(prev_idx);
} else { } else {
changeFocus(prev_idx); changeFocus(prev_idx);
@@ -79,28 +94,49 @@
copy_buffer = { ...current_baskets[current_idx] }; copy_buffer = { ...current_baskets[current_idx] };
}, },
paste: () => { paste: () => {
current_baskets[current_idx] = {...copy_buffer, b_id: current_baskets[current_idx].b_id, changed: true} if (Object.keys(copy_buffer).length !== 0) {
}, current_baskets[current_idx] = {
saveAll: async () => { ...copy_buffer,
const to_save = current_baskets.filter((basket) => basket.changed === true); b_id: current_baskets[current_idx].b_id,
const res = await fetch(`/api/baskets`, {body: JSON.stringify(to_save), method: 'POST', headers: {'Content-Type': 'application/json'}}); changed: true,
if (res.ok) {
for (let basket of current_baskets) {basket.changed = false};
changeFocus(0);
}; };
} }
changeFocus(current_idx);
},
saveAll: async () => {
const to_save = current_baskets.filter(
(basket) => basket.changed === true,
);
const res = await fetch(`/api/baskets`, {
body: JSON.stringify(to_save),
method: "POST",
headers: { "Content-Type": "application/json" },
});
if (res.ok) {
for (let basket of current_baskets) {
basket.changed = false;
} }
changeFocus(0);
}
},
};
if (browser) { if (browser) {
document.title = `${prefix.name} Basket Entry`
window.addEventListener("beforeunload", function (e) { window.addEventListener("beforeunload", function (e) {
if (current_baskets.filter(basket => basket.changed === true).length > 0) { if (
current_baskets.filter((basket) => basket.changed === true)
.length > 0
) {
e.preventDefault(); e.preventDefault();
} }
}) });
} }
</script> </script>
<svelte:head>
<title>{prefix.name} Basket Entry</title>
</svelte:head>
<h1>{prefix.name} Basket Entry</h1> <h1>{prefix.name} Basket Entry</h1>
<FormHeader {prefix} {functions} bind:pagerForm bind:headerHeight /> <FormHeader {prefix} {functions} bind:pagerForm bind:headerHeight />
<table> <table>
@@ -114,11 +150,31 @@
</thead> </thead>
<tbody> <tbody>
{#each current_baskets as basket, idx} {#each current_baskets as basket, idx}
<tr onfocusin={() => current_idx = idx}> <tr onfocusin={() => (current_idx = idx)}>
<td>{basket.b_id}</td> <td>{basket.b_id}</td>
<td><input type="text" id="{idx}_de" onchange={() => basket.changed = true} bind:value={basket.description}></td> <td
<td><input type="text" id="{idx}_do" onchange={() => basket.changed = true} bind:value={basket.donors}></td> ><input
<td><button tabindex="-1" onclick={() => basket.changed = !basket.changed}>{basket.changed ? "Y" : "N"}</button></td> type="text"
id="{idx}_de"
onchange={() => (basket.changed = true)}
bind:value={basket.description}
/></td
>
<td
><input
type="text"
id="{idx}_do"
onchange={() => (basket.changed = true)}
bind:value={basket.donors}
/></td
>
<td
><button
tabindex="-1"
onclick={() => (basket.changed = !basket.changed)}
>{basket.changed ? "Y" : "N"}</button
></td
>
</tr> </tr>
{/each} {/each}
</tbody> </tbody>
@@ -148,7 +204,8 @@
background: transparent; background: transparent;
border: solid 1px #000000; border: solid 1px #000000;
} }
input, button { input,
button {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;

View File

@@ -1,19 +1,27 @@
<script> <script>
import { browser } from '$app/environment'; import { browser } from "$app/environment";
const { data } = $props(); const { data } = $props();
const counts = data.counts; const counts = $derived(data.counts);
const prefixes = data.prefixes; function copyPrefixes() {
return [...data.prefixes];
}
const prefixes = $state(copyPrefixes());
let colormap = {}; let colormap = {};
for (let prefix of prefixes) {colormap[prefix.name] = prefix.color} for (let prefix of prefixes) {
colormap[prefix.name] = prefix.color;
}
if (browser) { if (browser) {
document.title = "Counts of tickets entered";
setTimeout(() => window.location.reload(true), 60000); setTimeout(() => window.location.reload(true), 60000);
} }
</script> </script>
<svelte:head>
<title>Counts of tickets entered</title>
</svelte:head>
<h1>Counts of tickets entered</h1> <h1>Counts of tickets entered</h1>
<table> <table>
<thead> <thead>

View File

@@ -1,17 +1,17 @@
<script> <script>
import { browser } from '$app/environment'; import { browser } from "$app/environment";
import FormHeader from '$lib/components/FormHeader.svelte'; import FormHeader from "$lib/components/FormHeader.svelte";
import { focusElement } from '$lib/focusElement.js'; import { focusElement } from "$lib/focusElement.js";
const { data } = $props(); const { data } = $props();
const prefix = {...data.prefix}; const prefix = $derived(data.prefix);
let pagerForm = $state({ id_from: 0, id_to: 0 }); let pagerForm = $state({ id_from: 0, id_to: 0 });
let current_idx = $state(0); let current_idx = $state(0);
let next_idx = $derived(current_idx + 1); let next_idx = $derived(current_idx + 1);
let prev_idx = $derived(current_idx - 1); let prev_idx = $derived(current_idx - 1);
let current_drawings = $state([]); let current_drawings = $state([]);
let copy_buffer = $state({prefix: prefix.name, b_id: 1, winning_ticket: 0, winner: ", "}); let copy_buffer = $state({});
let headerHeight = $state() let headerHeight = $state();
function changeFocus(idx) { function changeFocus(idx) {
const focusWt = document.getElementById(`${idx}_wt`); const focusWt = document.getElementById(`${idx}_wt`);
@@ -23,10 +23,15 @@
const functions = { const functions = {
refreshPage: async () => { refreshPage: async () => {
if (current_drawings.filter(drawing => drawing.changed === true).length > 0) { if (
functions.saveAll() current_drawings.filter((drawing) => drawing.changed === true)
.length > 0
) {
functions.saveAll();
} }
const res = await fetch(`/api/combined/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`); const res = await fetch(
`/api/combined/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`,
);
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
current_drawings = [...data]; current_drawings = [...data];
@@ -47,7 +52,11 @@
}, },
duplicateDown: () => { duplicateDown: () => {
if (current_drawings[next_idx]) { if (current_drawings[next_idx]) {
current_drawings[next_idx] = {...current_drawings[current_idx], b_id: current_drawings[next_idx].b_id, changed: true}; current_drawings[next_idx] = {
...current_drawings[current_idx],
b_id: current_drawings[next_idx].b_id,
changed: true,
};
changeFocus(next_idx); changeFocus(next_idx);
} else { } else {
changeFocus(current_idx); changeFocus(current_idx);
@@ -55,7 +64,11 @@
}, },
duplicateUp: () => { duplicateUp: () => {
if (prev_idx >= 0) { if (prev_idx >= 0) {
current_drawings[prev_idx] = {...current_drawing[current_idx], b_id: current_drawings[prev_idx].b_id, changed: true}; current_drawings[prev_idx] = {
...current_drawing[current_idx],
b_id: current_drawings[prev_idx].b_id,
changed: true,
};
changeFocus(prev_idx); changeFocus(prev_idx);
} else { } else {
changeFocus(current_idx); changeFocus(current_idx);
@@ -79,28 +92,49 @@
copy_buffer = { ...current_drawings[current_idx] }; copy_buffer = { ...current_drawings[current_idx] };
}, },
paste: () => { paste: () => {
current_drawings[current_idx] = {...copy_buffer, b_id: current_drawings[current_idx], changed: true}; if (Object.keys(copy_buffer).length !== 0) {
current_drawings[current_idx] = {
...copy_buffer,
b_id: current_drawings[current_idx],
changed: true,
};
}
changeFocus(current_idx);
}, },
saveAll: async () => { saveAll: async () => {
const to_save = current_drawings.filter((drawing) => drawing.changed === true); const to_save = current_drawings.filter(
const res = await fetch("/api/combined", {body: JSON.stringify(to_save), method: 'POST', headers: {'Content-Type': 'application/json'}}); (drawing) => drawing.changed === true,
);
const res = await fetch("/api/combined", {
body: JSON.stringify(to_save),
method: "POST",
headers: { "Content-Type": "application/json" },
});
if (res.ok) { if (res.ok) {
for (let drawing of current_drawings) {drawing.changed = false}; for (let drawing of current_drawings) {
drawing.changed = false;
}
changeFocus(0); changeFocus(0);
} }
} },
} };
if (browser) { if (browser) {
document.title = `${prefix.name} Drawing Form`
window.addEventListener("beforeunload", function (e) { window.addEventListener("beforeunload", function (e) {
if (current_drawings.filter(drawing => drawing.changed === true).length > 0) { if (
current_drawings.filter((drawing) => drawing.changed === true)
.length > 0
) {
e.preventDefault(); e.preventDefault();
} }
}); });
} }
</script> </script>
<svelte:head>
<title>{prefix.name} Drawing Form</title>
</svelte:head>
<h1>{prefix.name} Drawing Form</h1> <h1>{prefix.name} Drawing Form</h1>
<FormHeader {prefix} {functions} bind:pagerForm bind:headerHeight /> <FormHeader {prefix} {functions} bind:pagerForm bind:headerHeight />
<table> <table>
@@ -114,18 +148,31 @@
</thead> </thead>
<tbody> <tbody>
{#each current_drawings as drawing, idx} {#each current_drawings as drawing, idx}
<tr onfocusin={() => current_idx = idx}> <tr onfocusin={() => (current_idx = idx)}>
<td>{drawing.b_id}</td> <td>{drawing.b_id}</td>
<td><input type="number" id="{idx}_wt" bind:value={drawing.winning_ticket} onfocus={focusElement} onchange={async () => { <td
><input
type="number"
id="{idx}_wt"
bind:value={drawing.winning_ticket}
onfocus={focusElement}
onchange={async () => {
drawing.changed = true; drawing.changed = true;
const res = await fetch(`/api/tickets/${prefix.name}/${drawing.winning_ticket}`); const res = await fetch(
`/api/tickets/${prefix.name}/${drawing.winning_ticket}`,
);
if (res.ok) { if (res.ok) {
const t_data = await res.json() const t_data = await res.json();
drawing.winner = `${t_data.last_name}, ${t_data.first_name}` drawing.winner = `${t_data.last_name}, ${t_data.first_name}`;
} }
}}></td> }}
/></td
>
<td>{drawing.winner}</td> <td>{drawing.winner}</td>
<td><button tabindex="-1">{drawing.changed ? "Y" : "N"}</button></td> <td
><button tabindex="-1">{drawing.changed ? "Y" : "N"}</button
></td
>
</tr> </tr>
{/each} {/each}
</tbody> </tbody>
@@ -155,7 +202,8 @@
background: transparent; background: transparent;
border: solid 1px #000000; border: solid 1px #000000;
} }
input, button { input,
button {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;

View File

@@ -1,33 +1,53 @@
<script> <script>
import { env } from '$env/dynamic/public'; import { env } from "$env/dynamic/public";
import { browser } from '$app/environment'; import { browser } from "$app/environment";
const { data } = $props(); const { data } = $props();
const prefix = {...data.prefix}; const prefix = $derived(data.prefix);
const report_data = data.report; const report_data = $derived(data.report);
let show_data = $state([...report_data]); function copyReportData() {
let report_subject = $state("All Preferences"); return [...report_data];
if (browser) {
document.title = `${prefix.name} Report By Basket ID`
} }
let show_data = $state(copyReportData());
let report_subject = $state("All Preferences");
</script> </script>
<svelte:head>
<title>{prefix.name} Report By Basket ID</title>
</svelte:head>
<div id="reportheader"> <div id="reportheader">
<div class="flex-row-space {prefix.color}"> <div class="flex-row-space {prefix.color}">
<div class="flex-row"> <div class="flex-row">
<button class="styled" onclick={() => { <button
show_data = [...report_data]; class="styled"
onclick={() => {
show_data = copyReportData();
report_subject = "All Preferences"; report_subject = "All Preferences";
}}>All Preferences</button> }}>All Preferences</button
<button class="styled" onclick={() => { >
show_data = [...report_data.filter((entry) => entry.preference === "CALL")]; <button
report_subject = "CALL Preference" class="styled"
}}>Call</button> onclick={() => {
<button class="styled" onclick={() => { show_data = [
show_data = [...report_data.filter((entry) => entry.preference === "TEXT")]; ...report_data.filter(
(entry) => entry.preference === "CALL",
),
];
report_subject = "CALL Preference";
}}>Call</button
>
<button
class="styled"
onclick={() => {
show_data = [
...report_data.filter(
(entry) => entry.preference === "TEXT",
),
];
report_subject = "TEXT Preference"; report_subject = "TEXT Preference";
}}>Text</button> }}>Text</button
>
</div> </div>
<div class="flex-row"> <div class="flex-row">
<button class="styled" onclick={() => window.print()}>Print</button> <button class="styled" onclick={() => window.print()}>Print</button>
@@ -37,7 +57,9 @@
<table> <table>
<thead> <thead>
<tr> <tr>
<th colspan="90"><h1>{prefix.name} - Report - {report_subject}</h1></th> <th colspan="90"
><h1>{prefix.name} - Report - {report_subject}</h1></th
>
</tr> </tr>
<tr> <tr>
<th>Basket ID</th> <th>Basket ID</th>
@@ -76,7 +98,6 @@
border: solid 1px black; border: solid 1px black;
padding: 0.2rem; padding: 0.2rem;
} }
} }
table tbody tr:nth-child(2n) { table tbody tr:nth-child(2n) {
background-color: #dddddd; background-color: #dddddd;

View File

@@ -1,33 +1,53 @@
<script> <script>
import { env } from '$env/dynamic/public'; import { env } from "$env/dynamic/public";
import { browser } from '$app/environment'; import { browser } from "$app/environment";
const { data } = $props(); const { data } = $props();
const prefix = {...data.prefix}; const prefix = $derived(data.prefix);
const report_data = data.report; const report_data = $derived(data.report);
let show_data = $state([...report_data]); function copyReportData() {
let report_subject = $state("All Preferences"); return [...report_data];
if (browser) {
document.title = `${prefix.name} Report By Name`
} }
let show_data = $state(copyReportData());
let report_subject = $state("All Preferences");
</script> </script>
<svelte:head>
<title>{prefix.name} Report By Name</title>
</svelte:head>
<div id="reportheader"> <div id="reportheader">
<div class="flex-row-space {prefix.color}"> <div class="flex-row-space {prefix.color}">
<div class="flex-row"> <div class="flex-row">
<button class="styled" onclick={() => { <button
show_data = [...report_data]; class="styled"
onclick={() => {
show_data = copyReportData();
report_subject = "All Preferences"; report_subject = "All Preferences";
}}>All Preferences</button> }}>All Preferences</button
<button class="styled" onclick={() => { >
show_data = [...report_data.filter((entry) => entry.preference === "CALL")]; <button
report_subject = "CALL Preference" class="styled"
}}>Call</button> onclick={() => {
<button class="styled" onclick={() => { show_data = [
show_data = [...report_data.filter((entry) => entry.preference === "TEXT")]; ...report_data.filter(
(entry) => entry.preference === "CALL",
),
];
report_subject = "CALL Preference";
}}>Call</button
>
<button
class="styled"
onclick={() => {
show_data = [
...report_data.filter(
(entry) => entry.preference === "TEXT",
),
];
report_subject = "TEXT Preference"; report_subject = "TEXT Preference";
}}>Text</button> }}>Text</button
>
</div> </div>
<div class="flex-row"> <div class="flex-row">
<button class="styled" onclick={() => window.print()}>Print</button> <button class="styled" onclick={() => window.print()}>Print</button>
@@ -37,7 +57,9 @@
<table> <table>
<thead> <thead>
<tr> <tr>
<th colspan="90"><h1>{prefix.name} - Report - {report_subject}</h1></th> <th colspan="90"
><h1>{prefix.name} - Report - {report_subject}</h1></th
>
</tr> </tr>
<tr> <tr>
<th>Winner Name</th> <th>Winner Name</th>
@@ -76,7 +98,6 @@
border: solid 1px black; border: solid 1px black;
padding: 0.2rem; padding: 0.2rem;
} }
} }
table tbody tr:nth-child(2n) { table tbody tr:nth-child(2n) {
background-color: #dddddd; background-color: #dddddd;

View File

@@ -1,18 +1,30 @@
<script> <script>
import { browser } from '$app/environment'; import { browser } from "$app/environment";
const { data } = $props(); const { data } = $props();
let stagingSettings = $state({...data.settings}); function copySettings() {
let status = $state("") return { ...data.settings };
}
let stagingSettings = $state(copySettings());
let status = $state("");
async function saveChanges() { async function saveChanges() {
const res = await fetch('/api/settings', {method: 'POST', body: JSON.stringify(stagingSettings), headers: {'Content-Type': 'application/json'}}); const res = await fetch("/api/settings", {
method: "POST",
body: JSON.stringify(stagingSettings),
headers: { "Content-Type": "application/json" },
});
if (res.ok) { if (res.ok) {
status = "Changes saved successfully"; status = "Changes saved successfully";
setTimeout(() => {status = ""}, 5000); setTimeout(() => {
status = "";
}, 5000);
} else { } else {
status = "Error saving changes, check config file path, make sure folder exists"; status =
setTimeout(() => {status = ""}, 5000); "Error saving changes, check config file path, make sure folder exists";
setTimeout(() => {
status = "";
}, 5000);
} }
} }
@@ -21,7 +33,7 @@
} }
if (browser) { if (browser) {
document.title = "TAM3 - Settings" document.title = "TAM3 - Settings";
} }
</script> </script>
@@ -31,11 +43,16 @@
<h2>Remote Server</h2> <h2>Remote Server</h2>
<div><strong>Address:</strong></div> <div><strong>Address:</strong></div>
<div><em>For example: https://ip_or_hostname:8443</em></div> <div><em>For example: https://ip_or_hostname:8443</em></div>
<div><input type="text" bind:value={stagingSettings.TAM3_REMOTE}></div> <div><input type="text" bind:value={stagingSettings.TAM3_REMOTE} /></div>
<div><strong>API Key:</strong></div> <div><strong>API Key:</strong></div>
<div class="flex-row"> <div class="flex-row">
<input type="password" bind:value={stagingSettings.TAM3_REMOTE_KEY}> <input type="password" bind:value={stagingSettings.TAM3_REMOTE_KEY} />
<button class="styled" onclick={() => {stagingSettings.TAM3_REMOTE_KEY = ""}}>Clear</button> <button
class="styled"
onclick={() => {
stagingSettings.TAM3_REMOTE_KEY = "";
}}>Clear</button
>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,128 @@
<script>
let formData = $state({ startNumber: 0, endNumber: 0, perPage: 20 });
let currentRows = $state([]);
function selectOnFocus(e) {
e.target.select();
}
function loadRows() {
currentRows = [];
for (let i = formData.startNumber; i <= formData.endNumber; i++) {
currentRows = [...currentRows, i];
}
}
</script>
<svelte:head>
<title>Print Ticket Sheets</title>
</svelte:head>
<div id="header">
<h1>Print Ticket Sheets</h1>
<div class="flex-row-space">
<div class="flex-row">
<div>
<div>Start Number</div>
<input
type="number"
onfocus={selectOnFocus}
bind:value={formData.startNumber}
/>
</div>
<div>
<div>Ending Number</div>
<input
type="number"
onfocus={selectOnFocus}
bind:value={formData.endNumber}
/>
</div>
<div>
<div>Per Page</div>
<input
type="number"
onfocus={selectOnFocus}
bind:value={formData.perPage}
/>
</div>
<div>
<button class="styled" onclick={loadRows}>Load</button>
</div>
</div>
<div class="flex-row">
<button class="styled" onclick={() => window.print()}>Print</button>
</div>
</div>
</div>
<table id="main_table">
<colgroup>
<col style="width: 15%" />
<col style="width: 40%" />
<col style="width: 35%" />
<col style="width: 10%" />
</colgroup>
<thead>
<tr>
<th>Ticket #</th>
<th>Name</th>
<th>Phone Number</th>
<th>Text?</th>
</tr>
</thead>
<tbody style="height: 100%">
{#each currentRows as row, idx}
{#if idx !== 0 && idx % formData.perPage === 0}
<tr class="pagebreak"></tr>
{/if}
<tr>
<td><strong>{row}</strong></td>
<td></td>
<td></td>
<td></td>
</tr>
{/each}
</tbody>
</table>
<style>
#main_table {
width: 100%;
height: 100%;
text-align: left;
table-layout: fixed;
th,
td {
border: solid black 1px;
white-space: nowrap;
overflow: hidden;
}
}
@media print {
#header {
display: none;
}
#main_table {
table-layout: fixed;
}
#main_table tbody {
font-size: 16pt;
}
#main_table tbody tr {
height: 32pt;
}
#main_table tbody tr.pagebreak {
break-after: page;
}
@page:after {
content: "My Text";
}
}
</style>

View File

@@ -1,16 +1,16 @@
<script> <script>
import { browser } from '$app/environment'; import { browser } from "$app/environment";
import FormHeader from '$lib/components/FormHeader.svelte'; import FormHeader from "$lib/components/FormHeader.svelte";
import { focusElement } from '$lib/focusElement.js'; import { focusElement } from "$lib/focusElement.js";
const { data } = $props(); const { data } = $props();
let prefix = {...data.prefix}; const prefix = $derived(data.prefix);
let pagerForm = $state({ id_from: 0, id_to: 0 }); let pagerForm = $state({ id_from: 0, id_to: 0 });
let current_idx = $state(0); let current_idx = $state(0);
let next_idx = $derived(current_idx + 1); let next_idx = $derived(current_idx + 1);
let prev_idx = $derived(current_idx - 1); let prev_idx = $derived(current_idx - 1);
let current_tickets = $state([]); let current_tickets = $state([]);
let copy_buffer = $state({prefix: prefix.name, t_id: 0, first_name: "", last_name: "", phone_number: "", preference: "CALL", changed: true}); let copy_buffer = $state({});
let headerHeight = $state(); let headerHeight = $state();
function changeFocus(idx) { function changeFocus(idx) {
@@ -22,42 +22,55 @@
const functions = { const functions = {
refreshPage: async () => { refreshPage: async () => {
if (current_tickets.filter(ticket => ticket.changed === true).length > 0) { if (
current_tickets.filter((ticket) => ticket.changed === true)
.length > 0
) {
functions.saveAll(); functions.saveAll();
}; }
const res = await fetch(`/api/tickets/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`); const res = await fetch(
`/api/tickets/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`,
);
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
current_tickets = [...data]; current_tickets = [...data];
setTimeout(() => changeFocus(0), 100); setTimeout(() => changeFocus(0), 100);
}; }
}, },
prevPage: () => { prevPage: () => {
const diff = current_tickets.length; const diff = current_tickets.length;
pagerForm.id_from = pagerForm.id_from - diff; pagerForm.id_from = pagerForm.id_from - diff;
pagerForm.id_to = pagerForm.id_to - diff; pagerForm.id_to = pagerForm.id_to - diff;
functions.refreshPage() functions.refreshPage();
}, },
nextPage: () => { nextPage: () => {
const diff = current_tickets.length; const diff = current_tickets.length;
pagerForm.id_from = pagerForm.id_from + diff; pagerForm.id_from = pagerForm.id_from + diff;
pagerForm.id_to = pagerForm.id_to + diff; pagerForm.id_to = pagerForm.id_to + diff;
functions.refreshPage() functions.refreshPage();
}, },
duplicateDown: () => { duplicateDown: () => {
if (current_tickets[next_idx]) { if (current_tickets[next_idx]) {
current_tickets[next_idx] = {...current_tickets[current_idx], t_id: current_tickets[next_idx].t_id, changed: true}; current_tickets[next_idx] = {
...current_tickets[current_idx],
t_id: current_tickets[next_idx].t_id,
changed: true,
};
changeFocus(next_idx); changeFocus(next_idx);
} else { } else {
changeFocus(current_idx) changeFocus(current_idx);
} }
}, },
duplicateUp: () => { duplicateUp: () => {
if (prev_idx >= 0) { if (prev_idx >= 0) {
current_tickets[prev_idx] = {...current_tickets[current_idx], t_id: current_tickets[prev_idx].t_id, changed: true}; current_tickets[prev_idx] = {
...current_tickets[current_idx],
t_id: current_tickets[prev_idx].t_id,
changed: true,
};
changeFocus(prev_idx); changeFocus(prev_idx);
} else { } else {
changeFocus(current_idx) changeFocus(current_idx);
} }
}, },
gotoNext: () => { gotoNext: () => {
@@ -77,31 +90,52 @@
copy: () => { copy: () => {
copy_buffer = { ...current_tickets[current_idx] }; copy_buffer = { ...current_tickets[current_idx] };
changeFocus(current_idx); changeFocus(current_idx);
disablePaste = false;
}, },
paste: () => { paste: () => {
current_tickets[current_idx] = {...copy_buffer, t_id: current_tickets[current_idx].t_id, changed: true}; if (Object.keys(copy_buffer).length !== 0) {
current_tickets[current_idx] = {
...copy_buffer,
t_id: current_tickets[current_idx].t_id,
changed: true,
};
}
changeFocus(current_idx); changeFocus(current_idx);
}, },
saveAll: async () => { saveAll: async () => {
const to_save = current_tickets.filter((ticket) => ticket.changed === true); const to_save = current_tickets.filter(
const res = await fetch(`/api/tickets`, {body: JSON.stringify(to_save), method: 'POST', headers: {'Content-Type': 'application/json'}}); (ticket) => ticket.changed === true,
);
const res = await fetch(`/api/tickets`, {
body: JSON.stringify(to_save),
method: "POST",
headers: { "Content-Type": "application/json" },
});
if (res.ok) { if (res.ok) {
for (let ticket of current_tickets) {ticket.changed = false}; for (let ticket of current_tickets) {
ticket.changed = false;
}
changeFocus(0); changeFocus(0);
} }
} },
}; };
if (browser) { if (browser) {
document.title = `${prefix.name} Ticket Entry`;
window.addEventListener("beforeunload", function (e) { window.addEventListener("beforeunload", function (e) {
if (current_tickets.filter(ticket => ticket.changed === true).length > 0) { if (
current_tickets.filter((ticket) => ticket.changed === true)
.length > 0
) {
e.preventDefault(); e.preventDefault();
} }
}); });
} }
</script> </script>
<svelte:head>
<title>{prefix.name} Ticket Entry</title>
</svelte:head>
<h1>{prefix.name} Ticket Entry</h1> <h1>{prefix.name} Ticket Entry</h1>
<FormHeader {prefix} {functions} bind:pagerForm bind:headerHeight /> <FormHeader {prefix} {functions} bind:pagerForm bind:headerHeight />
<table> <table>
@@ -117,16 +151,54 @@
</thead> </thead>
<tbody> <tbody>
{#each current_tickets as ticket, idx} {#each current_tickets as ticket, idx}
<tr onfocusin={() => current_idx = idx}> <tr onfocusin={() => (current_idx = idx)}>
<td>{ticket.t_id}</td> <td>{ticket.t_id}</td>
<td><input id="{idx}_fn" type="text" bind:value={ticket.first_name} onfocus={focusElement} onchange={() => ticket.changed = true}></td> <td
<td><input id="{idx}_ln" type="text" bind:value={ticket.last_name} onfocus={focusElement} onchange={() => ticket.changed = true}></td> ><input
<td><input id="{idx}_pn" type="text" bind:value={ticket.phone_number} onfocus={focusElement} onchange={() => ticket.changed = true}></td> id="{idx}_fn"
<td><select id="{idx}_pr" style="width: 100%" bind:value={ticket.preference} onfocus={focusElement} onchange={() => ticket.changed = true}> type="text"
bind:value={ticket.first_name}
onfocus={focusElement}
onchange={() => (ticket.changed = true)}
/></td
>
<td
><input
id="{idx}_ln"
type="text"
bind:value={ticket.last_name}
onfocus={focusElement}
onchange={() => (ticket.changed = true)}
/></td
>
<td
><input
id="{idx}_pn"
type="text"
bind:value={ticket.phone_number}
onfocus={focusElement}
onchange={() => (ticket.changed = true)}
/></td
>
<td
><select
id="{idx}_pr"
style="width: 100%"
bind:value={ticket.preference}
onfocus={focusElement}
onchange={() => (ticket.changed = true)}
>
<option value="CALL">Call</option> <option value="CALL">Call</option>
<option value="TEXT">Text</option> <option value="TEXT">Text</option>
</select></td> </select></td
<td><button tabindex="-1" onclick={() => ticket.changed = !ticket.changed}>{ticket.changed ? "Y" : "N"}</button></td> >
<td
><button
tabindex="-1"
onclick={() => (ticket.changed = !ticket.changed)}
>{ticket.changed ? "Y" : "N"}</button
></td
>
</tr> </tr>
{/each} {/each}
</tbody> </tbody>
@@ -156,7 +228,8 @@
background: transparent; background: transparent;
border: solid 1px #000000; border: solid 1px #000000;
} }
input, button { input,
button {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;