Compare commits

5 Commits

Author SHA1 Message Date
5d24b80e26 (fix): prefixes 2025-10-21 22:51:31 -04:00
32263ad6c6 MM test correction 2025-10-21 22:44:34 -04:00
382eddf8ea MM revamp, save prompt, upload fix 2025-10-21 22:27:13 -04:00
0b0ae291d0 (change): MM revamp, scroll behavior 2025-10-19 21:35:18 -04:00
d7523f3e67 0.1.1 2025-10-19 21:00:31 -04:00
13 changed files with 84 additions and 33 deletions

View File

@@ -9,9 +9,9 @@ if [ $rmserver = "y" -o $rmserver = "Y" ]; then
read -p "Enter the protocol, server host/ip, and port like "https://ip_or_host:8443" w/o quotes: " serveraddr
read -p "Paste in (Ctrl + Shift + V on most terminal emulators) or enter the api key you generated for your server: " serverapi
if [ -x "$(command -v docker)" ]; then
docker run -d --name=tam3-webclient --restart=always -v ~/.config/TAM3/data:/data:rw,z -e TAM3_REMOTE=$serveraddr -e TAM3_REMOTE_KEY=$serverapi -e NODE_TLS_REJECT_UNAUTHORIZED=0 -e PUBLIC_TAM3_VENUE="$venuename" -p 127.0.0.1:8300:3000 docker.io/dbob16/tam3-webclient:0.1.0
docker run -d --name=tam3-webclient --restart=always -v ~/.config/TAM3/data:/data:rw,z -e TAM3_REMOTE=$serveraddr -e TAM3_REMOTE_KEY=$serverapi -e NODE_TLS_REJECT_UNAUTHORIZED=0 -e PUBLIC_TAM3_VENUE="$venuename" -p 127.0.0.1:8300:3000 docker.io/dbob16/tam3-webclient:0.1.1
elif [ -x "$(command -v podman)" ]; then
podman run -d --name=tam3-webclient --restart=always -v ~/.config/TAM3/data:/data:rw,z -e TAM3_REMOTE=$serveraddr -e TAM3_REMOTE_KEY=$serverapi -e NODE_TLS_REJECT_UNAUTHORIZED=0 -e PUBLIC_TAM3_VENUE="$venuename" -p 127.0.0.1:8300:3000 docker.io/dbob16/tam3-webclient:0.1.0
podman run -d --name=tam3-webclient --restart=always -v ~/.config/TAM3/data:/data:rw,z -e TAM3_REMOTE=$serveraddr -e TAM3_REMOTE_KEY=$serverapi -e NODE_TLS_REJECT_UNAUTHORIZED=0 -e PUBLIC_TAM3_VENUE="$venuename" -p 127.0.0.1:8300:3000 docker.io/dbob16/tam3-webclient:0.1.1
runin_podman="true"
else
echo "Neither Docker nor Podman are installed. Please install whichever you prefer and try again."
@@ -19,9 +19,9 @@ exit 1
fi
else
if [ -x "$(command -v docker)" ]; then
docker run -d --name=tam3-webclient --restart=always -v ~/.config/TAM3/data:/data:rw,z -e PUBLIC_TAM3_VENUE="$venuename" -p 127.0.0.1:8300:3000 docker.io/dbob16/tam3-webclient:0.1.0
docker run -d --name=tam3-webclient --restart=always -v ~/.config/TAM3/data:/data:rw,z -e PUBLIC_TAM3_VENUE="$venuename" -p 127.0.0.1:8300:3000 docker.io/dbob16/tam3-webclient:0.1.1
elif [ -x "$(command -v podman )" ]; then
podman run -d --name=tam3-webclient --restart=always -v ~/.config/TAM3/data:/data:rw,z -e PUBLIC_TAM3_VENUE="$venuename" -p 127.0.0.1:8300:3000 docker.io/dbob16/tam3-webclient:0.1.0
podman run -d --name=tam3-webclient --restart=always -v ~/.config/TAM3/data:/data:rw,z -e PUBLIC_TAM3_VENUE="$venuename" -p 127.0.0.1:8300:3000 docker.io/dbob16/tam3-webclient:0.1.1
runin_podman="true"
else
echo "Neither Docker nor Podman are installed. Please install whichever you prefer and try again."

View File

@@ -1,6 +1,6 @@
services:
tam3-db:
image: docker.io/dbob16/tam3-db:0.1.0
image: docker.io/dbob16/tam3-db:0.1.1
restart: always
environment:
MARIADB_RANDOM_ROOT_PASSWORD: 1
@@ -16,7 +16,7 @@ services:
timeout: 5s
retries: 3
tam3-api:
image: docker.io/dbob16/tam3-api:0.1.0
image: docker.io/dbob16/tam3-api:0.1.1
restart: always
environment:
TAM3_DATA_PATH: /data

