Compare commits
8 Commits
382eddf8ea
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c1b0421650 | |||
| 84c99880ed | |||
| 866b759add | |||
| c3195521aa | |||
| b06994354f | |||
| 2d7f6457cd | |||
| 5d24b80e26 | |||
| 32263ad6c6 |
@@ -9,6 +9,12 @@ from repos.tickets import Ticket, TicketRepo
|
|||||||
from repos.baskets import Basket, BasketRepo
|
from repos.baskets import Basket, BasketRepo
|
||||||
from repos.template import Repo
|
from repos.template import Repo
|
||||||
|
|
||||||
|
def chunk_list(in_lst: list, chunk_size: int):
|
||||||
|
chunks = []
|
||||||
|
for i in range(0, len(in_lst), chunk_size):
|
||||||
|
chunks.append(in_lst[i:i + chunk_size])
|
||||||
|
return chunks
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BackupFile:
|
class BackupFile:
|
||||||
prefixes: List[Prefix] = field(default_factory=list)
|
prefixes: List[Prefix] = field(default_factory=list)
|
||||||
@@ -21,8 +27,10 @@ class BackupFileRepo:
|
|||||||
return new_file
|
return new_file
|
||||||
def post_file(self, uploaded_file: BackupFile) -> str:
|
def post_file(self, uploaded_file: BackupFile) -> str:
|
||||||
PrefixRepo().add_list(uploaded_file.prefixes)
|
PrefixRepo().add_list(uploaded_file.prefixes)
|
||||||
TicketRepo().post_list(uploaded_file.tickets)
|
for ticket_chunk in chunk_list(uploaded_file.tickets, 300):
|
||||||
BasketRepo().post_list(uploaded_file.baskets)
|
TicketRepo().post_list(ticket_chunk)
|
||||||
|
for basket_chunk in chunk_list(uploaded_file.baskets, 300):
|
||||||
|
BasketRepo().post_list(basket_chunk)
|
||||||
return "File posted successfully."
|
return "File posted successfully."
|
||||||
|
|
||||||
backup_router = APIRouter(prefix="/api/backuprestore")
|
backup_router = APIRouter(prefix="/api/backuprestore")
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ services:
|
|||||||
MARIADB_USER: tam3
|
MARIADB_USER: tam3
|
||||||
MARIADB_PASSWORD: tam3
|
MARIADB_PASSWORD: tam3
|
||||||
volumes:
|
volumes:
|
||||||
- "tam3-db:/var/lib/mysql"
|
- "tam3-db:/var/lib/mysql:rw,z"
|
||||||
ports:
|
ports:
|
||||||
- 127.0.0.1:3306:3306
|
- 127.0.0.1:3306:3306
|
||||||
adminer:
|
adminer:
|
||||||
|
|||||||
@@ -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 "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
|
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
|
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.1
|
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 PUBLIC_TAM3_VENUE="$venuename" -p 127.0.0.1:8300:3000 docker.io/dbob16/tam3-webclient:0.2.0
|
||||||
elif [ -x "$(command -v podman)" ]; then
|
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.1
|
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 PUBLIC_TAM3_VENUE="$venuename" -p 127.0.0.1:8300:3000 docker.io/dbob16/tam3-webclient:0.2.0
|
||||||
runin_podman="true"
|
runin_podman="true"
|
||||||
else
|
else
|
||||||
echo "Neither Docker nor Podman are installed. Please install whichever you prefer and try again."
|
echo "Neither Docker nor Podman are installed. Please install whichever you prefer and try again."
|
||||||
@@ -19,9 +19,9 @@ exit 1
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [ -x "$(command -v docker)" ]; then
|
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.1
|
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.2.0
|
||||||
elif [ -x "$(command -v podman )" ]; then
|
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.1
|
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.2.0
|
||||||
runin_podman="true"
|
runin_podman="true"
|
||||||
else
|
else
|
||||||
echo "Neither Docker nor Podman are installed. Please install whichever you prefer and try again."
|
echo "Neither Docker nor Podman are installed. Please install whichever you prefer and try again."
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
tam3-db:
|
tam3-db:
|
||||||
image: docker.io/dbob16/tam3-db:0.1.1
|
image: docker.io/dbob16/tam3-db:0.2.0
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
MARIADB_RANDOM_ROOT_PASSWORD: 1
|
MARIADB_RANDOM_ROOT_PASSWORD: 1
|
||||||
@@ -16,7 +16,7 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
tam3-api:
|
tam3-api:
|
||||||
image: docker.io/dbob16/tam3-api:0.1.1
|
image: docker.io/dbob16/tam3-api:0.2.0
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
TAM3_DATA_PATH: /data
|
TAM3_DATA_PATH: /data
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
tam3-db:
|
tam3-db:
|
||||||
image: docker.io/dbob16/tam3-db:0.1.1
|
image: docker.io/dbob16/tam3-db:0.2.0
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
MARIADB_RANDOM_ROOT_PASSWORD: 1
|
MARIADB_RANDOM_ROOT_PASSWORD: 1
|
||||||
@@ -16,7 +16,7 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
tam3-api:
|
tam3-api:
|
||||||
image: docker.io/dbob16/tam3-api:0.1.1
|
image: docker.io/dbob16/tam3-api:0.2.0
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
TAM3_DATA_PATH: /data
|
TAM3_DATA_PATH: /data
|
||||||
|
|||||||
1
webapp/.gitignore
vendored
1
webapp/.gitignore
vendored
@@ -17,6 +17,7 @@ Thumbs.db
|
|||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
!.env.test
|
!.env.test
|
||||||
|
settings.json
|
||||||
|
|
||||||
# Vite
|
# Vite
|
||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ COPY . .
|
|||||||
RUN npm install && npm run build
|
RUN npm install && npm run build
|
||||||
|
|
||||||
ENV DATABASE_URL=file:/data/local.db
|
ENV DATABASE_URL=file:/data/local.db
|
||||||
|
ENV SETTINGS_PATH=/data/settings.json
|
||||||
ENV BODY_SIZE_LIMIT=100M
|
ENV BODY_SIZE_LIMIT=100M
|
||||||
ENV NODE_TLS_REJECT_UNAUTHORIZED=0
|
ENV NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
:root {
|
:root {
|
||||||
|
font-family: serif;
|
||||||
|
font-size: 14pt;
|
||||||
--button-fg: #333333;
|
--button-fg: #333333;
|
||||||
--button-bg: #eeeeee;
|
--button-bg: #eeeeee;
|
||||||
--button-border: #333333;
|
--button-border: #333333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button, input, select, option {
|
||||||
|
font-family: serif;
|
||||||
|
font-size: 14pt;
|
||||||
|
}
|
||||||
|
|
||||||
a.styled, button.styled {
|
a.styled, button.styled {
|
||||||
display: block;
|
display: block;
|
||||||
font: bold 12pt Arial;
|
font-weight: bold;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background-color: var(--button-bg);
|
background-color: var(--button-bg);
|
||||||
color: var(--button-fg);
|
color: var(--button-fg);
|
||||||
@@ -20,7 +27,7 @@ a.styled, button.styled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
border: 1px solid #000000;
|
border: 2px solid #000000;
|
||||||
border-radius: 0.13rem;
|
border-radius: 0.13rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
webapp/src/lib/server/chunkArray.js
Normal file
7
webapp/src/lib/server/chunkArray.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export function chunkArray(arr, chunkSize) {
|
||||||
|
const chunks = [];
|
||||||
|
for (let i = 0; i < arr.length; i += chunkSize) {
|
||||||
|
chunks.push(arr.slice(i, i + chunkSize));
|
||||||
|
}
|
||||||
|
return chunks;
|
||||||
|
}
|
||||||
19
webapp/src/lib/server/settings.js
Normal file
19
webapp/src/lib/server/settings.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { env } from "$env/dynamic/private";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
export function readSettings() {
|
||||||
|
if (fs.existsSync(env.SETTINGS_PATH)) {
|
||||||
|
const rawFileData = fs.readFileSync(env.SETTINGS_PATH, 'utf-8');
|
||||||
|
const fileData = JSON.parse(rawFileData);
|
||||||
|
return fileData;
|
||||||
|
} else {
|
||||||
|
const fileData = {TAM3_REMOTE: env.TAM3_REMOTE || "", TAM3_REMOTE_KEY: env.TAM3_REMOTE_KEY || ""};
|
||||||
|
fs.writeFileSync(env.SETTINGS_PATH, JSON.stringify(fileData, null, 2));
|
||||||
|
return fileData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeSettings(settingsObj) {
|
||||||
|
fs.writeFileSync(env.SETTINGS_PATH, JSON.stringify(settingsObj, null, 2));
|
||||||
|
return "File written successfully."
|
||||||
|
}
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
import favicon from "$lib/assets/favicon.svg"
|
import favicon from "$lib/assets/favicon.svg"
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
const all_prefixes = [...data.prefixes];
|
||||||
let prefix_name = $state("");
|
let prefix_name = $state("");
|
||||||
let all_prefixes = $state([]);
|
|
||||||
let current_prefix = $state({name: "", color: "", weight: 0});
|
let current_prefix = $state({name: "", color: "", weight: 0});
|
||||||
let admin_mode = $state(false);
|
let admin_mode = $state(false);
|
||||||
const venue = env.PUBLIC_TAM3_VENUE || "TAM3";
|
const venue = env.PUBLIC_TAM3_VENUE || "TAM3";
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (browser) {
|
if (browser) {
|
||||||
all_prefixes = [...data.prefixes];
|
|
||||||
document.title = `${venue} - Main Menu`;
|
document.title = `${venue} - Main Menu`;
|
||||||
hotkeys.filter = function(event) {return true};
|
hotkeys.filter = function(event) {return true};
|
||||||
hotkeys('alt+a', function(event) {event.preventDefault(); admin_mode = !admin_mode; return false;});
|
hotkeys('alt+a', function(event) {event.preventDefault(); admin_mode = !admin_mode; return false;});
|
||||||
@@ -36,7 +35,7 @@
|
|||||||
<img src="{favicon}" alt="TAM3 Icon - Red ticket with TAM3 on it" class="icon">
|
<img src="{favicon}" alt="TAM3 Icon - Red ticket with TAM3 on it" class="icon">
|
||||||
<h1>{venue} - Main Menu</h1>
|
<h1>{venue} - Main Menu</h1>
|
||||||
</div>
|
</div>
|
||||||
{#if prefix_name !== ""}
|
{#if all_prefixes.length > 0}
|
||||||
<div class="universal-reports flex-row tb-margin">
|
<div class="universal-reports flex-row tb-margin">
|
||||||
<a href="/counts" target="_blank" class="styled">Counts</a>
|
<a href="/counts" target="_blank" class="styled">Counts</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -66,6 +65,8 @@
|
|||||||
<a href="/reports/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="/reports/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>
|
||||||
|
{:else}
|
||||||
|
<p>There aren't any prefixes available, please create them.</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if admin_mode}
|
{#if admin_mode}
|
||||||
@@ -73,6 +74,7 @@
|
|||||||
<div class="flex-row">
|
<div class="flex-row">
|
||||||
<a href="/prefixes" target="_blank" class="styled">Prefix Editor</a>
|
<a href="/prefixes" target="_blank" class="styled">Prefix Editor</a>
|
||||||
<a href="/backuprestore" target="_blank" class="styled">Backup/Restore</a>
|
<a href="/backuprestore" target="_blank" class="styled">Backup/Restore</a>
|
||||||
|
<a href="/settings" target="_blank" class="styled">Settings</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { env } from "$env/dynamic/private";
|
import { readSettings } from "$lib/server/settings";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
|
const env = readSettings();
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
import { env } from "$env/dynamic/private";
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
import { prefixes, tickets, baskets } from "$lib/server/db/schema";
|
import { prefixes, tickets, baskets } from "$lib/server/db/schema";
|
||||||
import { sql } from "drizzle-orm";
|
import { sql } from "drizzle-orm";
|
||||||
|
import { chunkArray } from "$lib/server/chunkArray";
|
||||||
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() {
|
export async function GET() {
|
||||||
|
const env = readSettings();
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/backuprestore/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/backuprestore/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
@@ -28,6 +22,7 @@ export async function GET() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function POST({ request }) {
|
export async function POST({ request }) {
|
||||||
|
const env = readSettings();
|
||||||
const req = await request.json();
|
const req = await request.json();
|
||||||
for (let prefixChunk of chunkArray(req.prefixes, 300)) {
|
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`}});
|
await db.insert(prefixes).values(prefixChunk).onConflictDoUpdate({target: prefixes.name, set: {color: sql`excluded.color`, weight: sql`excluded.weight`}});
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { env } from "$env/dynamic/private";
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
|
import { sql } from "drizzle-orm";
|
||||||
import { baskets } from "$lib/server/db/schema";
|
import { baskets } from "$lib/server/db/schema";
|
||||||
|
import { chunkArray } from "$lib/server/chunkArray";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
|
const env = readSettings();
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/baskets/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/baskets/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
@@ -25,19 +28,20 @@ export async function GET() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function POST({ request }) {
|
export async function POST({ request }) {
|
||||||
const i_baskets = await request.json();
|
const env = readSettings();
|
||||||
for (let basket of i_baskets) {
|
const i_baskets = await request.json();
|
||||||
await db.insert(baskets).values({prefix: basket.prefix, b_id: basket.b_id, description: basket.description, donors: basket.donors, winning_ticket: basket.winning_ticket})
|
for (let basketChunk of chunkArray(i_baskets, 300)) {
|
||||||
.onConflictDoUpdate({target: [baskets.prefix, baskets.b_id], set: {description: basket.description, donors: basket.donors, winning_ticket: basket.winning_ticket}})
|
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/baskets/?api_key=${env.TAM3_REMOTE_KEY}`, {
|
||||||
|
body: JSON.stringify([...i_baskets]), method: 'POST', headers: {'Content-Type': 'application/json'}
|
||||||
|
});
|
||||||
|
if (!res.ok) {
|
||||||
|
return new Response(JSON.stringify({details: "Issue posting baskets to remote."}), {status: res.status, statusText: res.statusText})
|
||||||
};
|
};
|
||||||
if (env.TAM3_REMOTE) {
|
const data = await res.json();
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/baskets/?api_key=${env.TAM3_REMOTE_KEY}`, {
|
};
|
||||||
body: JSON.stringify([...i_baskets]), method: 'POST', headers: {'Content-Type': 'application/json'}
|
return new Response(JSON.stringify({details: "Posted baskets successfully."}), {status: 200, statusText: "Posted baskets successfully."})
|
||||||
});
|
|
||||||
if (!res.ok) {
|
|
||||||
return new Response(JSON.stringify({details: "Issue posting baskets to remote."}), {status: res.status, statusText: res.statusText})
|
|
||||||
};
|
|
||||||
const data = await res.json();
|
|
||||||
};
|
|
||||||
return new Response(JSON.stringify({details: "Posted baskets successfully."}), {status: 200, statusText: "Posted baskets successfully."})
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { env } from "$env/dynamic/private";
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
import { baskets } from "$lib/server/db/schema";
|
import { baskets } from "$lib/server/db/schema";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
|
|
||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
|
const env = readSettings();
|
||||||
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/${params.prefix}/${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}`);
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { env } from "$env/dynamic/private";
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
|
import { sql } from "drizzle-orm";
|
||||||
import { baskets, combined } from "$lib/server/db/schema";
|
import { baskets, combined } from "$lib/server/db/schema";
|
||||||
|
import { chunkArray } from "$lib/server/chunkArray";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
|
const env = readSettings();
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/combined/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/combined/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
@@ -20,10 +23,11 @@ export async function GET() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function POST({ request }) {
|
export async function POST({ request }) {
|
||||||
|
const env = readSettings();
|
||||||
const r_data = await request.json()
|
const r_data = await request.json()
|
||||||
for (let winner of r_data) {
|
for (let winnerChunk of chunkArray(r_data, 300)) {
|
||||||
await db.insert(baskets).values({prefix: winner.prefix, b_id: winner.b_id, winning_ticket: winner.winning_ticket})
|
await db.insert(baskets).values(winnerChunk)
|
||||||
.onConflictDoUpdate({target: [baskets.prefix, baskets.b_id], set: {winning_ticket: winner.winning_ticket}})
|
.onConflictDoUpdate({target: [baskets.prefix, baskets.b_id], set: {winning_ticket: sql`excluded.winning_ticket`}})
|
||||||
}
|
}
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/combined/?api_key=${env.TAM3_REMOTE_KEY}`, {
|
const res = await fetch(`${env.TAM3_REMOTE}/api/combined/?api_key=${env.TAM3_REMOTE_KEY}`, {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { env } from "process";
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
import { combined } from "$lib/server/db/schema";
|
import { combined } from "$lib/server/db/schema";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
|
|
||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
|
const env = readSettings();
|
||||||
const n_b_from = parseInt(params.b_from), n_b_to = parseInt(params.b_to);
|
const 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/combined/${params.prefix}/${n_b_from}/${n_b_to}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/combined/${params.prefix}/${n_b_from}/${n_b_to}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { env } from "$env/dynamic/private";
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
import { combined } from "$lib/server/db/schema";
|
import { combined } from "$lib/server/db/schema";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
|
|
||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
|
const env = readSettings();
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/combined/${params.prefix}/${params.b_id}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/combined/${params.prefix}/${params.b_id}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { env } from "$env/dynamic/private";
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
import { counts } from "$lib/server/db/schema";
|
import { counts } from "$lib/server/db/schema";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
|
const env = readSettings();
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/counts/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/counts/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
import { prefixes } from "$lib/server/db/schema";
|
import { prefixes } from "$lib/server/db/schema";
|
||||||
import { env } from "$env/dynamic/private";
|
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
|
const env = readSettings();
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/prefixes/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/prefixes/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
@@ -17,6 +18,7 @@ export async function GET() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function POST({ request }) {
|
export async function POST({ request }) {
|
||||||
|
const env = readSettings();
|
||||||
const { name, color, weight } = await request.json();
|
const { name, color, weight } = await request.json();
|
||||||
await db.insert(prefixes).values({name: name, color: color, weight: weight}).onConflictDoUpdate({target: prefixes.name, set: {color: color, weight: weight}});
|
await db.insert(prefixes).values({name: name, color: color, weight: weight}).onConflictDoUpdate({target: prefixes.name, set: {color: color, weight: weight}});
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { prefixes } from "$lib/server/db/schema";
|
import { prefixes } from "$lib/server/db/schema";
|
||||||
import { env } from "$env/dynamic/private";
|
|
||||||
|
|
||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
|
const env = readSettings();
|
||||||
let { name } = params;
|
let { name } = params;
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/prefixes/${name}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/prefixes/${name}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
@@ -23,6 +24,7 @@ export async function GET({ params }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function DELETE({ params }) {
|
export async function DELETE({ params }) {
|
||||||
|
const env = readSettings();
|
||||||
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) {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { env } from "process";
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
import { report } from "$lib/server/db/schema";
|
import { report } from "$lib/server/db/schema";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
|
const env = readSettings();
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/reports/bybasket/${params.prefix}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/reports/bybasket/${params.prefix}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { env } from "process";
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
import { report } from "$lib/server/db/schema";
|
import { report } from "$lib/server/db/schema";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
|
const env = readSettings();
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/reports/byname/${params.prefix}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/reports/byname/${params.prefix}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
|||||||
11
webapp/src/routes/api/settings/+server.js
Normal file
11
webapp/src/routes/api/settings/+server.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { readSettings, writeSettings } from "$lib/server/settings";
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
return new Response(JSON.stringify(readSettings()), {status: 200, statusText: "Settings returned successfully"});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST({ request }) {
|
||||||
|
const params = await request.json();
|
||||||
|
await writeSettings(params);
|
||||||
|
return new Response(JSON.stringify({detail: "Settings saved successfully."}), {status: 200, statusText: "Settings saved successfully."})
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
|
import { sql } from "drizzle-orm";
|
||||||
import { tickets } from "$lib/server/db/schema";
|
import { tickets } from "$lib/server/db/schema";
|
||||||
import { env } from "$env/dynamic/private";
|
import { chunkArray } from "$lib/server/chunkArray";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
|
const env = readSettings();
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/tickets/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/tickets/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
@@ -29,10 +32,11 @@ export async function GET() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function POST({ request }) {
|
export async function POST({ request }) {
|
||||||
|
const env = readSettings();
|
||||||
const i_tickets = await request.json();
|
const i_tickets = await request.json();
|
||||||
for (let ticket of i_tickets) {
|
for (let ticketChunk of chunkArray(i_tickets, 300)) {
|
||||||
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})
|
await db.insert(tickets).values(ticketChunk)
|
||||||
.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}});
|
.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`}});
|
||||||
};
|
};
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/tickets/?api_key=${env.TAM3_REMOTE_KEY}`, {
|
const res = await fetch(`${env.TAM3_REMOTE}/api/tickets/?api_key=${env.TAM3_REMOTE_KEY}`, {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
import { tickets } from "$lib/server/db/schema";
|
import { tickets } from "$lib/server/db/schema";
|
||||||
import { env } from "$env/dynamic/private";
|
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
|
|
||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
|
const env = readSettings();
|
||||||
let n_t_from = parseInt(params.t_from), n_t_to = parseInt(params.t_to);
|
let n_t_from = parseInt(params.t_from), n_t_to = parseInt(params.t_to);
|
||||||
if (env.TAM3_REMOTE) {
|
if (env.TAM3_REMOTE) {
|
||||||
const res = await fetch(`${env.TAM3_REMOTE}/api/tickets/${params.prefix}/${n_t_from}/${n_t_to}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
const res = await fetch(`${env.TAM3_REMOTE}/api/tickets/${params.prefix}/${n_t_from}/${n_t_to}/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { env } from "$env/dynamic/private";
|
import { readSettings } from "$lib/server/settings";
|
||||||
import { db } from "$lib/server/db";
|
import { db } from "$lib/server/db";
|
||||||
import { tickets } from "$lib/server/db/schema";
|
import { tickets } from "$lib/server/db/schema";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
|
|
||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
|
const env = readSettings();
|
||||||
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_KEY}`);
|
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) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
const functions = {
|
const functions = {
|
||||||
refreshPage: async () => {
|
refreshPage: async () => {
|
||||||
if (current_baskets.length > 0) {
|
if (current_baskets.filter(basket => basket.changed === true).length > 0) {
|
||||||
functions.saveAll()
|
functions.saveAll()
|
||||||
}
|
}
|
||||||
const res = await fetch(`/api/baskets/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`);
|
const res = await fetch(`/api/baskets/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`);
|
||||||
@@ -136,6 +136,8 @@
|
|||||||
}
|
}
|
||||||
tbody tr:focus-within td:first-child {
|
tbody tr:focus-within td:first-child {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
border-top: solid 1px;
|
||||||
|
border-bottom: solid 1px;
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
const functions = {
|
const functions = {
|
||||||
refreshPage: async () => {
|
refreshPage: async () => {
|
||||||
if (current_drawings.length > 0) {
|
if (current_drawings.filter(drawing => drawing.changed === true).length > 0) {
|
||||||
functions.saveAll()
|
functions.saveAll()
|
||||||
}
|
}
|
||||||
const res = await fetch(`/api/combined/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`);
|
const res = await fetch(`/api/combined/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`);
|
||||||
@@ -142,6 +142,8 @@
|
|||||||
}
|
}
|
||||||
tbody tr:focus-within td:first-child {
|
tbody tr:focus-within td:first-child {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
border-top: solid 1px;
|
||||||
|
border-bottom: solid 1px;
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|||||||
5
webapp/src/routes/settings/+page.js
Normal file
5
webapp/src/routes/settings/+page.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export async function load({ fetch }) {
|
||||||
|
const res = await fetch('/api/settings');
|
||||||
|
const settingsData = await res.json();
|
||||||
|
return {settings: settingsData};
|
||||||
|
}
|
||||||
53
webapp/src/routes/settings/+page.svelte
Normal file
53
webapp/src/routes/settings/+page.svelte
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<script>
|
||||||
|
import { browser } from '$app/environment';
|
||||||
|
const { data } = $props();
|
||||||
|
|
||||||
|
let stagingSettings = $state({...data.settings});
|
||||||
|
let status = $state("")
|
||||||
|
|
||||||
|
async function saveChanges() {
|
||||||
|
const res = await fetch('/api/settings', {method: 'POST', body: JSON.stringify(stagingSettings), headers: {'Content-Type': 'application/json'}});
|
||||||
|
if (res.ok) {
|
||||||
|
status = "Changes saved successfully";
|
||||||
|
setTimeout(() => {status = ""}, 5000);
|
||||||
|
} else {
|
||||||
|
status = "Error saving changes, check config file path, make sure folder exists";
|
||||||
|
setTimeout(() => {status = ""}, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelChanges() {
|
||||||
|
stagingSettings = {...data.settings};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser) {
|
||||||
|
document.title = "TAM3 - Settings"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1>Settings</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2>Remote Server</h2>
|
||||||
|
<div><strong>Address:</strong></div>
|
||||||
|
<div><em>For example: https://ip_or_hostname:8443</em></div>
|
||||||
|
<div><input type="text" bind:value={stagingSettings.TAM3_REMOTE}></div>
|
||||||
|
<div><strong>API Key:</strong></div>
|
||||||
|
<div class="flex-row">
|
||||||
|
<input type="password" bind:value={stagingSettings.TAM3_REMOTE_KEY}>
|
||||||
|
<button class="styled" onclick={() => {stagingSettings.TAM3_REMOTE_KEY = ""}}>Clear</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div class="flex-row">
|
||||||
|
<button class="styled" onclick={saveChanges}>Save</button>
|
||||||
|
<button class="styled" onclick={cancelChanges}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div class="status">
|
||||||
|
<div>{status}</div>
|
||||||
|
</div>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
const functions = {
|
const functions = {
|
||||||
refreshPage: async () => {
|
refreshPage: async () => {
|
||||||
if (current_tickets.length > 0) {
|
if (current_tickets.filter(ticket => ticket.changed === true).length > 0) {
|
||||||
functions.saveAll();
|
functions.saveAll();
|
||||||
};
|
};
|
||||||
const res = await fetch(`/api/tickets/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`);
|
const res = await fetch(`/api/tickets/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`);
|
||||||
@@ -144,6 +144,8 @@
|
|||||||
}
|
}
|
||||||
tbody tr:focus-within td:first-child {
|
tbody tr:focus-within td:first-child {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
border-top: solid 1px;
|
||||||
|
border-bottom: solid 1px;
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|||||||
Reference in New Issue
Block a user