Milestone - core reports working
This commit is contained in:
14
README.md
14
README.md
@@ -16,4 +16,16 @@ After cloning, cd'ing into webapp, and running `npm install` followed by `npm ru
|
|||||||
|
|
||||||
That is making prefixes.
|
That is making prefixes.
|
||||||
|
|
||||||
On the main menu press "Alt (or Option) + a" to toggle admin mode. Then click Prefix editor to open the form to edit prefixes.
|
On the main menu press "Alt (or Option) + a" to toggle admin mode. Then click Prefix editor to open the form to edit prefixes.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Ticket Form: **functional**
|
||||||
|
Basket Form: **functional**
|
||||||
|
Drawing Form: **functional**
|
||||||
|
|
||||||
|
By Name Report: **functional**
|
||||||
|
Basket ID Report: **functional**
|
||||||
|
Ticket Count Report: **not started**
|
||||||
|
|
||||||
|
Deployment System: **not started** | Likely will Dockerize it first, then will move to possible Electron or Tauri option for desktop application.
|
||||||
@@ -7,6 +7,7 @@ from routers.prefixes import prefix_router
|
|||||||
from routers.tickets import ticket_router
|
from routers.tickets import ticket_router
|
||||||
from routers.baskets import basket_router
|
from routers.baskets import basket_router
|
||||||
from routers.combined import combined_router
|
from routers.combined import combined_router
|
||||||
|
from routers.reports import report_router
|
||||||
|
|
||||||
if argv[1] == "run":
|
if argv[1] == "run":
|
||||||
app = FastAPI(title="TAM3 API Server", docs_url=None, redoc_url=None)
|
app = FastAPI(title="TAM3 API Server", docs_url=None, redoc_url=None)
|
||||||
@@ -16,4 +17,5 @@ else:
|
|||||||
app.include_router(prefix_router)
|
app.include_router(prefix_router)
|
||||||
app.include_router(ticket_router)
|
app.include_router(ticket_router)
|
||||||
app.include_router(basket_router)
|
app.include_router(basket_router)
|
||||||
app.include_router(combined_router)
|
app.include_router(combined_router)
|
||||||
|
app.include_router(report_router)
|
||||||
22
api/repos/reports.py
Normal file
22
api/repos/reports.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from .template import Repo
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ReportItem:
|
||||||
|
prefix: str
|
||||||
|
winner_name: str
|
||||||
|
phone_number: str | None
|
||||||
|
preference: str
|
||||||
|
b_id: int
|
||||||
|
winning_ticket: int
|
||||||
|
description: str
|
||||||
|
|
||||||
|
class ReportRepo(Repo):
|
||||||
|
def get_byname(self, prefix: str) -> list[ReportItem]:
|
||||||
|
self.cur.execute("SELECT * FROM report WHERE prefix = %s", (prefix,))
|
||||||
|
results = self.cur.fetchall()
|
||||||
|
return [ReportItem(*r) for r in results]
|
||||||
|
def get_bybasket(self, prefix: str) -> list[ReportItem]:
|
||||||
|
self.cur.execute("SELECT * FROM report WHERE prefix = %s ORDER BY b_id ASC", (prefix,))
|
||||||
|
results = self.cur.fetchall()
|
||||||
|
return [ReportItem(*r) for r in results]
|
||||||
18
api/routers/reports.py
Normal file
18
api/routers/reports.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
from repos.reports import ReportItem, ReportRepo
|
||||||
|
from repos.api_keys import ApiKeyRepo
|
||||||
|
from exceptions import bad_key
|
||||||
|
|
||||||
|
report_router = APIRouter(prefix="/api/reports")
|
||||||
|
|
||||||
|
@report_router.get("/byname/{prefix}/")
|
||||||
|
def get_report_byname(api_key: str, prefix: str):
|
||||||
|
if not ApiKeyRepo().check_api(api_key):
|
||||||
|
raise bad_key
|
||||||
|
return ReportRepo().get_byname(prefix)
|
||||||
|
|
||||||
|
@report_router.get("/bybasket/{prefix}/")
|
||||||
|
def get_report_bybasket(api_key: str, prefix: str):
|
||||||
|
if not ApiKeyRepo().check_api(api_key):
|
||||||
|
raise bad_key
|
||||||
|
return ReportRepo().get_bybasket(prefix)
|
||||||
@@ -12,19 +12,19 @@ CREATE TABLE IF NOT EXISTS prefixes (
|
|||||||
CREATE TABLE IF NOT EXISTS tickets (
|
CREATE TABLE IF NOT EXISTS tickets (
|
||||||
`prefix` VARCHAR(255),
|
`prefix` VARCHAR(255),
|
||||||
`t_id` INT,
|
`t_id` INT,
|
||||||
`first_name` VARCHAR(255),
|
`first_name` VARCHAR(255) DEFAULT "",
|
||||||
`last_name` VARCHAR(255),
|
`last_name` VARCHAR(255) DEFAULT "",
|
||||||
`phone_number` VARCHAR(255),
|
`phone_number` VARCHAR(255) DEFAULT "",
|
||||||
`preference` VARCHAR(20),
|
`preference` VARCHAR(20) DEFAULT "CALL",
|
||||||
PRIMARY KEY (`prefix`, `t_id`)
|
PRIMARY KEY (`prefix`, `t_id`)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS baskets (
|
CREATE TABLE IF NOT EXISTS baskets (
|
||||||
`prefix` VARCHAR(255),
|
`prefix` VARCHAR(255),
|
||||||
`b_id` INT,
|
`b_id` INT,
|
||||||
`description` VARCHAR(255),
|
`description` VARCHAR(255) DEFAULT "",
|
||||||
`donors` VARCHAR(255),
|
`donors` VARCHAR(255) DEFAULT "",
|
||||||
`winning_ticket` INT,
|
`winning_ticket` INT DEFAULT 0,
|
||||||
PRIMARY KEY (`prefix`, `b_id`)
|
PRIMARY KEY (`prefix`, `b_id`)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
27
testdata/tam3_name.py
vendored
Normal file
27
testdata/tam3_name.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from sys import argv
|
||||||
|
from names import get_full_name
|
||||||
|
import random as r
|
||||||
|
|
||||||
|
def gen_phone_number():
|
||||||
|
def d0():
|
||||||
|
return r.randint(0, 1)
|
||||||
|
def d():
|
||||||
|
return r.randint(0, 9)
|
||||||
|
return f"{d0()}{d()}{d()}-{d()}{d()}{d()}-{d()}{d()}{d()}{d()}"
|
||||||
|
|
||||||
|
class Person:
|
||||||
|
def __init__(self):
|
||||||
|
self.name = get_full_name()
|
||||||
|
self.phone_number = gen_phone_number()
|
||||||
|
self.preference = r.choice(("TEXT", "TEXT", "TEXT", "CALL"))
|
||||||
|
def __repr__(self):
|
||||||
|
return f"{self.name} {self.phone_number} {self.preference}"
|
||||||
|
|
||||||
|
how_many = input("Insert how many random people to generate: ")
|
||||||
|
try:
|
||||||
|
how_many = int(how_many)
|
||||||
|
except:
|
||||||
|
print("Try entering an integer next time.")
|
||||||
|
|
||||||
|
for i in range(0, int(how_many)):
|
||||||
|
print(Person())
|
||||||
20
testdata/tam3_ticket_num.py
vendored
Normal file
20
testdata/tam3_ticket_num.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import random as r
|
||||||
|
|
||||||
|
how_many = input("Enter how many numbers you want to generate: ")
|
||||||
|
try:
|
||||||
|
how_many = int(how_many)
|
||||||
|
except:
|
||||||
|
print("Enter an integer next time.")
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
ticket_range = input("Enter the start and end numbers of the random range you want to use, separated by a hyphen: ")
|
||||||
|
|
||||||
|
ticket_range = ticket_range.split("-")
|
||||||
|
try:
|
||||||
|
ticket_range = [int(i) for i in ticket_range]
|
||||||
|
except:
|
||||||
|
print("Invalid range entered. Needs to be something like \"1-20\"")
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
for i in range(0, how_many):
|
||||||
|
print(r.randint(ticket_range[0], ticket_range[1]))
|
||||||
@@ -10,10 +10,10 @@ export const prefixes = sqliteTable('prefixes', {
|
|||||||
export const tickets = sqliteTable('tickets', {
|
export const tickets = sqliteTable('tickets', {
|
||||||
prefix: text('prefix'),
|
prefix: text('prefix'),
|
||||||
t_id: integer('t_id'),
|
t_id: integer('t_id'),
|
||||||
first_name: text('first_name'),
|
first_name: text('first_name').default(''),
|
||||||
last_name: text('last_name'),
|
last_name: text('last_name').default(''),
|
||||||
phone_number: text('phone_number'),
|
phone_number: text('phone_number').default(''),
|
||||||
preference: text('preference')
|
preference: text('preference').default('CALL')
|
||||||
}, (table) => [
|
}, (table) => [
|
||||||
primaryKey({columns: [table.prefix, table.t_id]})
|
primaryKey({columns: [table.prefix, table.t_id]})
|
||||||
]);
|
]);
|
||||||
@@ -21,8 +21,8 @@ export const tickets = sqliteTable('tickets', {
|
|||||||
export const baskets = sqliteTable('baskets', {
|
export const baskets = sqliteTable('baskets', {
|
||||||
prefix: text('prefix'),
|
prefix: text('prefix'),
|
||||||
b_id: integer('b_id'),
|
b_id: integer('b_id'),
|
||||||
description: text('description'),
|
description: text('description').default(''),
|
||||||
donors: text('donors'),
|
donors: text('donors').default(''),
|
||||||
winning_ticket: integer('winning_ticket').default(0)
|
winning_ticket: integer('winning_ticket').default(0)
|
||||||
}, (table) => [
|
}, (table) => [
|
||||||
primaryKey({columns: [table.prefix, table.b_id]})
|
primaryKey({columns: [table.prefix, table.b_id]})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export async function load({ fetch }) {
|
export async function load({ fetch }) {
|
||||||
const res = await fetch('/api/prefixes/');
|
const res = await fetch('/api/prefixes');
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
return { prefixes: data, status: "Prefixes fetched successfully." }
|
return { prefixes: data, status: "Prefixes fetched successfully." }
|
||||||
|
|||||||
@@ -47,8 +47,8 @@
|
|||||||
</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="/report/byname/{current_prefix.name}/" target="_blank" class="styled">By Name</a>
|
<a href="/reports/byname/{current_prefix.name}/" target="_blank" class="styled">By Name</a>
|
||||||
<a href="/report/bybasket/{current_prefix.name}/" target="_blank" class="styled">By Basket ID</a>
|
<a href="/reports/bybasket/{current_prefix.name}/" target="_blank" class="styled">By Basket ID</a>
|
||||||
</div>
|
</div>
|
||||||
{#if admin_mode}
|
{#if admin_mode}
|
||||||
<div><h2>Admin Mode:</h2></div>
|
<div><h2>Admin Mode:</h2></div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { eq, and } from "drizzle-orm";
|
|||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
let n_b_from = parseInt(params.b_from), n_b_to = parseInt(params.b_to);
|
let n_b_from = parseInt(params.b_from), n_b_to = parseInt(params.b_to);
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/baskets/${n_b_from}/${n_b_to}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/baskets/${params.prefix}/${n_b_from}/${n_b_to}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
return new Response(JSON.stringify({detail: "Unable to fetch baskets"}), {status: res.status, statusText: res.statusText});
|
return new Response(JSON.stringify({detail: "Unable to fetch baskets"}), {status: res.status, statusText: res.statusText});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { env } from "$env/dynamic/private";
|
|||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
let { name } = params;
|
let { name } = params;
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/prefixes/${name}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/prefixes/${name}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
return new Response(JSON.stringify({status: "Issue getting prefix."}), {status: res.status, statusText: res.statusText});
|
return new Response(JSON.stringify({status: "Issue getting prefix."}), {status: res.status, statusText: res.statusText});
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ export async function DELETE({ params }) {
|
|||||||
let { name } = params;
|
let { name } = params;
|
||||||
await db.delete(prefixes).where(eq(prefixes.name, name))
|
await db.delete(prefixes).where(eq(prefixes.name, name))
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/prefixes/?api_key=${env.TAM3_REMOTE_KEY}&prefix_name=${name}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/prefixes/?api_key=${env.TAM3_REMOTE_KEY}&prefix_name=${name}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
return new Response(JSON.stringify({status: "Issue deleting prefix."}), {status: res.status, statusText: res.statusText});
|
return new Response(JSON.stringify({status: "Issue deleting prefix."}), {status: res.status, statusText: res.statusText});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
18
webapp/src/routes/api/reports/bybasket/[prefix]/+server.js
Normal file
18
webapp/src/routes/api/reports/bybasket/[prefix]/+server.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { env } from "process";
|
||||||
|
import { db } from "$lib/server/db";
|
||||||
|
import { report } from "$lib/server/db/schema";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
export async function GET({ params }) {
|
||||||
|
if (env.TAM3_REMOTE) {
|
||||||
|
const res = await fetch(`${env.TAM3_REMOTE}/api/reports/bybasket/${params.prefix}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
|
if (!res.ok) {
|
||||||
|
return new Response(JSON.stringify({detail: "Unable to fetch report."}), {status: res.status, statusText: res.statusText})
|
||||||
|
}
|
||||||
|
const data = await res.json();
|
||||||
|
return new Response(JSON.stringify(data), {status: 200, statusText: "Fetched report successfully."})
|
||||||
|
} else {
|
||||||
|
const data = await db.select().from(report).where(eq(report.prefix, params.prefix)).orderBy(report.b_id);
|
||||||
|
return new Response(JSON.stringify(data), {status: 200, statusText: "Loaded report successfully."})
|
||||||
|
}
|
||||||
|
}
|
||||||
18
webapp/src/routes/api/reports/byname/[prefix]/+server.js
Normal file
18
webapp/src/routes/api/reports/byname/[prefix]/+server.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { env } from "process";
|
||||||
|
import { db } from "$lib/server/db";
|
||||||
|
import { report } from "$lib/server/db/schema";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
export async function GET({ params }) {
|
||||||
|
if (env.TAM3_REMOTE) {
|
||||||
|
const res = await fetch(`${env.TAM3_REMOTE}/api/reports/byname/${params.prefix}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
|
if (!res.ok) {
|
||||||
|
return new Response(JSON.stringify({detail: "Unable to fetch report."}), {status: res.status, statusText: res.statusText})
|
||||||
|
}
|
||||||
|
const data = await res.json();
|
||||||
|
return new Response(JSON.stringify(data), {status: 200, statusText: "Fetched report successfully."})
|
||||||
|
} else {
|
||||||
|
const data = await db.select().from(report).where(eq(report.prefix, params.prefix));
|
||||||
|
return new Response(JSON.stringify(data), {status: 200, statusText: "Loaded report successfully."})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import { eq, and } from "drizzle-orm";
|
|||||||
|
|
||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/tickets/${params.prefix}/${params.t_id}/?api_key=${env.TAM3_REMOTE}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/tickets/${params.prefix}/${params.t_id}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
return new Response(JSON.stringify({detail: "Unable to fetch ticket."}), {status: res.status, statusText: res.statusText})
|
return new Response(JSON.stringify({detail: "Unable to fetch ticket."}), {status: res.status, statusText: res.statusText})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
duplicateDown: () => {
|
duplicateDown: () => {
|
||||||
const next_idx = current_idx + 1;
|
const next_idx = current_idx + 1;
|
||||||
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, 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);
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
duplicateUp: () => {
|
duplicateUp: () => {
|
||||||
const prev_idx = current_idx - 1;
|
const prev_idx = current_idx - 1;
|
||||||
if (prev_idx >= 0) {
|
if (prev_idx >= 0) {
|
||||||
current_baskets[prev_idx] = {...current_baskets[current_idx], b_id: current_baskets[prev_idx].b_id, 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);
|
||||||
|
|||||||
7
webapp/src/routes/reports/bybasket/[prefix]/+page.js
Normal file
7
webapp/src/routes/reports/bybasket/[prefix]/+page.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export async function load({ fetch, params }) {
|
||||||
|
let res = await fetch(`/api/prefixes/${params.prefix}`);
|
||||||
|
const prefix_data = await res.json();
|
||||||
|
res = await fetch(`/api/reports/bybasket/${params.prefix}`);
|
||||||
|
const report_data = await res.json()
|
||||||
|
return {prefix: prefix_data, report: report_data}
|
||||||
|
}
|
||||||
95
webapp/src/routes/reports/bybasket/[prefix]/+page.svelte
Normal file
95
webapp/src/routes/reports/bybasket/[prefix]/+page.svelte
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<script>
|
||||||
|
import { env } from '$env/dynamic/public';
|
||||||
|
import { browser } from '$app/environment';
|
||||||
|
|
||||||
|
const { data } = $props();
|
||||||
|
const prefix = {...data.prefix};
|
||||||
|
const report_data = data.report;
|
||||||
|
let show_data = $state([...report_data]);
|
||||||
|
let report_subject = $state("All Preferences");
|
||||||
|
|
||||||
|
if (browser) {
|
||||||
|
document.title = `${prefix.name} Report By Name`
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="reportheader">
|
||||||
|
<div class="flex-row-space {prefix.color}">
|
||||||
|
<div class="flex-row">
|
||||||
|
<button class="styled" onclick={() => {
|
||||||
|
show_data = [...report_data];
|
||||||
|
report_subject = "All Preferences";
|
||||||
|
}}>All Preferences</button>
|
||||||
|
<button class="styled" onclick={() => {
|
||||||
|
show_data = [...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";
|
||||||
|
}}>Text</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex-row">
|
||||||
|
<button class="styled" onclick={() => window.print()}>Print</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="90"><h1>{prefix.name} - Report - {report_subject}</h1></th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Basket ID</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Ticket #</th>
|
||||||
|
<th>Winner Name</th>
|
||||||
|
<th>Phone Number</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each show_data as report_entry}
|
||||||
|
<tr>
|
||||||
|
<td>{report_entry.b_id}</td>
|
||||||
|
<td>{report_entry.description}</td>
|
||||||
|
<td>{report_entry.winning_ticket}</td>
|
||||||
|
<td>{report_entry.winner_name}</td>
|
||||||
|
<td>{report_entry.phone_number}</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">{env.PUBLIC_TAM3_VENUE || ""}</td>
|
||||||
|
<td colspan="2" style="text-align: right">TAM3 by Dilan Gilluly</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
border: solid 1px black;
|
||||||
|
padding: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
table tbody tr:nth-child(2n) {
|
||||||
|
background-color: #dddddd;
|
||||||
|
}
|
||||||
|
table tfoot td {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
#reportheader {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@page {
|
||||||
|
margin: 0.25in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
7
webapp/src/routes/reports/byname/[prefix]/+page.js
Normal file
7
webapp/src/routes/reports/byname/[prefix]/+page.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export async function load({ fetch, params }) {
|
||||||
|
let res = await fetch(`/api/prefixes/${params.prefix}`);
|
||||||
|
const prefix_data = await res.json();
|
||||||
|
res = await fetch(`/api/reports/byname/${params.prefix}`);
|
||||||
|
const report_data = await res.json()
|
||||||
|
return {prefix: prefix_data, report: report_data}
|
||||||
|
}
|
||||||
95
webapp/src/routes/reports/byname/[prefix]/+page.svelte
Normal file
95
webapp/src/routes/reports/byname/[prefix]/+page.svelte
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<script>
|
||||||
|
import { env } from '$env/dynamic/public';
|
||||||
|
import { browser } from '$app/environment';
|
||||||
|
|
||||||
|
const { data } = $props();
|
||||||
|
const prefix = {...data.prefix};
|
||||||
|
const report_data = data.report;
|
||||||
|
let show_data = $state([...report_data]);
|
||||||
|
let report_subject = $state("All Preferences");
|
||||||
|
|
||||||
|
if (browser) {
|
||||||
|
document.title = `${prefix.name} Report By Name`
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="reportheader">
|
||||||
|
<div class="flex-row-space {prefix.color}">
|
||||||
|
<div class="flex-row">
|
||||||
|
<button class="styled" onclick={() => {
|
||||||
|
show_data = [...report_data];
|
||||||
|
report_subject = "All Preferences";
|
||||||
|
}}>All Preferences</button>
|
||||||
|
<button class="styled" onclick={() => {
|
||||||
|
show_data = [...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";
|
||||||
|
}}>Text</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex-row">
|
||||||
|
<button class="styled" onclick={() => window.print()}>Print</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="90"><h1>{prefix.name} - Report - {report_subject}</h1></th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Winner Name</th>
|
||||||
|
<th>Phone Number</th>
|
||||||
|
<th>Basket ID</th>
|
||||||
|
<th>Ticket #</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each show_data as report_entry}
|
||||||
|
<tr>
|
||||||
|
<td>{report_entry.winner_name}</td>
|
||||||
|
<td>{report_entry.phone_number}</td>
|
||||||
|
<td>{report_entry.b_id}</td>
|
||||||
|
<td>{report_entry.winning_ticket}</td>
|
||||||
|
<td>{report_entry.description}</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">{env.PUBLIC_TAM3_VENUE || ""}</td>
|
||||||
|
<td colspan="2" style="text-align: right">TAM3 by Dilan Gilluly</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
border: solid 1px black;
|
||||||
|
padding: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
table tbody tr:nth-child(2n) {
|
||||||
|
background-color: #dddddd;
|
||||||
|
}
|
||||||
|
table tfoot td {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
#reportheader {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@page {
|
||||||
|
margin: 0.25in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user