0
deployment/remote_server_offline_images/server-load.sh Normal file → Executable file
View File

View File

@@ -1,6 +1,6 @@
services:
tam3-db:
image: docker.io/dbob16/tam3-db:0.1.0
image: docker.io/dbob16/tam3-db:0.1.1
restart: always
environment:
MARIADB_RANDOM_ROOT_PASSWORD: 1
@@ -16,7 +16,7 @@ services:
timeout: 5s
retries: 3
tam3-api:
image: docker.io/dbob16/tam3-api:0.1.0
image: docker.io/dbob16/tam3-api:0.1.1
restart: always
environment:
TAM3_DATA_PATH: /data

View File

@@ -7,6 +7,8 @@ COPY . .
RUN npm install && npm run build
ENV DATABASE_URL=file:/data/local.db
ENV BODY_SIZE_LIMIT=100M
ENV NODE_TLS_REJECT_UNAUTHORIZED=0
COPY deploy/start-server.sh start-server.sh

View File

@@ -28,7 +28,12 @@
<input type="number" bind:value={pagerForm.id_from}>
<div style="font-size: 22pt">-</div>
<input type="number" bind:value={pagerForm.id_to}>
<button class="styled" onclick={functions.refreshPage}>Refresh</button>
<button class="styled" onclick={() => {
if (Math.abs(pagerForm.id_to - pagerForm.id_from) > 800) {
pagerForm.id_to = pagerForm.id_from + 799;
}
setTimeout(functions.refreshPage, 50);
}}>Refresh</button>
</div>
<div class="flex-row">
<button class="styled" title="Alt + B" tabindex="-1" onclick={functions.prevPage}>Prev Page</button>

View File

