Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56d7d1068d | ||
|
|
103f1cf3c4 | ||
|
|
a11c313a8e | ||
|
|
1b3c97516c | ||
|
|
f67e1c184e | ||
|
|
6c17f85e4c | ||
|
|
4869c119d0 |
No files matched your search
@@ -2,6 +2,73 @@
|
|||||||
|
|
||||||
This is Ticket Auction Manager. A project I (Dilan Gilluly) am working on as a hobby project. It's main scope is to manage in person penny socials or benefit auctions.
|
This is Ticket Auction Manager. A project I (Dilan Gilluly) am working on as a hobby project. It's main scope is to manage in person penny socials or benefit auctions.
|
||||||
|
|
||||||
## Under Development
|
The remote server (api directory) is written in FastAPI and Python and the client (client directory) is written in Sveltekit.
|
||||||
|
|
||||||
This project is **NOT COMPLETE** and is under **active development**. Also I am not a programmer by profession so please forgive any beginner mistakes I make.
|
Features:
|
||||||
|
|
||||||
|
- **Forms**: Facilitates the entry of data throughout the platform. The main goal of this is to allow one to manage in-person benefit auctions for non-profit causes.
|
||||||
|
- **Ticket Form**: Enter the names and phone numbers of ticket purchasers, as well as their contact preference, which the default is controllable via the Settings screen.
|
||||||
|
- **Basket Form**: Optionally, basket/item descriptions can be added as well as who the donor(s) are for each one. The descriptions appear on the reports later on.
|
||||||
|
- **Drawing Form**: Use this form to enter the winning ticket numbers. The form automatically looks up if an entry exists when a field is changed and will popluate the information next to it if it does.
|
||||||
|
- **Reports**: Reports are automatically generated with one click to avoid line shifts or other issues which may arise during compilation.
|
||||||
|
- **By Name Report**: This report orders the lines by the last name of each winner, then first name, phone number, and finally basket number.
|
||||||
|
- **By Basket Report**: Orders winners by basket number.
|
||||||
|
- **Counts Report**: Displays counts of ticket sales by prefix as well as totals.
|
||||||
|
- **Settings**:
|
||||||
|
- **Settings section**: The Admin/settings section can be accessed on the main menu by pressing Alt(option)+A. The combination toggles it so if you want it to go away again, just press Alt(option)+A again.
|
||||||
|
- **Settings page**: Allows you to control the base options of the operation. Including remote mode (leave the Remote Server field blank for standalone(offline) mode or put in a remote server to connect to a server), remote port, TLS, default contact preference, and the name of the venue/benefit (appears on main menu as well as reports).
|
||||||
|
- **Auth Keys**: Allows you to manage auth keys if it's in remote mode. To do so, you need to know the auth password on the server.
|
||||||
|
- **Prefixes**: Allows you to add or change prefixes which are available on the main menu to be able to access the respective forms for each prefix. Note, **you need to add prefixes through this form after first installation of either client or server to be able to access forms and reports.**
|
||||||
|
- **Remote Mode**: Remote mode, which is configurable in the Settings screen, by putting in a remote server address/hostname, allows data to be synched across multiple computers for large scale operations.
|
||||||
|
|
||||||
|
## Cloning the repo
|
||||||
|
|
||||||
|
To clone the repo you just need to run the git clone command to clone it to a directory of your choosing. I generally try to push to a few different git providers just so that if one has an outage, it allows you to go with another one for the time being. Replace 'yourrepofolder' at the end with the folder/dir of your choosing.
|
||||||
|
|
||||||
|
Gitlab:
|
||||||
|
|
||||||
|
`git clone https://www.gitlab.com/Ticket-Auction-Manager/tam yourrepofolder`
|
||||||
|
|
||||||
|
Github:
|
||||||
|
|
||||||
|
`git clone https://www.github.com/Ticket-Auction-Manager/tam yourrepofolder`
|
||||||
|
|
||||||
|
My own git server:
|
||||||
|
|
||||||
|
`git clone https://git.dilangilluly.us/Ticket-Auction-Manager/tam yourrepofolder`
|
||||||
|
|
||||||
|
## Installing dependencies
|
||||||
|
|
||||||
|
Server:
|
||||||
|
|
||||||
|
(needs Python installed, and a virtual environment activated depending on OS distro and Python version)
|
||||||
|
|
||||||
|
```
|
||||||
|
cd api
|
||||||
|
pip install -r app/requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Client:
|
||||||
|
|
||||||
|
(needs pnpm installed)
|
||||||
|
|
||||||
|
```
|
||||||
|
cd client
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running dev instances
|
||||||
|
|
||||||
|
Server:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd api
|
||||||
|
sh start_dev.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Client:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd client
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
+13
-30
@@ -1,42 +1,25 @@
|
|||||||
# sv
|
# Ticket Auction Manager (Client)
|
||||||
|
|
||||||
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
The contents of this directory of the Ticket Auction Manager repo pertains to just the client side. Which is a web-based client interface, allowing users to use Ticket Auction Manager.
|
||||||
|
|
||||||
## Creating a project
|
Due to dev resources already being in the main repo, I have decided to make this dedicated to users instead of developers.
|
||||||
|
|
||||||
If you're seeing this, you've probably already done this step. Congrats!
|
## Running TAM
|
||||||
|
|
||||||
```sh
|
You have some choice when it comes to running TAM, using Docker or through a portable Node environment (coming soon!).
|
||||||
# create a new project
|
|
||||||
npx sv create my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
To recreate this project with the same configuration:
|
### Docker
|
||||||
|
|
||||||
```sh
|
Docker images are available under the dbob16/tam-client repo within Docker Hub. To pull the latest image:
|
||||||
# recreate this project
|
|
||||||
pnpm dlx sv@0.16.1 create --template minimal --no-types --add prettier eslint tailwindcss="plugins:none" sveltekit-adapter="adapter:node" drizzle="database:sqlite+sqlite:better-sqlite3" --install pnpm ./client
|
|
||||||
```
|
|
||||||
|
|
||||||
## Developing
|
`docker pull dbob16/tam-client:latest`
|
||||||
|
|
||||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
Then to run it:
|
||||||
|
|
||||||
```sh
|
`docker run -d --name tam-client --network host --restart unless-stopped -e HOST=127.0.0.1 -e PORT=3000 -v tam-data:/data dbob16/tam-client:latest`
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# or start the server and open the app in a new browser tab
|
It should then be available in a web browser at `http://localhost:3000` on the computer it is being run on. You can bookmark or make shortcuts to it for user convenience.
|
||||||
npm run dev -- --open
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
#### Port Conflict
|
||||||
|
|
||||||
To create a production version of your app:
|
If there is a conflict with the port when you go to start TAM, you can change the port by changing the number in the `-e PORT=3000` to another integer. For instance, if you have to change it to port 3001, you can change it to `-e PORT=3001` and then navigate to `http://localhost:3001` instead.
|
||||||
|
|
||||||
```sh
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
You can preview the production build with `npm run preview`.
|
|
||||||
|
|
||||||
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
|
||||||
+16
-16
@@ -17,27 +17,27 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@sveltejs/adapter-node": "^5.5.4",
|
"@sveltejs/adapter-node": "^5.5.7",
|
||||||
"@sveltejs/kit": "^2.63.0",
|
"@sveltejs/kit": "^2.70.3",
|
||||||
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
||||||
"@tailwindcss/vite": "^4.3.0",
|
"@tailwindcss/vite": "^4.3.3",
|
||||||
"@types/better-sqlite3": "^7.6.13",
|
"@types/better-sqlite3": "^7.6.13",
|
||||||
"@types/node": "^24",
|
"@types/node": "^24.13.4",
|
||||||
"drizzle-kit": "^0.31.10",
|
"drizzle-kit": "^0.31.10",
|
||||||
"drizzle-orm": "^0.45.2",
|
"drizzle-orm": "^0.45.2",
|
||||||
"eslint": "^10.4.1",
|
"eslint": "^10.10.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-svelte": "^3.19.0",
|
"eslint-plugin-svelte": "^3.23.0",
|
||||||
"globals": "^17.6.0",
|
"globals": "^17.12.0",
|
||||||
"prettier": "^3.8.3",
|
"prettier": "^3.9.6",
|
||||||
"prettier-plugin-svelte": "^4.1.0",
|
"prettier-plugin-svelte": "^4.1.1",
|
||||||
"prettier-plugin-tailwindcss": "^0.8.0",
|
"prettier-plugin-tailwindcss": "^0.8.1",
|
||||||
"svelte": "^5.56.1",
|
"svelte": "^5.57.0",
|
||||||
"tailwindcss": "^4.3.0",
|
"tailwindcss": "^4.3.3",
|
||||||
"vite": "^8.0.16"
|
"vite": "^8.3.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"better-sqlite3": "^12.10.0",
|
"better-sqlite3": "^12.11.1",
|
||||||
"hotkeys-js": "^4.0.4"
|
"hotkeys-js": "^4.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Generated
+843
-680
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,10 @@
|
|||||||
import { db } from "$lib/server/db";
|
import { db } from '$lib/server/db';
|
||||||
|
import { env } from '$env/dynamic/public';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
export const init = async () => {
|
export const init = async () => {
|
||||||
await db.run(`CREATE TABLE IF NOT EXISTS baskets (
|
env.PUBLIC_TAM_CLIENT_ID = randomUUID();
|
||||||
|
await db.run(`CREATE TABLE IF NOT EXISTS baskets (
|
||||||
prefix text,
|
prefix text,
|
||||||
b_id integer,
|
b_id integer,
|
||||||
description text,
|
description text,
|
||||||
@@ -9,12 +12,12 @@ export const init = async () => {
|
|||||||
winning_ticket integer,
|
winning_ticket integer,
|
||||||
PRIMARY KEY(prefix, b_id)
|
PRIMARY KEY(prefix, b_id)
|
||||||
)`);
|
)`);
|
||||||
await db.run(`CREATE TABLE IF NOT EXISTS prefixes (
|
await db.run(`CREATE TABLE IF NOT EXISTS prefixes (
|
||||||
prefix text PRIMARY KEY NOT NULL,
|
prefix text PRIMARY KEY NOT NULL,
|
||||||
color text,
|
color text,
|
||||||
weight integer
|
weight integer
|
||||||
)`);
|
)`);
|
||||||
await db.run(`CREATE TABLE IF NOT EXISTS tickets (
|
await db.run(`CREATE TABLE IF NOT EXISTS tickets (
|
||||||
prefix text,
|
prefix text,
|
||||||
t_id integer,
|
t_id integer,
|
||||||
first_name text,
|
first_name text,
|
||||||
@@ -23,19 +26,39 @@ export const init = async () => {
|
|||||||
pref text,
|
pref text,
|
||||||
PRIMARY KEY(prefix, t_id)
|
PRIMARY KEY(prefix, t_id)
|
||||||
)`);
|
)`);
|
||||||
await db.run(`CREATE VIEW IF NOT EXISTS drawing AS SELECT b.prefix, b.b_id, b.description, b.winning_ticket, t.last_name, t.first_name, t.phone_number
|
await db.run(`CREATE VIEW IF NOT EXISTS drawing AS SELECT b.prefix, b.b_id, b.description, b.winning_ticket, t.last_name, t.first_name, t.phone_number
|
||||||
FROM baskets b LEFT JOIN tickets t ON b.prefix = t.prefix AND b.winning_ticket = t.t_id
|
FROM baskets b LEFT JOIN tickets t ON b.prefix = t.prefix AND b.winning_ticket = t.t_id
|
||||||
ORDER BY b.prefix, b.b_id`);
|
ORDER BY b.prefix, b.b_id`);
|
||||||
await db.run(`CREATE VIEW IF NOT EXISTS report_by_basket AS SELECT b.prefix, b.b_id, b.description, b.donors, b.winning_ticket, t.last_name, t.first_name, t.phone_number, t.pref
|
await db.run(`CREATE VIEW IF NOT EXISTS report_by_basket AS SELECT b.prefix, b.b_id, b.description, b.donors, b.winning_ticket, t.last_name, t.first_name, t.phone_number, t.pref
|
||||||
FROM baskets b LEFT JOIN tickets t on b.prefix = t.prefix AND b.winning_ticket = t.t_id
|
FROM baskets b LEFT JOIN tickets t on b.prefix = t.prefix AND b.winning_ticket = t.t_id
|
||||||
ORDER BY b.prefix, b.b_id`);
|
ORDER BY b.prefix, b.b_id`);
|
||||||
await db.run(`CREATE VIEW IF NOT EXISTS report_by_name AS SELECT t.last_name, t.first_name, t.phone_number, t.pref, b.prefix, b.b_id, b.description, b.donors, b.winning_ticket
|
await db.run(`CREATE VIEW IF NOT EXISTS report_by_name AS SELECT t.last_name, t.first_name, t.phone_number, t.pref, b.prefix, b.b_id, b.description, b.donors, b.winning_ticket
|
||||||
FROM baskets b LEFT JOIN tickets t ON b.prefix = t.prefix AND b.winning_ticket = t.t_id
|
FROM baskets b LEFT JOIN tickets t ON b.prefix = t.prefix AND b.winning_ticket = t.t_id
|
||||||
ORDER BY t.last_name, t.first_name, t.phone_number, b.prefix, b.b_id`)
|
ORDER BY t.last_name, t.first_name, t.phone_number, b.prefix, b.b_id`);
|
||||||
await db.run(`CREATE VIEW IF NOT EXISTS report_counts AS SELECT prefix, COUNT(DISTINCT(CONCAT(first_name, last_name, phone_number))) AS unique_buyers, COUNT(*) AS total_buys
|
await db.run(`CREATE VIEW IF NOT EXISTS report_counts AS SELECT prefix, COUNT(DISTINCT(CONCAT(first_name, last_name, phone_number))) AS unique_buyers, COUNT(*) AS total_buys
|
||||||
FROM tickets
|
FROM tickets
|
||||||
GROUP BY prefix
|
GROUP BY prefix
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT 'Total', COUNT(DISTINCT(CONCAT(first_name, last_name, phone_number))), COUNT(*)
|
SELECT 'Total', COUNT(DISTINCT(CONCAT(first_name, last_name, phone_number))), COUNT(*)
|
||||||
FROM tickets`);
|
FROM tickets`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handle = async ({ event, resolve }) => {
|
||||||
|
if (event.url.pathname.startsWith('/api')) {
|
||||||
|
const clientID = event.request.headers.get('TAM-CLIENT-ID');
|
||||||
|
if (env.PUBLIC_TAM_CLIENT_ID == clientID) {
|
||||||
|
const response = await resolve(event);
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
return new Response(JSON.stringify({detail: "Unauthorized"}), { status: 401 })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const response = await resolve(event);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleFetch = async ({ request, fetch }) => {
|
||||||
|
request.headers.set('TAM-CLIENT-ID', env.PUBLIC_TAM_CLIENT_ID);
|
||||||
|
return fetch(request);
|
||||||
}
|
}
|
||||||
@@ -78,5 +78,10 @@
|
|||||||
>Save Marked</button
|
>Save Marked</button
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if functions.cancel}
|
||||||
|
<button class={bS[prefix.color]} title="Alt + S" onclick={() => functions.cancel()}
|
||||||
|
>Cancel Marked</button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,9 +40,14 @@
|
|||||||
onclick={(e) => {
|
onclick={(e) => {
|
||||||
e.target.select();
|
e.target.select();
|
||||||
}}
|
}}
|
||||||
onchange={() => {
|
oninput={() => {
|
||||||
if (functions.pagerFromUpdate) functions.pagerFromUpdate();
|
if (functions.pagerFromUpdate) functions.pagerFromUpdate();
|
||||||
}}
|
}}
|
||||||
|
onkeydown={(e) => {
|
||||||
|
if (e.key == "Enter") {
|
||||||
|
functions.getPage()
|
||||||
|
}
|
||||||
|
}}
|
||||||
bind:value={pager.idFrom}
|
bind:value={pager.idFrom}
|
||||||
/>
|
/>
|
||||||
<div>-</div>
|
<div>-</div>
|
||||||
@@ -54,6 +59,11 @@
|
|||||||
onclick={(e) => {
|
onclick={(e) => {
|
||||||
e.target.select();
|
e.target.select();
|
||||||
}}
|
}}
|
||||||
|
onkeydown={(e) => {
|
||||||
|
if (e.key == "Enter") {
|
||||||
|
functions.getPage()
|
||||||
|
}
|
||||||
|
}}
|
||||||
bind:value={pager.idTo}
|
bind:value={pager.idTo}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { env } from '$env/dynamic/private';
|
import { env } from '$env/dynamic/private';
|
||||||
import { readFileSync, writeFileSync, mkdirSync } from 'fs';
|
import { readFileSync, writeFileSync } from 'fs';
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
remote_server: '',
|
remote_server: '',
|
||||||
@@ -7,7 +7,8 @@ const defaultSettings = {
|
|||||||
remote_port: '8000',
|
remote_port: '8000',
|
||||||
remote_tls: false,
|
remote_tls: false,
|
||||||
default_pref: 'CALL',
|
default_pref: 'CALL',
|
||||||
venue_name: 'TAM'
|
venue_name: 'Venue Name',
|
||||||
|
disable_attrib: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSettings = () => {
|
export const getSettings = () => {
|
||||||
@@ -16,7 +17,6 @@ export const getSettings = () => {
|
|||||||
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
||||||
return settings;
|
return settings;
|
||||||
} catch {
|
} catch {
|
||||||
mkdirSync(env.TAM_DATA_DIR || './data', { recursive: true });
|
|
||||||
writeFileSync(settingsPath, JSON.stringify(defaultSettings, null, 2), 'utf-8');
|
writeFileSync(settingsPath, JSON.stringify(defaultSettings, null, 2), 'utf-8');
|
||||||
return defaultSettings;
|
return defaultSettings;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { env } from "$env/dynamic/public"
|
||||||
|
|
||||||
|
export const load = async () => {
|
||||||
|
const tamClientID = `${env.PUBLIC_TAM_CLIENT_ID}`;
|
||||||
|
return { tamClientID }
|
||||||
|
}
|
||||||
@@ -2,9 +2,10 @@ import { getSettings } from '$lib/server/settings';
|
|||||||
|
|
||||||
export const load = async ({ fetch }) => {
|
export const load = async ({ fetch }) => {
|
||||||
const settings = getSettings();
|
const settings = getSettings();
|
||||||
|
const disableAttrib = settings.disable_attrib || false;
|
||||||
const res = await fetch('/api');
|
const res = await fetch('/api');
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const resPrefixes = await fetch('/api/prefixes');
|
const resPrefixes = await fetch('/api/prefixes');
|
||||||
const dataPrefixes = await resPrefixes.json();
|
const dataPrefixes = await resPrefixes.json();
|
||||||
return { ...data, venueName: settings.venue_name, prefixes: dataPrefixes };
|
return { ...data, venueName: settings.venue_name, prefixes: dataPrefixes, disableAttrib };
|
||||||
};
|
};
|
||||||
@@ -134,12 +134,15 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<div class="text-center text-xs">
|
<div class="text-center text-xs">
|
||||||
<p>© 2026 Ticket Auction Manager</p>
|
<p>© 2026 Ticket Auction Manager</p>
|
||||||
|
{#if !data.disableAttrib}
|
||||||
<p>
|
<p>
|
||||||
Created by Dilan Gilluly. Support me on <a
|
Created by Dilan Gilluly. <a
|
||||||
href="https://ko-fi.com/techguydilan"
|
href="https://ko-fi.com/techguydilan"
|
||||||
class="text-blue-500">Ko-Fi</a
|
class="text-blue-500"
|
||||||
|
target="_blank">My Ko-Fi</a
|
||||||
>.
|
>.
|
||||||
</p>
|
</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -15,44 +15,50 @@ export const GET = async () => {
|
|||||||
|
|
||||||
export const POST = async ({ request }) => {
|
export const POST = async ({ request }) => {
|
||||||
const reqData = await request.json();
|
const reqData = await request.json();
|
||||||
for (let i = 0; i < reqData.prefixes.length; i += chunk_size) {
|
if (reqData.prefixes) {
|
||||||
const chunk = reqData.prefixes.slice(i, i + chunk_size);
|
for (let i = 0; i < reqData.prefixes.length; i += chunk_size) {
|
||||||
await db
|
const chunk = reqData.prefixes.slice(i, i + chunk_size);
|
||||||
.insert(prefixes)
|
await db
|
||||||
.values(chunk)
|
.insert(prefixes)
|
||||||
.onConflictDoUpdate({
|
.values(chunk)
|
||||||
target: prefixes.prefix,
|
.onConflictDoUpdate({
|
||||||
set: { color: sql`EXCLUDED.color`, weight: sql`EXCLUDED.weight` }
|
target: prefixes.prefix,
|
||||||
});
|
set: { color: sql`EXCLUDED.color`, weight: sql`EXCLUDED.weight` }
|
||||||
}
|
});
|
||||||
for (let i = 0; i < reqData.baskets.length; i += chunk_size) {
|
};
|
||||||
const chunk = reqData.baskets.slice(i, i + chunk_size);
|
};
|
||||||
await db
|
if (reqData.baskets) {
|
||||||
.insert(baskets)
|
for (let i = 0; i < reqData.baskets.length; i += chunk_size) {
|
||||||
.values(chunk)
|
const chunk = reqData.baskets.slice(i, i + chunk_size);
|
||||||
.onConflictDoUpdate({
|
await db
|
||||||
target: [baskets.prefix, baskets.b_id],
|
.insert(baskets)
|
||||||
set: {
|
.values(chunk)
|
||||||
description: sql`EXCLUDED.description`,
|
.onConflictDoUpdate({
|
||||||
donors: sql`EXCLUDED.donors`,
|
target: [baskets.prefix, baskets.b_id],
|
||||||
winning_ticket: sql`EXCLUDED.winning_ticket`
|
set: {
|
||||||
}
|
description: sql`EXCLUDED.description`,
|
||||||
});
|
donors: sql`EXCLUDED.donors`,
|
||||||
}
|
winning_ticket: sql`EXCLUDED.winning_ticket`
|
||||||
for (let i = 0; i < reqData.tickets.length; i += chunk_size) {
|
}
|
||||||
const chunk = reqData.tickets.slice(i, i + chunk_size);
|
});
|
||||||
await db
|
};
|
||||||
.insert(tickets)
|
};
|
||||||
.values(chunk)
|
if (reqData.tickets) {
|
||||||
.onConflictDoNothing({
|
for (let i = 0; i < reqData.tickets.length; i += chunk_size) {
|
||||||
target: [tickets.prefix, tickets.t_id],
|
const chunk = reqData.tickets.slice(i, i + chunk_size);
|
||||||
set: {
|
await db
|
||||||
first_name: sql`EXCLUDED.first_name`,
|
.insert(tickets)
|
||||||
last_name: sql`EXCLUDED.last_name`,
|
.values(chunk)
|
||||||
phone_number: sql`EXCLUDED.phone_number`,
|
.onConflictDoNothing({
|
||||||
pref: sql`EXCLUDED.pref`
|
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`,
|
||||||
|
pref: sql`EXCLUDED.pref`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
return json({ details: 'Data loaded successfully.' });
|
return json({ details: 'Data loaded successfully.' });
|
||||||
};
|
};
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
import CommandBar from '$lib/client/components/CommandBar.svelte';
|
import CommandBar from '$lib/client/components/CommandBar.svelte';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
let { prefix, prefixes } = $derived(data);
|
let { prefix, prefixes, tamClientID } = $derived(data);
|
||||||
|
|
||||||
let pageTitle = $derived(`${prefix.prefix} Baskets | TAM`);
|
let pageTitle = $derived(`${prefix.prefix} Baskets | TAM`);
|
||||||
|
|
||||||
@@ -27,35 +27,36 @@
|
|||||||
|
|
||||||
let pager = $state({ idFrom: 0, idTo: 0 });
|
let pager = $state({ idFrom: 0, idTo: 0 });
|
||||||
let items = $state([]);
|
let items = $state([]);
|
||||||
let itemsLength = $derived.by(() => (items.length ? items.length : 0));
|
let itemsLength = $derived(items.length || 1);
|
||||||
let itemsBuffer = $derived(items.filter((i) => i.changed));
|
let itemsBuffer = $derived(items.filter((i) => i.changed));
|
||||||
const functions = {
|
const functions = {
|
||||||
getPage: async () => {
|
async getPage() {
|
||||||
functions.save();
|
this.save();
|
||||||
if (pager.idFrom > pager.idTo) {
|
if (pager.idFrom > pager.idTo) {
|
||||||
[pager.idFrom, pager.idTo] = [pager.idTo, pager.idFrom];
|
[pager.idFrom, pager.idTo] = [pager.idTo, pager.idFrom];
|
||||||
}
|
}
|
||||||
if (pager.idTo - pager.idFrom > 300) {
|
if (pager.idTo - pager.idFrom > 300) {
|
||||||
pager.idTo = pager.idFrom + 300;
|
pager.idTo = pager.idFrom + 300;
|
||||||
}
|
}
|
||||||
const res = await fetch(`/api/baskets/${prefix.prefix}/${pager.idFrom}/${pager.idTo}`);
|
const res = await fetch(`/api/baskets/${prefix.prefix}/${pager.idFrom}/${pager.idTo}`, {
|
||||||
|
headers: { 'TAM-CLIENT-ID': tamClientID }
|
||||||
|
});
|
||||||
const resData = await res.json();
|
const resData = await res.json();
|
||||||
resData.map((i) => (i.changed = false));
|
resData.map((i) => (i.changed = false));
|
||||||
items = [...resData];
|
items = [...resData];
|
||||||
setTimeout(() => focusIdx(0));
|
setTimeout(() => focusIdx(0));
|
||||||
},
|
},
|
||||||
pagerFromUpdate(){
|
pagerFromUpdate() {
|
||||||
if (itemsLength === 0) {
|
pager.idTo = pager.idFrom + (itemsLength - 1);
|
||||||
pager.idTo = pager.idFrom;
|
|
||||||
} else {
|
|
||||||
pager.idTo = pager.idFrom + (itemsLength - 1);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
save: async () => {
|
async save() {
|
||||||
if (itemsBuffer.length > 0) {
|
if (itemsBuffer.length > 0) {
|
||||||
const res = await fetch('/api/baskets', {
|
const res = await fetch('/api/baskets', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'TAM-CLIENT-ID': tamClientID
|
||||||
|
},
|
||||||
body: JSON.stringify(itemsBuffer)
|
body: JSON.stringify(itemsBuffer)
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@@ -68,15 +69,21 @@
|
|||||||
focusIdx(0);
|
focusIdx(0);
|
||||||
}, 1);
|
}, 1);
|
||||||
},
|
},
|
||||||
prevPage: () => {
|
cancel() {
|
||||||
|
if (itemsBuffer.length > 0) {
|
||||||
|
itemsBuffer.forEach((i) => (i.changed = false));
|
||||||
|
this.getPage();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
prevPage() {
|
||||||
((pager.idFrom -= itemsLength), (pager.idTo -= itemsLength));
|
((pager.idFrom -= itemsLength), (pager.idTo -= itemsLength));
|
||||||
functions.getPage();
|
this.getPage();
|
||||||
},
|
},
|
||||||
nextPage: () => {
|
nextPage() {
|
||||||
((pager.idFrom += itemsLength), (pager.idTo += itemsLength));
|
((pager.idFrom += itemsLength), (pager.idTo += itemsLength));
|
||||||
functions.getPage();
|
this.getPage();
|
||||||
},
|
},
|
||||||
nextLine: () => {
|
nextLine() {
|
||||||
if (items[nextIdx]) {
|
if (items[nextIdx]) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
focusIdx(nextIdx);
|
focusIdx(nextIdx);
|
||||||
@@ -87,7 +94,7 @@
|
|||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
prevLine: () => {
|
prevLine() {
|
||||||
if (curIdx > 0) {
|
if (curIdx > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
focusIdx(prevIdx);
|
focusIdx(prevIdx);
|
||||||
@@ -98,27 +105,27 @@
|
|||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dupDown: () => {
|
dupDown() {
|
||||||
if (items[nextIdx]) {
|
if (items[nextIdx]) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 'b_id'].forEach((key) => delete buffer[key]);
|
['prefix', 'b_id'].forEach((key) => delete buffer[key]);
|
||||||
items[nextIdx] = { ...items[nextIdx], ...buffer, changed: true };
|
items[nextIdx] = { ...items[nextIdx], ...buffer, changed: true };
|
||||||
functions.nextLine();
|
this.nextLine();
|
||||||
} else {
|
} else {
|
||||||
focusIdx(curIdx);
|
focusIdx(curIdx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dupUp: () => {
|
dupUp() {
|
||||||
if (curIdx > 0) {
|
if (curIdx > 0) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 'b_id'].forEach((key) => delete buffer[key]);
|
['prefix', 'b_id'].forEach((key) => delete buffer[key]);
|
||||||
items[prevIdx] = { ...items[prevIdx], ...buffer, changed: true };
|
items[prevIdx] = { ...items[prevIdx], ...buffer, changed: true };
|
||||||
functions.prevLine();
|
this.prevLine();
|
||||||
} else {
|
} else {
|
||||||
focusIdx(curIdx);
|
focusIdx(curIdx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
copy: () => {
|
copy() {
|
||||||
if (items[curIdx]) {
|
if (items[curIdx]) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
||||||
@@ -126,7 +133,7 @@
|
|||||||
}
|
}
|
||||||
focusIdx(curIdx);
|
focusIdx(curIdx);
|
||||||
},
|
},
|
||||||
paste: () => {
|
paste() {
|
||||||
if (items[curIdx]) {
|
if (items[curIdx]) {
|
||||||
const buffer = JSON.parse(window.localStorage.getItem('tam-basket'));
|
const buffer = JSON.parse(window.localStorage.getItem('tam-basket'));
|
||||||
items[curIdx] = { ...items[curIdx], ...buffer, changed: true };
|
items[curIdx] = { ...items[curIdx], ...buffer, changed: true };
|
||||||
@@ -137,8 +144,8 @@
|
|||||||
const headers = ['Basket ID', 'Description', 'Donors', 'Save?'];
|
const headers = ['Basket ID', 'Description', 'Donors', 'Save?'];
|
||||||
|
|
||||||
if (browser) {
|
if (browser) {
|
||||||
window.addEventListener('beforeunload', (e) => {
|
window.addEventListener('beforeunload', () => {
|
||||||
if (itemsBuffer.length > 0) e.preventDefault();
|
if (itemsBuffer.length > 0) functions.save();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -186,7 +193,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
class="{iS.normal} w-full"
|
class="{iS.normal} w-full"
|
||||||
id="{idx}_first"
|
id="{idx}_first"
|
||||||
onchangecapture={() => (item.changed = true)}
|
oninput={() => (item.changed = true)}
|
||||||
bind:value={item.description}
|
bind:value={item.description}
|
||||||
/></td
|
/></td
|
||||||
>
|
>
|
||||||
@@ -195,7 +202,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
class="{iS.normal} w-full"
|
class="{iS.normal} w-full"
|
||||||
id="{idx}_second"
|
id="{idx}_second"
|
||||||
onchangecapture={() => (item.changed = true)}
|
oninput={() => (item.changed = true)}
|
||||||
bind:value={item.donors}
|
bind:value={item.donors}
|
||||||
/></td
|
/></td
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import CommandBar from '$lib/client/components/CommandBar.svelte';
|
import CommandBar from '$lib/client/components/CommandBar.svelte';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
let { prefix, prefixes } = $derived(data);
|
let { prefix, prefixes, tamClientID } = $derived(data);
|
||||||
|
|
||||||
let pageTitle = $derived(`${prefix.prefix} Drawing Form | TAM`);
|
let pageTitle = $derived(`${prefix.prefix} Drawing Form | TAM`);
|
||||||
|
|
||||||
@@ -27,35 +27,30 @@
|
|||||||
|
|
||||||
let pager = $state({ idFrom: 0, idTo: 0 });
|
let pager = $state({ idFrom: 0, idTo: 0 });
|
||||||
let items = $state([]);
|
let items = $state([]);
|
||||||
let itemsLength = $derived.by(() => (items.length ? items.length : 0));
|
let itemsLength = $derived(items.length || 1);
|
||||||
let itemsBuffer = $derived(items.filter((i) => i.changed));
|
let itemsBuffer = $derived(items.filter((i) => i.changed));
|
||||||
const functions = {
|
const functions = {
|
||||||
getPage: async () => {
|
async getPage() {
|
||||||
functions.save();
|
this.save();
|
||||||
if (pager.idFrom > pager.idTo) {
|
if (pager.idFrom > pager.idTo) {
|
||||||
[pager.idFrom, pager.idTo] = [pager.idTo, pager.idFrom];
|
[pager.idFrom, pager.idTo] = [pager.idTo, pager.idFrom];
|
||||||
}
|
}
|
||||||
if (pager.idTo - pager.idFrom > 300) {
|
if (pager.idTo - pager.idFrom > 300) {
|
||||||
pager.idTo = pager.idFrom + 300;
|
pager.idTo = pager.idFrom + 300;
|
||||||
}
|
}
|
||||||
const res = await fetch(`/api/drawing/${prefix.prefix}/${pager.idFrom}/${pager.idTo}`);
|
const res = await fetch(`/api/drawing/${prefix.prefix}/${pager.idFrom}/${pager.idTo}`, {
|
||||||
|
headers: { 'TAM-CLIENT-ID': tamClientID }
|
||||||
|
});
|
||||||
const resData = await res.json();
|
const resData = await res.json();
|
||||||
resData.map((i) => (i.changed = false));
|
resData.map((i) => (i.changed = false));
|
||||||
items = [...resData];
|
items = [...resData];
|
||||||
setTimeout(() => focusIdx(0));
|
setTimeout(() => focusIdx(0));
|
||||||
},
|
},
|
||||||
pagerFromUpdate(){
|
async save() {
|
||||||
if (itemsLength === 0) {
|
|
||||||
pager.idTo = pager.idFrom;
|
|
||||||
} else {
|
|
||||||
pager.idTo = pager.idFrom + (itemsLength - 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
save: async () => {
|
|
||||||
if (itemsBuffer.length > 0) {
|
if (itemsBuffer.length > 0) {
|
||||||
const res = await fetch('/api/drawing', {
|
const res = await fetch('/api/drawing', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json', 'TAM-CLIENT-ID': tamClientID },
|
||||||
body: JSON.stringify(itemsBuffer)
|
body: JSON.stringify(itemsBuffer)
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@@ -68,15 +63,24 @@
|
|||||||
focusIdx(0);
|
focusIdx(0);
|
||||||
}, 1);
|
}, 1);
|
||||||
},
|
},
|
||||||
prevPage: () => {
|
cancel() {
|
||||||
|
if (itemsBuffer.length > 0) {
|
||||||
|
itemsBuffer.forEach((i) => (i.changed = false));
|
||||||
|
this.getPage();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pagerFromUpdate() {
|
||||||
|
pager.idTo = pager.idFrom + (itemsLength - 1);
|
||||||
|
},
|
||||||
|
prevPage() {
|
||||||
((pager.idFrom -= itemsLength), (pager.idTo -= itemsLength));
|
((pager.idFrom -= itemsLength), (pager.idTo -= itemsLength));
|
||||||
functions.getPage();
|
this.getPage();
|
||||||
},
|
},
|
||||||
nextPage: () => {
|
nextPage() {
|
||||||
((pager.idFrom += itemsLength), (pager.idTo += itemsLength));
|
((pager.idFrom += itemsLength), (pager.idTo += itemsLength));
|
||||||
functions.getPage();
|
this.getPage();
|
||||||
},
|
},
|
||||||
nextLine: () => {
|
nextLine() {
|
||||||
if (items[nextIdx]) {
|
if (items[nextIdx]) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
focusIdx(nextIdx);
|
focusIdx(nextIdx);
|
||||||
@@ -87,7 +91,7 @@
|
|||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
prevLine: () => {
|
prevLine() {
|
||||||
if (curIdx > 0) {
|
if (curIdx > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
focusIdx(prevIdx);
|
focusIdx(prevIdx);
|
||||||
@@ -98,27 +102,27 @@
|
|||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dupDown: () => {
|
dupDown() {
|
||||||
if (items[nextIdx]) {
|
if (items[nextIdx]) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 'b_id'].forEach((key) => delete buffer[key]);
|
['prefix', 'b_id'].forEach((key) => delete buffer[key]);
|
||||||
items[nextIdx] = { ...items[nextIdx], ...buffer, changed: true };
|
items[nextIdx] = { ...items[nextIdx], ...buffer, changed: true };
|
||||||
functions.nextLine();
|
this.nextLine();
|
||||||
} else {
|
} else {
|
||||||
focusIdx(curIdx);
|
focusIdx(curIdx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dupUp: () => {
|
dupUp() {
|
||||||
if (curIdx > 0) {
|
if (curIdx > 0) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 'b_id'].forEach((key) => delete buffer[key]);
|
['prefix', 'b_id'].forEach((key) => delete buffer[key]);
|
||||||
items[prevIdx] = { ...items[prevIdx], ...buffer, changed: true };
|
items[prevIdx] = { ...items[prevIdx], ...buffer, changed: true };
|
||||||
functions.prevLine();
|
this.prevLine();
|
||||||
} else {
|
} else {
|
||||||
focusIdx(curIdx);
|
focusIdx(curIdx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
copy: () => {
|
copy() {
|
||||||
if (items[curIdx]) {
|
if (items[curIdx]) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 'b_id'].forEach((key) => delete buffer[key]);
|
['prefix', 'b_id'].forEach((key) => delete buffer[key]);
|
||||||
@@ -126,7 +130,7 @@
|
|||||||
}
|
}
|
||||||
focusIdx(curIdx);
|
focusIdx(curIdx);
|
||||||
},
|
},
|
||||||
paste: () => {
|
paste() {
|
||||||
if (items[curIdx]) {
|
if (items[curIdx]) {
|
||||||
const buffer = JSON.parse(window.localStorage.getItem('tam-drawing'));
|
const buffer = JSON.parse(window.localStorage.getItem('tam-drawing'));
|
||||||
items[curIdx] = { ...items[curIdx], ...buffer, changed: true };
|
items[curIdx] = { ...items[curIdx], ...buffer, changed: true };
|
||||||
@@ -137,8 +141,8 @@
|
|||||||
const headers = ['Basket ID', 'Description', 'Winning Ticket', 'Winner', 'Save?'];
|
const headers = ['Basket ID', 'Description', 'Winning Ticket', 'Winner', 'Save?'];
|
||||||
|
|
||||||
if (browser) {
|
if (browser) {
|
||||||
window.addEventListener('beforeunload', (e) => {
|
window.addEventListener('beforeunload', () => {
|
||||||
if (itemsBuffer.length > 0) e.preventDefault();
|
if (itemsBuffer.length > 0) functions.save();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -187,9 +191,11 @@
|
|||||||
type="number"
|
type="number"
|
||||||
class="{iS.normal} w-full"
|
class="{iS.normal} w-full"
|
||||||
id="{idx}_first"
|
id="{idx}_first"
|
||||||
onchangecapture={async () => {
|
oninput={async () => {
|
||||||
item.changed = true;
|
item.changed = true;
|
||||||
const res = await fetch(`/api/tickets/${prefix.prefix}/${item.winning_ticket}`);
|
const res = await fetch(`/api/tickets/${prefix.prefix}/${item.winning_ticket}`, {
|
||||||
|
headers: { 'TAM-CLIENT-ID': tamClientID }
|
||||||
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
[item.last_name, item.first_name, item.phone_number] = [
|
[item.last_name, item.first_name, item.phone_number] = [
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import { tS, bS } from '$lib/client/styles';
|
import { tS, bS } from '$lib/client/styles';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
let prefixes = $derived(data.prefixes);
|
let { prefixes, tamClientID } = $derived(data);
|
||||||
let tableData = $state([]);
|
let tableData = $state([]);
|
||||||
let currentTimeout = $state();
|
let currentTimeout = $state();
|
||||||
let lastRefreshed = $state('');
|
let lastRefreshed = $state('');
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
const loadCounts = async () => {
|
const loadCounts = async () => {
|
||||||
const rtnData = {};
|
const rtnData = {};
|
||||||
const res = await fetch('/api/reports/counts');
|
const res = await fetch('/api/reports/counts', {headers: {'TAM-CLIENT-ID': tamClientID}});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
prefixes.forEach((p) => (rtnData[p.prefix] = { ...p }));
|
prefixes.forEach((p) => (rtnData[p.prefix] = { ...p }));
|
||||||
const resData = await res.json();
|
const resData = await res.json();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import TicketSearchBar from '$lib/client/components/TicketSearchBar.svelte';
|
import TicketSearchBar from '$lib/client/components/TicketSearchBar.svelte';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
let { prefix, prefixes } = $derived(data);
|
let { prefix, prefixes, tamClientID } = $derived(data);
|
||||||
|
|
||||||
let pageTitle = 'Ticket Search | TAM';
|
let pageTitle = 'Ticket Search | TAM';
|
||||||
|
|
||||||
@@ -34,20 +34,22 @@
|
|||||||
let items = $state([]);
|
let items = $state([]);
|
||||||
let itemsBuffer = $derived(items.filter((i) => i.changed));
|
let itemsBuffer = $derived(items.filter((i) => i.changed));
|
||||||
const functions = {
|
const functions = {
|
||||||
search: async () => {
|
async search() {
|
||||||
const searchParams = new URLSearchParams({ ...searchForm });
|
const searchParams = new URLSearchParams({ ...searchForm });
|
||||||
const res = await fetch(`/api/search/tickets?${searchParams.toString()}`);
|
const res = await fetch(`/api/search/tickets?${searchParams.toString()}`, {
|
||||||
|
headers: { 'TAM-CLIENT-ID': tamClientID }
|
||||||
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const resData = await res.json();
|
const resData = await res.json();
|
||||||
items = [...resData];
|
items = [...resData];
|
||||||
setTimeout(() => focusIdx(0), 1);
|
setTimeout(() => focusIdx(0), 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
save: async () => {
|
async save() {
|
||||||
if (itemsBuffer.length > 0) {
|
if (itemsBuffer.length > 0) {
|
||||||
const res = await fetch('/api/search/tickets', {
|
const res = await fetch('/api/search/tickets', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json', 'TAM-CLIENT-ID': tamClientID },
|
||||||
body: JSON.stringify(itemsBuffer)
|
body: JSON.stringify(itemsBuffer)
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@@ -60,7 +62,7 @@
|
|||||||
focusIdx(0);
|
focusIdx(0);
|
||||||
}, 1);
|
}, 1);
|
||||||
},
|
},
|
||||||
nextLine: () => {
|
nextLine() {
|
||||||
if (items[nextIdx]) {
|
if (items[nextIdx]) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
focusIdx(nextIdx);
|
focusIdx(nextIdx);
|
||||||
@@ -71,7 +73,7 @@
|
|||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
prevLine: () => {
|
prevLine() {
|
||||||
if (curIdx > 0) {
|
if (curIdx > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
focusIdx(prevIdx);
|
focusIdx(prevIdx);
|
||||||
@@ -82,27 +84,27 @@
|
|||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dupDown: () => {
|
dupDown() {
|
||||||
if (items[nextIdx]) {
|
if (items[nextIdx]) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
||||||
items[nextIdx] = { ...items[nextIdx], ...buffer, changed: true };
|
items[nextIdx] = { ...items[nextIdx], ...buffer, changed: true };
|
||||||
functions.nextLine();
|
this.nextLine();
|
||||||
} else {
|
} else {
|
||||||
focusIdx(curIdx);
|
focusIdx(curIdx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dupUp: () => {
|
dupUp() {
|
||||||
if (curIdx > 0) {
|
if (curIdx > 0) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
||||||
items[prevIdx] = { ...items[prevIdx], ...buffer, changed: true };
|
items[prevIdx] = { ...items[prevIdx], ...buffer, changed: true };
|
||||||
functions.prevLine();
|
this.prevLine();
|
||||||
} else {
|
} else {
|
||||||
focusIdx(curIdx);
|
focusIdx(curIdx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
copy: () => {
|
copy() {
|
||||||
if (items[curIdx]) {
|
if (items[curIdx]) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
||||||
@@ -110,7 +112,7 @@
|
|||||||
}
|
}
|
||||||
setTimeout(() => focusIdx(curIdx), 1);
|
setTimeout(() => focusIdx(curIdx), 1);
|
||||||
},
|
},
|
||||||
paste: () => {
|
paste() {
|
||||||
if (items[curIdx]) {
|
if (items[curIdx]) {
|
||||||
const buffer = JSON.parse(window.localStorage.getItem('tam-ticket'));
|
const buffer = JSON.parse(window.localStorage.getItem('tam-ticket'));
|
||||||
items[curIdx] = { ...items[curIdx], ...buffer, changed: true };
|
items[curIdx] = { ...items[curIdx], ...buffer, changed: true };
|
||||||
@@ -171,7 +173,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
class="{iS.normal} w-full"
|
class="{iS.normal} w-full"
|
||||||
id="{idx}_first"
|
id="{idx}_first"
|
||||||
onchangecapture={() => (item.changed = true)}
|
oninput={() => (item.changed = true)}
|
||||||
bind:value={item.first_name}
|
bind:value={item.first_name}
|
||||||
/></td
|
/></td
|
||||||
>
|
>
|
||||||
@@ -180,7 +182,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
class="{iS.normal} w-full"
|
class="{iS.normal} w-full"
|
||||||
id="{idx}_second"
|
id="{idx}_second"
|
||||||
onchangecapture={() => (item.changed = true)}
|
oninput={() => (item.changed = true)}
|
||||||
bind:value={item.last_name}
|
bind:value={item.last_name}
|
||||||
/></td
|
/></td
|
||||||
>
|
>
|
||||||
@@ -189,7 +191,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
class="{iS.normal} w-full"
|
class="{iS.normal} w-full"
|
||||||
id="{idx}_third"
|
id="{idx}_third"
|
||||||
onchangecapture={() => (item.changed = true)}
|
oninput={() => (item.changed = true)}
|
||||||
bind:value={item.phone_number}
|
bind:value={item.phone_number}
|
||||||
/></td
|
/></td
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import HeaderBar from '$lib/client/components/HeaderBar.svelte';
|
import HeaderBar from '$lib/client/components/HeaderBar.svelte';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
let { tamClientID } = $derived(data);
|
||||||
let settings = $state({});
|
let settings = $state({});
|
||||||
let status = $state({
|
let status = $state({
|
||||||
message: '',
|
message: '',
|
||||||
@@ -73,6 +74,17 @@
|
|||||||
<div>Venue Name:</div>
|
<div>Venue Name:</div>
|
||||||
<input type="text" id="venue_name" class={iS.normal} bind:value={settings.venue_name} />
|
<input type="text" id="venue_name" class={iS.normal} bind:value={settings.venue_name} />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-row gap-1 items-center">
|
||||||
|
<div>Disable Attribution:</div>
|
||||||
|
<button
|
||||||
|
class={bS.gray}
|
||||||
|
onclick={() => {
|
||||||
|
settings.disable_attrib
|
||||||
|
? (settings.disable_attrib = false)
|
||||||
|
: (settings.disable_attrib = true);
|
||||||
|
}}>{settings.disable_attrib ? 'Yes' : 'No'}</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<div class="flex flex-row gap-1 items-center">
|
<div class="flex flex-row gap-1 items-center">
|
||||||
<button
|
<button
|
||||||
class={bS.gray}
|
class={bS.gray}
|
||||||
@@ -80,7 +92,7 @@
|
|||||||
const res = await fetch('/api/settings', {
|
const res = await fetch('/api/settings', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(settings),
|
body: JSON.stringify(settings),
|
||||||
headers: { 'Content-Type': 'application/json' }
|
headers: { 'Content-Type': 'application/json', 'TAM-CLIENT-ID': tamClientID }
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
status.message = `Error Code: ${res.status}`;
|
status.message = `Error Code: ${res.status}`;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<script>
|
<script>
|
||||||
import HeaderBar from '$lib/client/components/HeaderBar.svelte';
|
import HeaderBar from '$lib/client/components/HeaderBar.svelte';
|
||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import { bS, iS } from '$lib/client/styles';
|
import { bS, iS, tS } from '$lib/client/styles';
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
let { tamClientID } = $derived(data);
|
||||||
|
|
||||||
const pageTitle = 'Auth Keys | TAM';
|
const pageTitle = 'Auth Keys | TAM';
|
||||||
|
|
||||||
@@ -14,10 +15,11 @@
|
|||||||
let authKeys = $state([]);
|
let authKeys = $state([]);
|
||||||
let newDesc = $state('');
|
let newDesc = $state('');
|
||||||
let curKey = $state('');
|
let curKey = $state('');
|
||||||
|
let errorStatus = $state('');
|
||||||
|
|
||||||
async function getKeys() {
|
async function getKeys() {
|
||||||
const res = await fetch('/api/auth', {
|
const res = await fetch('/api/auth', {
|
||||||
headers: { 'TAM-PWD': auth.pwd }
|
headers: { 'TAM-PWD': auth.pwd, 'TAM-CLIENT-ID': tamClientID }
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
auth.toggle = true;
|
auth.toggle = true;
|
||||||
@@ -25,8 +27,8 @@
|
|||||||
authKeys = [...resData];
|
authKeys = [...resData];
|
||||||
curKey = data.authKey;
|
curKey = data.authKey;
|
||||||
} else {
|
} else {
|
||||||
auth.pwd = 'Invalid Password!';
|
errorStatus = 'Invalid Password!';
|
||||||
setTimeout(() => (auth.pwd = ''), 3000);
|
setTimeout(() => (errorStatus = ''), 7000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -45,9 +47,22 @@
|
|||||||
{#if !auth.toggle}
|
{#if !auth.toggle}
|
||||||
<div class="flex flex-row gap-1 items-center py-1">
|
<div class="flex flex-row gap-1 items-center py-1">
|
||||||
<div>Password:</div>
|
<div>Password:</div>
|
||||||
<input type="password" id="pwd_input" class={iS.normal} bind:value={auth.pwd} />
|
<input
|
||||||
|
type="password"
|
||||||
|
id="pwd_input"
|
||||||
|
class={iS.normal}
|
||||||
|
onkeydown={(event) => {
|
||||||
|
if (event.key == 'Enter') {
|
||||||
|
getKeys();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
bind:value={auth.pwd}
|
||||||
|
/>
|
||||||
<button class={bS.gray} onclick={getKeys}>Login</button>
|
<button class={bS.gray} onclick={getKeys}>Login</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="py-1">
|
||||||
|
<p class={tS.red}>{errorStatus}</p>
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<table class="w-full my-1">
|
<table class="w-full my-1">
|
||||||
<thead class="text-left">
|
<thead class="text-left">
|
||||||
@@ -72,7 +87,10 @@
|
|||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
const res = await fetch('/api/settings', {
|
const res = await fetch('/api/settings', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'TAM-CLIENT-ID': tamClientID
|
||||||
|
},
|
||||||
body: JSON.stringify({ remote_key: key.auth_key })
|
body: JSON.stringify({ remote_key: key.auth_key })
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@@ -86,7 +104,7 @@
|
|||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
const res = await fetch(`/api/auth?key_to_del=${key.auth_key}`, {
|
const res = await fetch(`/api/auth?key_to_del=${key.auth_key}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: { 'TAM-PWD': auth.pwd }
|
headers: { 'TAM-PWD': auth.pwd, 'TAM-CLIENT-ID': tamClientID }
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -112,7 +130,11 @@
|
|||||||
if (newDesc) {
|
if (newDesc) {
|
||||||
const res = await fetch('/api/auth', {
|
const res = await fetch('/api/auth', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'TAM-PWD': auth.pwd, 'Content-Type': 'application/json' },
|
headers: {
|
||||||
|
'TAM-PWD': auth.pwd,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'TAM-CLIENT-ID': tamClientID
|
||||||
|
},
|
||||||
body: JSON.stringify({ description: newDesc })
|
body: JSON.stringify({ description: newDesc })
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
const pageTitle = 'Backup and Restore | TAM';
|
const pageTitle = 'Backup and Restore | TAM';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
let remoteServer = $derived(data.remoteServer);
|
let { remoteServer, tamClientID } = $derived(data);
|
||||||
let uploadFile = $state();
|
let uploadFile = $state();
|
||||||
let contents = $state('');
|
let contents = $state('');
|
||||||
let results = $state('');
|
let results = $state('');
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
hour: String(now.getHours()).padStart(2, '0'),
|
hour: String(now.getHours()).padStart(2, '0'),
|
||||||
minutes: String(now.getMinutes()).padStart(2, '0')
|
minutes: String(now.getMinutes()).padStart(2, '0')
|
||||||
};
|
};
|
||||||
const res = await fetch(fetch_url);
|
const res = await fetch(fetch_url, { headers: { 'TAM-CLIENT-ID': tamClientID } });
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const jsonString = JSON.stringify(data, null, 2);
|
const jsonString = JSON.stringify(data, null, 2);
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
const res = await fetch(`/api/backuprestore/${target}`, {
|
const res = await fetch(`/api/backuprestore/${target}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json', 'TAM-CLIENT-ID': tamClientID },
|
||||||
body: contents
|
body: contents
|
||||||
});
|
});
|
||||||
if (res.ok) setResult('File uploaded successfully. Check to see if your data exists.');
|
if (res.ok) setResult('File uploaded successfully. Check to see if your data exists.');
|
||||||
@@ -67,7 +67,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function pushData(target) {
|
async function pushData(target) {
|
||||||
const res = await fetch(`/api/backuprestore/push/${target}`, { method: 'HEAD' });
|
const res = await fetch(`/api/backuprestore/push/${target}`, {
|
||||||
|
method: 'HEAD',
|
||||||
|
headers: { 'TAM-CLIENT-ID': tamClientID }
|
||||||
|
});
|
||||||
const targetStr = target.charAt(0).toUpperCase() + target.slice(1);
|
const targetStr = target.charAt(0).toUpperCase() + target.slice(1);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setResult(`${targetStr} pushed successfully.`);
|
setResult(`${targetStr} pushed successfully.`);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
let { tamClientID } = $derived(data);
|
||||||
|
|
||||||
const pageTitle = 'Prefixes | TAM';
|
const pageTitle = 'Prefixes | TAM';
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
if (editPrefix.prefix) {
|
if (editPrefix.prefix) {
|
||||||
const req = await fetch('/api/prefixes', {
|
const req = await fetch('/api/prefixes', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json', 'TAM-CLIENT-ID': tamClientID },
|
||||||
body: JSON.stringify([editPrefix])
|
body: JSON.stringify([editPrefix])
|
||||||
});
|
});
|
||||||
if (req.ok) window.location.reload();
|
if (req.ok) window.location.reload();
|
||||||
@@ -125,7 +126,8 @@
|
|||||||
class={bS[prefix.color]}
|
class={bS[prefix.color]}
|
||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
const res = await fetch(`/api/prefixes?p=${prefix.prefix}`, {
|
const res = await fetch(`/api/prefixes?p=${prefix.prefix}`, {
|
||||||
method: 'DELETE'
|
method: 'DELETE',
|
||||||
|
headers: { 'TAM-CLIENT-ID': tamClientID }
|
||||||
});
|
});
|
||||||
if (res.ok) window.location.reload();
|
if (res.ok) window.location.reload();
|
||||||
}}>Delete</button
|
}}>Delete</button
|
||||||
|
|||||||
@@ -26,9 +26,12 @@
|
|||||||
<thead class="sticky top-0">
|
<thead class="sticky top-0">
|
||||||
<tr class="print:hidden">
|
<tr class="print:hidden">
|
||||||
<td colspan="50">
|
<td colspan="50">
|
||||||
<HeaderBar>
|
<HeaderBar />
|
||||||
<div>{pageTitle}</div>
|
</td>
|
||||||
</HeaderBar>
|
</tr>
|
||||||
|
<tr class="print:hidden">
|
||||||
|
<td colspan="50">
|
||||||
|
<h1 class="text-lg font-bold">{pageTitle}</h1>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="print:hidden">
|
<tr class="print:hidden">
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import CommandBar from '$lib/client/components/CommandBar.svelte';
|
import CommandBar from '$lib/client/components/CommandBar.svelte';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
let { prefix, prefixes } = $derived(data);
|
let { prefix, prefixes, tamClientID } = $derived(data);
|
||||||
|
|
||||||
let pageTitle = $derived(`${prefix.prefix} Tickets | TAM`);
|
let pageTitle = $derived(`${prefix.prefix} Tickets | TAM`);
|
||||||
|
|
||||||
@@ -27,35 +27,31 @@
|
|||||||
|
|
||||||
let pager = $state({ idFrom: 0, idTo: 0 });
|
let pager = $state({ idFrom: 0, idTo: 0 });
|
||||||
let items = $state([]);
|
let items = $state([]);
|
||||||
let itemsLength = $derived.by(() => (items.length ? items.length : 0));
|
let itemsLength = $derived(items.length || 1);
|
||||||
let itemsBuffer = $derived(items.filter((i) => i.changed));
|
let itemsBuffer = $derived(items.filter((i) => i.changed));
|
||||||
const functions = {
|
const functions = {
|
||||||
getPage: async () => {
|
async getPage(){
|
||||||
functions.save();
|
this.save();
|
||||||
if (pager.idFrom > pager.idTo) {
|
if (pager.idFrom > pager.idTo) {
|
||||||
[pager.idFrom, pager.idTo] = [pager.idTo, pager.idFrom];
|
[pager.idFrom, pager.idTo] = [pager.idTo, pager.idFrom];
|
||||||
}
|
}
|
||||||
if (pager.idTo - pager.idFrom > 300) {
|
if (pager.idTo - pager.idFrom > 300) {
|
||||||
pager.idTo = pager.idFrom + 300;
|
pager.idTo = pager.idFrom + 300;
|
||||||
}
|
}
|
||||||
const res = await fetch(`/api/tickets/${prefix.prefix}/${pager.idFrom}/${pager.idTo}`);
|
const res = await fetch(`/api/tickets/${prefix.prefix}/${pager.idFrom}/${pager.idTo}`, {headers: {'TAM-CLIENT-ID': tamClientID}});
|
||||||
const resData = await res.json();
|
const resData = await res.json();
|
||||||
resData.map((i) => (i.changed = false));
|
resData.map((i) => (i.changed = false));
|
||||||
items = [...resData];
|
items = [...resData];
|
||||||
setTimeout(() => focusIdx(0));
|
setTimeout(() => focusIdx(0));
|
||||||
},
|
},
|
||||||
pagerFromUpdate(){
|
pagerFromUpdate(){
|
||||||
if (itemsLength === 0) {
|
pager.idTo = pager.idFrom + (itemsLength - 1);
|
||||||
pager.idTo = pager.idFrom;
|
|
||||||
} else {
|
|
||||||
pager.idTo = pager.idFrom + (itemsLength - 1);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
save: async () => {
|
async save(){
|
||||||
if (itemsBuffer.length > 0) {
|
if (itemsBuffer.length > 0) {
|
||||||
const res = await fetch('/api/tickets', {
|
const res = await fetch('/api/tickets', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json', 'TAM-CLIENT-ID': tamClientID },
|
||||||
body: JSON.stringify(itemsBuffer)
|
body: JSON.stringify(itemsBuffer)
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@@ -68,15 +64,21 @@
|
|||||||
focusIdx(0);
|
focusIdx(0);
|
||||||
}, 1);
|
}, 1);
|
||||||
},
|
},
|
||||||
prevPage: () => {
|
cancel(){
|
||||||
|
if (itemsBuffer.length > 0) {
|
||||||
|
itemsBuffer.forEach((i) => (i.changed = false));
|
||||||
|
this.getPage();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
prevPage(){
|
||||||
((pager.idFrom -= itemsLength), (pager.idTo -= itemsLength));
|
((pager.idFrom -= itemsLength), (pager.idTo -= itemsLength));
|
||||||
functions.getPage();
|
this.getPage();
|
||||||
},
|
},
|
||||||
nextPage: () => {
|
nextPage(){
|
||||||
((pager.idFrom += itemsLength), (pager.idTo += itemsLength));
|
((pager.idFrom += itemsLength), (pager.idTo += itemsLength));
|
||||||
functions.getPage();
|
this.getPage();
|
||||||
},
|
},
|
||||||
nextLine: () => {
|
nextLine(){
|
||||||
if (items[nextIdx]) {
|
if (items[nextIdx]) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
focusIdx(nextIdx);
|
focusIdx(nextIdx);
|
||||||
@@ -87,7 +89,7 @@
|
|||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
prevLine: () => {
|
prevLine(){
|
||||||
if (curIdx > 0) {
|
if (curIdx > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
focusIdx(prevIdx);
|
focusIdx(prevIdx);
|
||||||
@@ -98,17 +100,17 @@
|
|||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dupDown: () => {
|
dupDown(){
|
||||||
if (items[nextIdx]) {
|
if (items[nextIdx]) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
||||||
items[nextIdx] = { ...items[nextIdx], ...buffer, changed: true };
|
items[nextIdx] = { ...items[nextIdx], ...buffer, changed: true };
|
||||||
functions.nextLine();
|
this.nextLine();
|
||||||
} else {
|
} else {
|
||||||
focusIdx(curIdx);
|
focusIdx(curIdx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dupUp: () => {
|
dupUp(){
|
||||||
if (curIdx > 0) {
|
if (curIdx > 0) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
||||||
@@ -118,7 +120,7 @@
|
|||||||
focusIdx(curIdx);
|
focusIdx(curIdx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
copy: () => {
|
copy(){
|
||||||
if (items[curIdx]) {
|
if (items[curIdx]) {
|
||||||
const buffer = { ...items[curIdx] };
|
const buffer = { ...items[curIdx] };
|
||||||
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
['prefix', 't_id'].forEach((key) => delete buffer[key]);
|
||||||
@@ -126,7 +128,7 @@
|
|||||||
}
|
}
|
||||||
setTimeout(() => focusIdx(curIdx), 1);
|
setTimeout(() => focusIdx(curIdx), 1);
|
||||||
},
|
},
|
||||||
paste: () => {
|
paste(){
|
||||||
if (items[curIdx]) {
|
if (items[curIdx]) {
|
||||||
const buffer = JSON.parse(window.localStorage.getItem('tam-ticket'));
|
const buffer = JSON.parse(window.localStorage.getItem('tam-ticket'));
|
||||||
items[curIdx] = { ...items[curIdx], ...buffer, changed: true };
|
items[curIdx] = { ...items[curIdx], ...buffer, changed: true };
|
||||||
@@ -137,8 +139,8 @@
|
|||||||
const headers = ['Ticket ID', 'First Name', 'Last Name', 'Phone Number', 'Pref', 'Save?'];
|
const headers = ['Ticket ID', 'First Name', 'Last Name', 'Phone Number', 'Pref', 'Save?'];
|
||||||
|
|
||||||
if (browser) {
|
if (browser) {
|
||||||
window.addEventListener('beforeunload', (e) => {
|
window.addEventListener('beforeunload', () => {
|
||||||
if (itemsBuffer.length > 0) e.preventDefault();
|
if (itemsBuffer.length > 0) functions.save();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -186,7 +188,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
class="{iS.normal} w-full"
|
class="{iS.normal} w-full"
|
||||||
id="{idx}_first"
|
id="{idx}_first"
|
||||||
onchangecapture={() => (item.changed = true)}
|
oninput={() => (item.changed = true)}
|
||||||
bind:value={item.first_name}
|
bind:value={item.first_name}
|
||||||
/></td
|
/></td
|
||||||
>
|
>
|
||||||
@@ -195,7 +197,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
class="{iS.normal} w-full"
|
class="{iS.normal} w-full"
|
||||||
id="{idx}_second"
|
id="{idx}_second"
|
||||||
onchangecapture={() => (item.changed = true)}
|
oninput={() => (item.changed = true)}
|
||||||
bind:value={item.last_name}
|
bind:value={item.last_name}
|
||||||
/></td
|
/></td
|
||||||
>
|
>
|
||||||
@@ -204,7 +206,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
class="{iS.normal} w-full"
|
class="{iS.normal} w-full"
|
||||||
id="{idx}_third"
|
id="{idx}_third"
|
||||||
onchangecapture={() => (item.changed = true)}
|
oninput={() => (item.changed = true)}
|
||||||
bind:value={item.phone_number}
|
bind:value={item.phone_number}
|
||||||
/></td
|
/></td
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
source ../../version.env
|
source ../../version.env
|
||||||
docker compose build
|
docker compose build
|
||||||
docker image tag dbob16/tam-client:latest dbob16/tam-client:${TAM_VERSION}
|
docker image tag dbob16/tam-client:latest dbob16/tam-client:${TAM_VERSION}
|
||||||
mkdir -p dist/images
|
echo "HOST=127.0.0.1" > dist/prod.env
|
||||||
echo "HOST=127.0.0.1" >> dist/prod.env
|
|
||||||
echo "PORT=3000" >> dist/prod.env
|
echo "PORT=3000" >> dist/prod.env
|
||||||
TAM_VERSION=${TAM_VERSION} envsubst < ./run-client.sh.template > ./dist/run-client.sh
|
TAM_VERSION=${TAM_VERSION} envsubst < ./run-client.sh.template > ./dist/run-client.sh
|
||||||
chmod +x dist/run-client.sh
|
chmod +x dist/run-client.sh
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ ! -d "./data" ]; then
|
||||||
|
mkdir ./data
|
||||||
|
fi
|
||||||
|
NODE_TLS_REJECT_UNAUTHORIZED=0 BODY_SIZE_LIMIT=Infinity HOST=localhost PORT=3000 ./exe/bin/node ./build
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{ config, pkgs, ... }
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
networking.hosts = {
|
||||||
|
"192.168.1.1" = [ "tam.lan" ]; # Change the IP of this line to the IP of the TAM server if applicable.
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users."tam" = {
|
||||||
|
isNormalUser = true;
|
||||||
|
description = "Ticket Auction Manager";
|
||||||
|
};
|
||||||
|
|
||||||
|
services.displayManager.sddm.settings.Autologin = {
|
||||||
|
Session = "plasma.desktop";
|
||||||
|
User = "tam";
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
chromium
|
||||||
|
];
|
||||||
|
|
||||||
|
virtualisation.docker.enable = true;
|
||||||
|
|
||||||
|
virtualisation.oci-containers = {
|
||||||
|
backend = "docker";
|
||||||
|
containers."tam-client" = {
|
||||||
|
image = "dbob16/tam-client:0.1.0"; # Change image to local repo to avoid pull limits.
|
||||||
|
volumes = [
|
||||||
|
"tam-data:/data"
|
||||||
|
];
|
||||||
|
environment = {
|
||||||
|
HOST = "localhost";
|
||||||
|
PORT = "3000";
|
||||||
|
};
|
||||||
|
extraOptions = ["--network=host"]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
Reference in new issue
Block a user