@@ -15,6 +15,15 @@ a.styled, button.styled {
border-radius: 0.25rem;
}
.p025 {
padding: 0.25rem;
}
.active {
border: 1px solid #000000;
border-radius: 0.13rem;
}
a.styled:hover, button.styled:hover {
text-decoration: underline;
cursor: pointer;

View File

@@ -5,8 +5,8 @@
import favicon from "$lib/assets/favicon.svg"
let { data } = $props();
const all_prefixes = [...data.prefixes];
let prefix_name = $state("");
let all_prefixes = $state([]);
let current_prefix = $state({name: "", color: "", weight: 0});
let admin_mode = $state(false);
const venue = env.PUBLIC_TAM3_VENUE || "TAM3";
@@ -19,7 +19,6 @@
})
if (browser) {
all_prefixes = [...data.prefixes];
document.title = `${venue} - Main Menu`;
hotkeys.filter = function(event) {return true};
hotkeys('alt+a', function(event) {event.preventDefault(); admin_mode = !admin_mode; return false;});
@@ -36,18 +35,24 @@
<img src="{favicon}" alt="TAM3 Icon - Red ticket with TAM3 on it" class="icon">
<h1>{venue} - Main Menu</h1>
</div>
{#if all_prefixes.length > 0}
<div class="universal-reports flex-row tb-margin">
<a href="/counts" target="_blank" class="styled">Counts</a>
</div>
<div>
<h2>Select Prefix:</h2>
<h2>Current Prefix: {current_prefix.name}</h2>
</div>
<div class="prefix-selector">
<select style="width: 100%; box-sizing: border-box;" bind:value={prefix_name}>
{#each all_prefixes as prefix}
<option value={prefix.name}>{prefix.name}</option>
{/each}
</select>
<div class="change_title">
<h2>Change Prefix:</h2>
</div>
<div class="prefix-selector flex-row">
{#each all_prefixes as prefix}
<div class="{prefix.color} p025{prefix.name === prefix_name ? " active" : ""}">
<button class="styled" onclick={() => {
prefix_name = prefix.name;
}}>{prefix.name}</button>
</div>
{/each}
</div>
<div><h2>Forms:</h2></div>
<div class="flex-row {current_prefix.color}">
@@ -60,14 +65,17 @@
<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>
{#if admin_mode}
<div><h2>Admin Mode:</h2></div>
<div class="flex-row {current_prefix.color}">
<a href="/prefixes" target="_blank" class="styled">Prefix Editor</a>
<a href="/backuprestore" target="_blank" class="styled">Backup/Restore</a>
</div>
{:else}
<p>There aren't any prefixes available, please create them.</p>
{/if}
</div>
{#if admin_mode}
<div><h2>Admin Mode:</h2></div>
<div class="flex-row">
<a href="/prefixes" target="_blank" class="styled">Prefix Editor</a>
<a href="/backuprestore" target="_blank" class="styled">Backup/Restore</a>
</div>
{/if}
<div class="status tb-margin">
{data.status}

View File

@@ -1,6 +1,15 @@
import { env } from "$env/dynamic/private";
import { db } from "$lib/server/db";
import { prefixes, tickets, baskets } from "$lib/server/db/schema";
import { sql } from "drizzle-orm";
function chunkArray(arr, chunkSize) {
const chunks = [];
for (let i = 0; i < arr.length; i += chunkSize) {
chunks.push(arr.slice(i, i + chunkSize));
}
return chunks;
}
export async function GET() {
if (env.TAM3_REMOTE) {
@@ -20,16 +29,16 @@ export async function GET() {
export async function POST({ request }) {
const req = await request.json();
for (let prefix of req.prefixes) {
await db.insert(prefixes).values({name: prefix.name, color: prefix.color, weight: prefix.weight}).onConflictDoUpdate({target: prefixes.name, set: {color: prefix.color, weight: prefix.weight}});
for (let prefixChunk of chunkArray(req.prefixes, 300)) {
await db.insert(prefixes).values(prefixChunk).onConflictDoUpdate({target: prefixes.name, set: {color: sql`excluded.color`, weight: sql`excluded.weight`}});
};
for (let ticket of req.tickets) {
await db.insert(tickets).values({prefix: ticket.prefix, t_id: ticket.t_id, first_name: ticket.first_name, last_name: ticket.last_name, phone_number: ticket.phone_number, preference: ticket.preference})
.onConflictDoUpdate({target: [tickets.prefix, tickets.t_id], set: {first_name: ticket.first_name, last_name: ticket.last_name, phone_number: ticket.phone_number, preference: ticket.preference}});
for (let ticketChunk of chunkArray(req.tickets, 300)) {
await db.insert(tickets).values(ticketChunk)
.onConflictDoUpdate({target: [tickets.prefix, tickets.t_id], set: {first_name: sql`excluded.first_name`, last_name: sql`excluded.last_name`, phone_number: sql`excluded.phone_number`, preference: sql`excluded.preference`}});
};
for (let basket of req.baskets) {
await db.insert(baskets).values({prefix: basket.prefix, b_id: basket.b_id, description: basket.description, donors: basket.donors, winning_ticket: basket.winning_ticket})
.onConflictDoUpdate({target: [baskets.prefix, baskets.b_id], set: {description: basket.description, donors: basket.donors, winning_ticket: basket.winning_ticket}})
for (let basketChunk of chunkArray(req.baskets, 300)) {
await db.insert(baskets).values(basketChunk)
.onConflictDoUpdate({target: [baskets.prefix, baskets.b_id], set: {description: sql`excluded.description`, donors: sql`excluded.donors`, winning_ticket: sql`excluded.winning_ticket`}})
};
if (env.TAM3_REMOTE) {
const res = await fetch(`${env.TAM3_REMOTE}/api/backuprestore/?api_key=${env.TAM3_REMOTE_KEY}`, {

View File

@@ -14,6 +14,7 @@
const focusDe = document.getElementById(`${idx}_de`);
if (focusDe) {
focusDe.select();
focusDe.scrollIntoView({block: "center"});
}
}
@@ -93,6 +94,11 @@
if (browser) {
document.title = `${prefix.name} Basket Entry`
window.addEventListener("beforeunload", function(e) {
if (current_baskets.filter(basket => basket.changed === true).length > 0) {
e.preventDefault();
}
})
}
</script>

View File

@@ -13,6 +13,7 @@
const focusWt = document.getElementById(`${idx}_wt`);
if (focusWt) {
focusWt.select();
focusWt.scrollIntoView({block: "center"});
}
}
@@ -92,6 +93,11 @@
if (browser) {
document.title = `${prefix.name} Drawing Form`
window.addEventListener("beforeunload", function(e) {
if (current_drawings.filter(drawing => drawing.changed === true).length > 0) {
e.preventDefault();
}
});
}
</script>

View File

@@ -9,7 +9,7 @@
let report_subject = $state("All Preferences");
if (browser) {
document.title = `${prefix.name} Report By Name`
document.title = `${prefix.name} Report By Basket ID`
}
</script>

View File

@@ -13,6 +13,7 @@
const focusFn = document.getElementById(`${idx}_fn`);
if (focusFn) {
focusFn.select();
focusFn.scrollIntoView({block: "center"});
}
}
@@ -94,6 +95,11 @@
if (browser) {
document.title = `${prefix.name} Ticket Entry`;
window.addEventListener("beforeunload", function(e) {
if (current_tickets.filter(ticket => ticket.changed === true).length > 0) {
e.preventDefault();
}
});
}
</script>