]> OzVa Git service - delta-velorum/commitdiff
Bulk commit
authorMax Value <greenwoodw50@gmail.com>
Mon, 6 Apr 2026 20:52:36 +0000 (21:52 +0100)
committerMax Value <greenwoodw50@gmail.com>
Mon, 6 Apr 2026 20:52:36 +0000 (21:52 +0100)
35 files changed:
director/__init__.py
director/database/__init__.py
director/database/read.py
director/database/utils.py
director/database/write.py [new file with mode: 0644]
director/src/admin.ts
director/src/discount.ts [new file with mode: 0644]
director/src/price.ts
director/src/show_until.ts [new file with mode: 0644]
director/src/sound.ts [new file with mode: 0644]
director/src/state.ts [new file with mode: 0644]
director/src/style.ts [new file with mode: 0644]
director/src/text.ts
director/src/time.ts [new file with mode: 0644]
director/src/until.ts [new file with mode: 0644]
director/src/update.ts
director/src/utils.ts [new file with mode: 0644]
director/static/admin.css [new file with mode: 0644]
director/static/info.css [new file with mode: 0644]
director/static/media/sfx/clock.wav [new file with mode: 0644]
director/static/media/sfx/shout.wav [new file with mode: 0644]
director/static/show_until.css [new file with mode: 0644]
director/templates/admin.html
director/templates/admin/color.html [new file with mode: 0644]
director/templates/admin/float.html
director/templates/admin/number.html
director/templates/admin/text.html
director/templates/admin/textarea.html
director/templates/admin/trigger.html
director/templates/index.html
director/templates/info/a.html [new file with mode: 0644]
director/templates/info/b.html [new file with mode: 0644]
director/templates/info/c.html [new file with mode: 0644]
director/templates/table.html [new file with mode: 0644]
director/utils.py [new file with mode: 0644]

index 9311785278deb8890cc4ca559da5ca1475d17eb4..608125075bde95c064c1450212818752722c9c43 100644 (file)
@@ -1,28 +1,58 @@
-from flask import Flask, render_template, send_from_directory
+from flask import Flask, render_template, send_from_directory, request
 from flask_socketio import SocketIO, emit
+import time
 import os
 
+from .utils import deindex
+
 from director.database.read import read as database_read
+from director.database.read import read_all_current as database_read_all_current
+from director.database.read import read_indexes as database_read_indexes
+from director.database.read import read_views as database_read_views
+from director.database.write import write as database_write
 
 app = Flask(__name__, instance_relative_config=False)
-socketio = SocketIO(app, logger=True, engineio_logger=True)
+socketio = SocketIO(app, logger=False, engineio_logger=False)
 
 @app.route("/", methods=['get'])
 def index():
        return render_template("index.html")
 
-@app.route("/admin", methods=['get'])
+@app.route("/info/<string:screen>", methods=["get"])
+def info(screen):
+       return render_template(f"info/{screen}.html")
+
+@app.route("/admin", methods=["get"])
 def admin():
-       indexes, current = database_read()
-       return render_template("admin.html", indexes=indexes, current=current)
+       return render_template("admin.html", indexes=database_read_indexes())
+
+@app.route("/admin/<string:table_name>", methods=["get", "post"])
+def admin_table(table_name):
+       start = time.time()
+
+       if request.method == "POST":
+               database_write(request.form)
+
+               emit(
+                       "update",
+                       database_read_views(table_name),
+                       json=True, namespace="/", broadcast=True
+                       )
+
+       end = time.time()
+
+       return render_template(
+               "table.html",
+               indexes=database_read_indexes(),
+               table=database_read(table_name),
+               table_name=table_name,
+               time_taken = end - start
+               )
 
 @app.route("/script/<path:filename>", methods=["get"])
 def script(filename):
        return send_from_directory(os.path.join(app.root_path, "build"), filename)
 
-@app.route("/test")
-def test():
-       return [database_read()]
-       #emit("update", ["data__product", {"data":{"product":{"currentPrice":100}}}], json=True, namespace="/", broadcast=True)
-
-       return ""
+@socketio.on('connect')
+def connecting():
+       emit("update", database_read_views(), json=True, namespace="/", broadcast=True)
index efdbc2f3b1f7f0210a7e5d9a735d41abae940f5a..e408c87974f86593b58125ca5d9d028ef4723960 100644 (file)
@@ -4,9 +4,11 @@
        check if file exists and pragam check the database on init
 """
 
-from .utils import integrity_check
+from .utils import integrity_check, optimize
 import sqlite3
 import os
 
 assert os.path.exists("./data/main.db"), "Database missing"
 assert integrity_check(), "Database integrity error"
+
+optimize()
index 8c5f0121a3b0b1ede5283e0994c4448b434a2151..b180b39bfc166be79b9597973950a98fff50d07c 100644 (file)
@@ -1,23 +1,69 @@
 import sqlite3
 
-from .utils import list_tables
+from .utils import list_all, list_views
 
-def read():
-       tables = list_tables()
+def read_all_current():
+       tables = list_all()
+       indexes = read_indexes()
 
        with sqlite3.connect("./data/main.db") as connection:
                cursor = connection.cursor()
 
                data = {}
                for table in tables:
+                       if table == "indexes": continue
+
+                       if "__view" in table:
+                               index_table = table.split("__view")[0]
+                       else: index_table = table
+
                        cursor.execute(f"SELECT * FROM {table};")
 
-                       names = list(map(lambda x: x[0], cursor.description))
-                       rows = [dict(zip(names, row)) for row in cursor.fetchall()]
+                       names = list(map(lambda x: f"{table}~~{x[0]}", cursor.description))
+                       row = cursor.fetchall()[indexes[f"int--{index_table}"]]
+
+                       data.update(dict(zip(names, row)))
+
+               return data
+
+def read(table):
+       """
+               reads a table by name
+       """
+       with sqlite3.connect("./data/main.db") as connection:
+               cursor = connection.cursor()
+
+               cursor.execute(f"SELECT * FROM {table};")
+
+               names = list(map(lambda x: x[0], cursor.description))
+               rows = [dict(zip(names, row)) for row in cursor.fetchall()]
+
+       return rows
+
+def read_views(table=None):
+       """
+               Reads views. If a table is specified, it reads all views related to that
+               table. If not it reads all views.
+       """
+       if table is None:
+               related = list_views()
+       else:
+               related = [view for view in list_views() if view.split("__view")[0] == table]
+
+       data = {}
+       for view in related:
+               for key, value in read(view)[0].items():
+                       data.update({f"{view.replace('__view', '')}~~{key}": value})
+
+       return data
+
+def read_indexes():
+       with sqlite3.connect("./data/main.db") as connection:
+               cursor = connection.cursor()
+
+               cursor.execute("SELECT * FROM indexes;")
 
-                       if table == "indexes":
-                               indexes = rows[0]
-                       else:
-                               data.update({table: rows})
+               names = list(map(lambda x: x[0], cursor.description))
+               indexes = [dict(zip(names, row)) for row in cursor.fetchall()][0]
 
-               return indexes, data
+               return indexes
index f564caa1adfbce904afda103609dbcd3a2b79e1a..6866761e382cdbb63c1a284525a0f964a15d8c3e 100644 (file)
@@ -10,11 +10,28 @@ def integrity_check():
 
                return integrity.fetchone()[0] == "ok"
 
-def list_tables():
+def optimize():
        """
-               get a list of all the tables in the database
+               check the database is ok and return true if it is
+       """
+       with sqlite3.connect("./data/main.db") as connection:
+               cursor = connection.cursor()
+               integrity = cursor.execute('PRAGMA optimize')
+
+def list_all():
+       """
+               get a list of all the tables and views in the database
+       """
+       with sqlite3.connect("./data/main.db") as connection:
+               cursor = connection.cursor()
+               cursor.execute("SELECT name FROM sqlite_master WHERE type IN ('table', 'view');")
+               return [table[0] for table in cursor.fetchall()]
+
+def list_views():
+       """
+               get a list of all the views in the database
        """
        with sqlite3.connect("./data/main.db") as connection:
                cursor = connection.cursor()
-               cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
+               cursor.execute("SELECT name FROM sqlite_master WHERE type='view';")
                return [table[0] for table in cursor.fetchall()]
diff --git a/director/database/write.py b/director/database/write.py
new file mode 100644 (file)
index 0000000..eea0126
--- /dev/null
@@ -0,0 +1,20 @@
+import sqlite3
+
+from .utils import list_all
+from .read import read
+
+def write(data):
+
+       with sqlite3.connect("./data/main.db") as connection:
+               cursor = connection.cursor()
+
+               for key in data:
+                       table, index, feild = key.split("~~")
+
+                       cursor.execute(f"""
+                               UPDATE {table}
+                               SET '{feild}' = '{data[key]}'
+                               WHERE ROWID = {int(index) + 1};
+                               """)
+
+                       connection.commit()
index f8d100aac84db44b52ba115b432efa3424fbc536..22e72e368fadd66651bbe7e1a3879e44aeb1c93d 100644 (file)
@@ -1,6 +1,5 @@
-interface Body {
-       [index: string]: number;
-}
+const outlineStyle: string = "4px solid green";
+const outlineTime: number = 500; // ms
 
 export function setupTrigger() {
        // setup all trigger buttons to send post request on click
@@ -9,16 +8,32 @@ export function setupTrigger() {
 
        for (let e of el) {
                e.addEventListener("click", (event) => {
-                       let time: number = Math.round(Date.now() / 1000);
-                       let body: Body = {};
+                       if (event.target instanceof HTMLElement) {
+                               let time: number = Math.round(Date.now() / 1000);
+                               let body: FormData = new FormData();
+                               let delay = document.getElementById(`${event.target.getAttribute('name')}+delay`);
+
+                               if (delay instanceof HTMLInputElement) {
+                                       body.set(`${event.target.getAttribute('name')}`, `${time}+${delay.value}`);
+
+                                       fetch("", {
+                                               method: "POST",
+                                               body: body
+                                       });
 
-                       if (event.target instanceof Element) {
-                               body[`${event.target.getAttribute('name')}`] = time;
+                                       // trigger the button to turn green for approx same time (or 1s)
+                                       let timeout: number = delay.value as unknown as number * 1000;
+                                       if (timeout < outlineTime) { timeout = outlineTime; }
 
-                               fetch(".", {
-                                       method: "POST",
-                                       body: JSON.stringify(body)
-                               });
+                                       if ( event.target instanceof HTMLElement) {
+                                               event.target.style.outline = outlineStyle;
+                                               setTimeout(function () {
+                                                       if ( event.target instanceof HTMLElement) {
+                                                               event.target.style.outline = "none"
+                                                       }
+                                               }, timeout);
+                                       }
+                               }
                        }
                })
        }
diff --git a/director/src/discount.ts b/director/src/discount.ts
new file mode 100644 (file)
index 0000000..d05bd20
--- /dev/null
@@ -0,0 +1,25 @@
+export function renderDiscount(): void {
+       const el: HTMLCollectionOf<Element> =
+               document.getElementsByClassName("discount");
+
+       for (let e of el) {
+               if (e instanceof HTMLElement) {
+                       if (typeof e.dataset.raw === "string") {
+                               e.innerHTML = `${e.dataset.raw}%`;
+                       }
+               }
+       }
+}
+
+export function renderReverseDiscount(): void {
+       const el: HTMLCollectionOf<Element> =
+       document.getElementsByClassName("reverseDiscount");
+
+       for (let e of el) {
+               if (e instanceof HTMLElement) {
+                       if (typeof e.dataset.raw === "string") {
+                               e.innerHTML = `${100 - Number(e.dataset.raw)}%`;
+                       }
+               }
+       }
+}
index e91bd5046f88b0f1680f960dde2bc13d6251b679..0d926c5660046f97ec1c4fff473bdd049075f929 100644 (file)
@@ -1,6 +1,4 @@
-const unstablePriceChance: number = 0.7;
-
-export function renderPrice(): void {
+export default function renderPrice(): void {
        const el: HTMLCollectionOf<Element> =
                document.getElementsByClassName("price");
 
@@ -12,20 +10,3 @@ export function renderPrice(): void {
                }
        }
 }
-
-export function renderUnstablePrice(): void {
-       const el: HTMLCollectionOf<Element> =
-               document.getElementsByClassName("unstablePrice");
-
-       for (let e of el) {
-               if (e instanceof HTMLElement) {
-                       if (typeof e.dataset.raw === "string") {
-                               if (Math.random() < unstablePriceChance) {
-                                       e.innerHTML = String(parseFloat(e.dataset.raw).toFixed(2));
-                               } else {
-                                       e.innerHTML = String(parseFloat(e.dataset.raw));
-                               }
-                       }
-               }
-       }
-}
diff --git a/director/src/show_until.ts b/director/src/show_until.ts
new file mode 100644 (file)
index 0000000..6658b09
--- /dev/null
@@ -0,0 +1,16 @@
+export default function renderShowUntil(): void {
+       const el: HTMLCollectionOf<Element> =
+               document.getElementsByClassName("showUntil");
+
+       for (let e of el) {
+               if (e instanceof HTMLElement) {
+                       if (typeof e.dataset.raw === "string") {
+                               let duration: number = (eval(e.dataset.raw) * 1000) - Date.now();
+                               if (duration > 0) {
+                                       e.classList.add("showUntil-showing")
+                                       setTimeout(function () {e.classList.remove("showUntil-showing")}, duration)
+                               }
+                       }
+               }
+       }
+}
diff --git a/director/src/sound.ts b/director/src/sound.ts
new file mode 100644 (file)
index 0000000..b387907
--- /dev/null
@@ -0,0 +1,30 @@
+export function setupSounds() {
+       const el: HTMLCollectionOf<Element> =
+               document.getElementsByClassName("sound");
+
+       const context = new AudioContext();
+       if (el.length > 0) {
+               for (let e of el) {
+                       if (e instanceof HTMLMediaElement) {
+                               const track = context.createMediaElementSource(e);
+                               track.connect(context.destination);
+                       }
+               }
+       }
+}
+
+export function playSounds() {
+       const el: HTMLCollectionOf<Element> =
+               document.getElementsByClassName("sound");
+
+       for (let e of el) {
+               if (e instanceof HTMLMediaElement) {
+                       if (typeof e.dataset.raw === "string") {
+                               if (e.dataset.rawLast != e.dataset.raw) {
+                                       e.play();
+                               }
+                               e.dataset.rawLast = e.dataset.raw;
+                       }
+               }
+       }
+}
diff --git a/director/src/state.ts b/director/src/state.ts
new file mode 100644 (file)
index 0000000..d206f25
--- /dev/null
@@ -0,0 +1,18 @@
+export default function renderState(): void {
+       const el: HTMLCollectionOf<Element> =
+               document.getElementsByClassName("state");
+
+       for (let e of el) {
+               if (e instanceof HTMLElement) {
+                       if (typeof e.dataset.raw === "string") {
+                               for (let className of e.classList) {
+                                       if (className.startsWith("state-")) {
+                                               e.classList.remove(className);
+                                       }
+                               }
+
+                               e.classList.add(`state-${e.dataset.raw}`);
+                       }
+               }
+       }
+}
diff --git a/director/src/style.ts b/director/src/style.ts
new file mode 100644 (file)
index 0000000..380559e
--- /dev/null
@@ -0,0 +1,31 @@
+export function setupStyle(): void {
+       const el: HTMLCollectionOf<Element> =
+               document.getElementsByClassName("style");
+
+       for (let i = 0; i < el.length; i++) {
+               const stylesheet = new CSSStyleSheet();
+               document.adoptedStyleSheets.push(stylesheet);
+       }
+}
+
+export function renderStyle(): void {
+       const el: HTMLCollectionOf<Element> =
+               document.getElementsByClassName("style");
+
+       for (let [i, e] of Array.from(el).entries()) {
+               if (e instanceof HTMLElement) {
+                       let variable;
+                       for (let className of e.classList) {
+                               if (className.includes("--")) {
+                                       variable = className.split("--")[1];
+                               }
+                       }
+
+                       if (typeof e.dataset.raw === "string") {
+                               document.adoptedStyleSheets[i].replace(`
+                               :root {--${variable}: ${e.dataset.raw};}
+                               `);
+                       }
+               }
+       }
+}
index d7a6032b14f066b9fa6c4d85cf0e2ec5f22417c4..76950655621e55cfa6e60b66ea4e039b8750e957 100644 (file)
@@ -1,4 +1,4 @@
-export function renderText(): void {
+export default function renderText(): void {
        const el = document.querySelectorAll(".text,.title");
        for (let e of el) {
                if (e instanceof HTMLElement) {
diff --git a/director/src/time.ts b/director/src/time.ts
new file mode 100644 (file)
index 0000000..84b69f3
--- /dev/null
@@ -0,0 +1,51 @@
+import { timeSince, timeUntil, timeFormatBroadcast } from "./utils.js";
+
+const timeUpdate: number = 1000; // ms
+
+export function setupTimeSince(): void {
+       const el: HTMLCollectionOf<Element> =
+               document.getElementsByClassName("since");
+
+       if (el.length > 0) {
+               setInterval(
+                       function () {
+                               const el: HTMLCollectionOf<Element> =
+                                       document.getElementsByClassName("since");
+
+                               for (let e of el) {
+                                       if (e instanceof HTMLElement) {
+                                               if (typeof e.dataset.raw === "string") {
+                                                       let [hours, minutes, seconds] = timeSince(eval(e.dataset.raw))
+                                                       e.innerHTML = timeFormatBroadcast(hours, minutes, seconds);
+                                               }
+                                       }
+                               }
+                       },
+                       timeUpdate
+               );
+       }
+}
+
+export function setupTimeUntil(): void {
+       const el: HTMLCollectionOf<Element> =
+       document.getElementsByClassName("until");
+
+       if (el.length > 0) {
+               setInterval(
+                       function () {
+                               const el: HTMLCollectionOf<Element> =
+                               document.getElementsByClassName("until");
+
+                               for (let e of el) {
+                                       if (e instanceof HTMLElement) {
+                                               if (typeof e.dataset.raw === "string") {
+                                                       let [hours, minutes, seconds] = timeUntil(eval(e.dataset.raw))
+                                                       e.innerHTML = timeFormatBroadcast(hours, minutes, seconds);
+                                               }
+                                       }
+                               }
+                       },
+                       timeUpdate
+               );
+       }
+}
diff --git a/director/src/until.ts b/director/src/until.ts
new file mode 100644 (file)
index 0000000..e69de29
index 275921d2fb0010a76be0d88c4ab8e621c449f847..b50539c2ef24d8de6cfc712c6ae9f3372314aba8 100644 (file)
@@ -1,37 +1,39 @@
-import { renderText } from "./text.js";
-import { renderPrice, renderUnstablePrice } from "./price.js"
 import { io } from "socket.io-client";
 
-// apply a number of sequential keys to an object
-function objectPath(obj: any, path: string[]): any {
-       let key: string|undefined = path.shift();
-       if (typeof key === "undefined") {
-               return obj
-       } else {
-               return objectPath(obj[key], path)
-       }
-}
+import { renderDiscount, renderReverseDiscount } from "./discount.js";
+import { setupTimeSince, setupTimeUntil } from "./time.js";
+import { setupSounds, playSounds } from "./sound.js";
+import { setupStyle, renderStyle } from "./style.js";
+import renderShowUntil from "./show_until.js";
+import renderState from "./state.js";
+import renderPrice from "./price.js"
+import renderText from "./text.js";
 
 export default function setupUpdate(): void {
-       const socket = io();
-       socket.on("update", ([namespace, data]) => {
-               const el = document.querySelectorAll(`.update[class*='${namespace}']`);
+       setupTimeSince();
+       setupTimeUntil();
+       setupSounds();
+       setupStyle();
 
-               for (let e of el) {
-                       let c: string|undefined = Array.from(e.classList)
-                               .find((c) => c.startsWith(namespace))
-
-                       if (typeof c === "string") {
-                               let path = c.split("__");
-                               let result = objectPath(data, path);
+       const socket = io();
+       socket.on("update", (data) => {
+               for (const [key, value] of Object.entries(data)) {
+                       const el = document.querySelectorAll(`.update[class~='${key}']`);
 
-                               if (typeof result !== "undefined" && e instanceof HTMLElement) {
-                                       e.dataset.raw = result;
+                       for (let e of el) {
+                               if (e instanceof HTMLElement) {
+                                       e.dataset.raw = value as unknown as string;
                                }
                        }
                }
 
+               renderReverseDiscount();
+               renderShowUntil();
+               renderDiscount();
+               renderState();
                renderPrice();
-               renderUnstablePrice();
-       })
+               renderStyle();
+               renderText();
+               playSounds();
+       });
 }
diff --git a/director/src/utils.ts b/director/src/utils.ts
new file mode 100644 (file)
index 0000000..684ee85
--- /dev/null
@@ -0,0 +1,35 @@
+export function timeUntil(end: number): number[] {
+       let current: number = Date.now() / 1000;
+       var time: number = end - current;
+
+       if (Math.sign(time) == -1) {time = 0;}
+
+       let hours: number = Math.floor(time / 3600);
+       time %= 3600;
+       let minutes: number = Math.floor(time / 60);
+       let seconds: number = Math.floor(time % 60);
+
+       return [hours, minutes, seconds]
+}
+
+export function timeSince(end: number): number[] {
+       let current: number = Date.now() / 1000;
+       var time: number = current - end;
+
+       if (Math.sign(time) == -1) {time = 0;}
+
+       let hours: number = Math.floor(time / 3600);
+       time %= 3600;
+       let minutes: number = Math.floor(time / 60);
+       let seconds: number = Math.floor(time % 60);
+
+       return [hours, minutes, seconds]
+}
+
+export function timeFormatBroadcast(hours: number, minutes: number, seconds: number): string {
+       var hoursString = hours.toString().padStart(2, "0");
+       var minutesString = minutes.toString().padStart(2, "0");
+       var secondsString = seconds.toString().padStart(2, "0");
+
+       return `${hoursString}h ${minutesString}' ${secondsString}"`
+}
diff --git a/director/static/admin.css b/director/static/admin.css
new file mode 100644 (file)
index 0000000..3f7e86d
--- /dev/null
@@ -0,0 +1,44 @@
+/* PAGE POSITIONING */
+
+:root {
+       --nav-background: lightgray;
+}
+
+body > * {
+       padding: 15px;
+       overflow: scroll
+}
+nav {
+       position: absolute;
+       top: 0; bottom: 0; left: 0;
+       right: max(80%, calc(100vw - 350px));
+       background-color: var(--nav-background);
+}
+main {
+       position: absolute;
+       top: 0; bottom: 0; right: 0;
+       left: min(20%, calc(350px));
+}
+
+
+/* THE OPTIONS GRID */
+
+.optionsGrid {
+       display: grid;
+       grid-template-columns: 50px auto auto auto;
+}
+.optionsGrid > * {
+       padding: 10px;
+       border: 1px solid gray;
+       margin-top: -1px;
+       margin-right: -1px;
+}
+.optionsGridSpacer { border: none; }
+
+
+:target {
+       outline: 4px solid blue;
+       z-index: 1;
+}
+.currentValue { color: red; }
+.currentValue[type="radio"] { background-color: red; }
diff --git a/director/static/info.css b/director/static/info.css
new file mode 100644 (file)
index 0000000..c8c37b9
--- /dev/null
@@ -0,0 +1,39 @@
+:root {
+       font-family: sans-serif;
+       color: white;
+       background-color: black;
+}
+
+.infoA { font-size: var(--aFontSize); }
+.infoB { font-size: var(--bFontSize); }
+.infoC { font-size: var(--cFontSize); }
+
+.title { text-transform: capitalize; }
+
+/* INFO A */
+.infoA div > * { padding: 20px; }
+.infoA div {
+       position: absolute;
+       top: 0; left: 0; bottom: 0; right: 0;
+       display: grid;
+       grid: none / 1fr;
+}
+
+/* INFO B */
+.infoB div > * { padding: 20px; }
+.infoB div {
+       position: absolute;
+       top: 0; left: 0; bottom: 0; right: 0;
+       display: grid;
+       grid: none / 1fr 1fr;
+}
+
+/* INFO C */
+.infoC div > * { padding: 20px; text-align: center; }
+.infoC div {
+       position: absolute;
+       top: 0; left: 0; bottom: 0; right: 0;
+       display: grid;
+       grid: none / 6fr 1fr 6fr;
+}
+
diff --git a/director/static/media/sfx/clock.wav b/director/static/media/sfx/clock.wav
new file mode 100644 (file)
index 0000000..a760531
Binary files /dev/null and b/director/static/media/sfx/clock.wav differ
diff --git a/director/static/media/sfx/shout.wav b/director/static/media/sfx/shout.wav
new file mode 100644 (file)
index 0000000..4a6849d
Binary files /dev/null and b/director/static/media/sfx/shout.wav differ
diff --git a/director/static/show_until.css b/director/static/show_until.css
new file mode 100644 (file)
index 0000000..28e2f5b
--- /dev/null
@@ -0,0 +1,5 @@
+.showUntil {
+       opacity: 0;
+       transition: opacity 0.5s;
+}
+.showUntil.showUntil-showing { opacity: 1; }
index 5fdfac82e755fe7a5e206f0de63b5590e7a79743..17f5828f22167564baaebe9fb4d6469b72e243d0 100644 (file)
@@ -3,8 +3,11 @@
        builds the admin page
 
 #}
+<!DOCTYPE html>
 <html>
        <head>
+               <link rel="stylesheet" href="/static/admin.css">
+
                <script type="module" defer>
 
 import { setupTrigger } from "/script/admin.js";
@@ -13,51 +16,22 @@ setupTrigger();
                </script>
        </head>
        <body>
-               {% for table in current %}
-                       <details>
-                               <summary>{{ table }}</summary>
-                               <form>
-                                       {% for row in current[table] %}
-
-                                               {# CREATE RADIO BUTTON FOR ROW #}
-
-                                               {% if loop.index0 == indexes["int--" ~ table] %}
-                                                       <input type="radio" name="{{ "indexes~~int--" ~ table }}" value="{{ loop.index0 }}" checked="checked" >
-                                               {% else %}
-                                                       <input type="radio" name="{{ "indexes~~int--" ~ table }}" value="{{ loop.index0 }}" >
-                                               {% endif %}
-
-
-                                               {# CREATE FEILDS #}
-
-                                               <div style="display: inline-block; vertical-align: middle; border: 1px solid gray;">
-                                                       {% for feild in row %}
-
-                                                               {# SELECT ELEMENT FROM TYPE #}
-
-                                                               {% set type, label = feild.split('--') %}
-                                                               {% set value = row[feild] %}
-                                                               {% set name = table ~ "~~" ~ feild %}
-
-                                                               {% if type == "textarea" %}
-                                                                       {% include "admin/textarea.html" %}
-                                                               {% elif type == "text" %}
-                                                                       {% include "admin/text.html" %}
-                                                               {% elif type == "number" %}
-                                                                       {% include "admin/number.html" %}
-                                                               {% elif type == "float" %}
-                                                                       {% include "admin/float.html" %}
-                                                               {% elif type == "trigger" %}
-                                                                       {% include "admin/trigger.html" %}
-                                                               {% endif %}
-
-                                                               <br>
-                                                       {% endfor %}
-                                               </div><br>
-
-                                       {% endfor %}
-                               </form>
-                       </details>
-               {% endfor %}
+               <nav>
+                       <p><b>Tables</b></p>
+                       <ul style="border: 1px solid gray">
+                               {% for table in indexes %}
+                                       {% set table_name = table.split("--")[1] %}
+                                       <li><a href="/admin/{{ table_name }}">{{ table_name }}</a></li>
+                               {% endfor %}
+                       </ul>
+               </nav>
+               <main>
+                       <h1>Admin</h1>
+                       <ul>
+                               <li><a href="/info/a">Info page A</a></li>
+                               <li><a href="/info/b">Info page B</a></li>
+                               <li><a href="/info/c">Info page C</a></li>
+                       </ul>
+               </main>
        </body>
 </html>
diff --git a/director/templates/admin/color.html b/director/templates/admin/color.html
new file mode 100644 (file)
index 0000000..1ecd886
--- /dev/null
@@ -0,0 +1,6 @@
+<label>{{ label }}</label>
+<span>
+       <input id="{{ name }}" type="color" name="{{ name }}" value="{{ value }}" />
+       {{ value }}
+</span>
+<span class="currentValue" style="background-color: {{ value }};"></span>
index 98a5c862c9f8e4b061fe46f06af5bb08fdc4e2e6..12f8b00e5d3ab5a39841b953b77aced0160b295c 100644 (file)
@@ -1,2 +1,3 @@
-<label>{{ label }}</label><br>
-<input type="number" name="{{ name }}" value="{{ value }}" step="0.01" />
+<label>{{ label }}</label>
+<input id="{{ name }}" type="number" name="{{ name }}" value="{{ value }}" step="0.01" />
+<span class="currentValue">{{ value }}</span>
index 8014eda105d6043071364283c751e9efe0410e0a..7ffe0c7ba0478fdddc24706211829e62bf3340f0 100644 (file)
@@ -1,2 +1,3 @@
-<label>{{ label }}</label><br>
-<input type="number" name="{{ name }}" value="{{ value }}" />
+<label>{{ label }}</label>
+<input id="{{ name }}" type="number" name="{{ name }}" value="{{ value }}" />
+<span class="currentValue">{{ value }}</span>
index cb4f369e19270528019fc30004e58b8a702b3a93..3878cc8c1185ebdaa55e0f2644656eb070211f80 100644 (file)
@@ -1,3 +1,4 @@
-<label>{{ label }}</label><br>
-<input type="text" name="{{ name }}" value="{{ value }}" />
+<label>{{ label }}</label>
+<input id="{{ name }}" type="text" name="{{ name }}" value="{{ value }}" />
+<span class="currentValue">{{ value }}</span>
 
index e38b563273865ec42bcce01a83acdb73890988cd..a9ce85b8ee1b4a9c79d96a41c7309c95d3c18f98 100644 (file)
@@ -1,4 +1,4 @@
-<label>{{ label }}</label><br>
-<textarea name="{{ name }}">
+<label>{{ label }}</label>
+<textarea id="{{ name }}" style="grid-column: 3 / span 2;" name="{{ name }}">
        {{- value -}}
 </textarea>
index 208cb05e752ec63f81a395ab9e3ca07b36d276ab..322450d9c9c5aa41a177b7537656d5b8242e7e01 100644 (file)
@@ -1,4 +1,7 @@
-<noscript style="color: red;">Trigger will not function without JS</noscript><br>
-<button type="button" name="{{ name }}" value="{{ label }}" class="trigger">
-       {{ label }}
-</button>
+<span id="{{ name }}" style="grid-column: 2 / span 3;">
+       <noscript style="color: red;">Trigger will not function without JS<br></noscript>
+       <button type="button" name="{{ name }}" value="{{ label }}" class="trigger">
+               {{ label }}
+       </button>
+       <input type="number" id="{{ name }}+delay" value="{{ value.split('+')[1] }}"/><span> seconds delay <em>(This does not update the triggered data.)</em></span>
+</span>
index 5f9cbf819ebfe417d4de81ea7b40f80a722ec948..d236140310594dd4882c5809946292f474e5d79d 100644 (file)
@@ -2,6 +2,7 @@
 <html>
        <head>
                <link rel="stylesheet" href="/static/style.css">
+               <link rel="stylesheet" href="/static/show_until.css">
                <script type="importmap">
                {
                        "imports": {
@@ -16,7 +17,20 @@ setupUpdate();
 
                </script>
        </head>
-       <body>
-               <span class="update data__product__currentPrice unstablePrice">This is some text</span>
+       <body class="update data__state~~text--state state">
+
+               {# DYNAMIC STYLING ELEMENTS #}
+               <span class="update style__colorscheme~~color--text style"></span>
+               <span class="update style__colorscheme~~color--textAccent style"></span>
+
+               {# SFX ELEMENTS #}
+               <audio src="/static/media/sfx/clock.wav" class="update data__clock~~number--positionDegrees sound"></audio>
+               <audio src="/static/media/sfx/shout.wav" class="update data~~trigger--shoutTrigger sound"></audio>
+
+               {# BEGINS #}
+
+               <div class="update data~~trigger--shoutTrigger showUntil">
+                       <span class="update data~~text--shoutText title"></span>
+               </div>
        </body>
 </html>
diff --git a/director/templates/info/a.html b/director/templates/info/a.html
new file mode 100644 (file)
index 0000000..fa6e8cc
--- /dev/null
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+       <head>
+               <link rel="stylesheet" href="/static/info.css">
+               <script type="importmap">
+               {
+                       "imports": {
+                       "socket.io-client": "https://cdn.socket.io/4.8.3/socket.io.esm.min.js"
+                       }
+               }
+               </script>
+               <script type="module" defer>
+
+import setupUpdate from "/script/update.js";
+setupUpdate();
+
+               </script>
+       </head>
+       <body class="infoA">
+               <span class="update style~~text--aFontSize style"></span>
+
+               <div>
+                       <span>
+                               <u style="color: pink;"><span class="update data__product~~text--name text"></span></u><br>
+                               <span style="color: pink" class="update data__product~~text--description text"></span>
+                       </span>
+
+                       <span class="styleProductNotes update data__product~~textarea--notes text"></span>
+
+                       <span style="color: yellow" class="update data~~textarea--anchorNotes text"></span>
+               </div>
+       </body>
+</html>
diff --git a/director/templates/info/b.html b/director/templates/info/b.html
new file mode 100644 (file)
index 0000000..c06b48e
--- /dev/null
@@ -0,0 +1,51 @@
+<!doctype html>
+<html>
+       <head>
+               <link rel="stylesheet" href="/static/info.css">
+               <script type="importmap">
+               {
+                       "imports": {
+                       "socket.io-client": "https://cdn.socket.io/4.8.3/socket.io.esm.min.js"
+                       }
+               }
+               </script>
+               <script type="module" defer>
+
+import setupUpdate from "/script/update.js";
+setupUpdate();
+
+               </script>
+       </head>
+       <body class="infoB">
+               <span class="update style~~text--bFontSize style"></span>
+
+               <div>
+                       <span>
+                               <span style="background-color: green" class="update data__product__currentPriceString~~text--currentPriceString text"></span> <em>(was <span class="update data__product__originalPriceString~~text--originalPriceString text"></span>)</em>
+                       </span>
+
+                       <span>
+                               <span class="update data__product~~number--quantityCurrent text"></span>
+                               left of
+                               <span class="update data__product~~number--quantityTotal text"></span>
+                               total
+                       </span>
+
+                       <span>
+                               <span style="background-color: purple;" class="update data__product~~number--discount discount"></span> off
+                       </span>
+
+                       <span>
+                               <span style="background-color: purple;" class="update data__product~~number--discount reverseDiscount"></span> full price
+                       </span>
+
+                       <span style="grid-column: 1 / span 2;">
+                               Target: <span style="background-color: orange;" class="update data__product~~number--targetDiscount discount"></span> (<span class="update data__product__remainingDiscount~~number--remainingDiscount discount"></span> left to drop)
+                       </span>
+
+                       <span style="grid-column: 1 / span 2;">
+                               Max: <span style="background-color: red;" class="update data__product~~number--maximumDiscount discount"></span> (<span class="update data__product__remainingMaximumDiscount~~number--remainingMaximumDiscount discount"></span> left to drop)
+                       </span>
+               </div>
+       </body>
+</html>
diff --git a/director/templates/info/c.html b/director/templates/info/c.html
new file mode 100644 (file)
index 0000000..b35ba79
--- /dev/null
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+       <head>
+               <link rel="stylesheet" href="/static/info.css">
+               <script type="importmap">
+               {
+                       "imports": {
+                       "socket.io-client": "https://cdn.socket.io/4.8.3/socket.io.esm.min.js"
+                       }
+               }
+               </script>
+               <script type="module" defer>
+
+import setupUpdate from "/script/update.js";
+setupUpdate();
+
+               </script>
+       </head>
+       <body class="infoC">
+               <span class="update style~~text--cFontSize style"></span>
+
+               <div>
+                       <span>Current product:<br><span style="background-color: blue;" class="update data__product~~text--name text"></span></span>
+                       <span>&rarr;</span>
+                       <span>Next product:<br><span style="background-color: blue;" class="update data__product__next~~text--name text"></span></span>
+
+                       <span>Current state: <span class="update data__state~~text--state title"></span></span>
+                       <span>&rarr;</span>
+                       <span>Next state: <span class="update data__state~~text--nextState title"></span></span>
+
+                       <span style="grid-column: 1 / span 3; font-size: 2em; font-size: 2em;">
+                               <span class="update data~~trigger--startTrigger since"></span> <span style="background-color: red;">T-<span class="update data__endTime~~text--endTime until"></span></span>
+                       </span>
+               </div>
+       </body>
+</html>
diff --git a/director/templates/table.html b/director/templates/table.html
new file mode 100644 (file)
index 0000000..c357be1
--- /dev/null
@@ -0,0 +1,88 @@
+{#
+
+       builds the admin page
+
+#}
+<!DOCTYPE html>
+<html>
+       <head>
+               <link rel="stylesheet" href="/static/admin.css">
+
+               <script type="module" defer>
+
+import { setupTrigger } from "/script/admin.js";
+setupTrigger();
+
+               </script>
+       </head>
+       <body>
+               <nav>
+                       <p><a href="/admin">.. back</a></p>
+                       <p><b>{{ table_name }}</b></p>
+                       <p>Generated in {{ (time_taken * 1000)|round(2) }}ms</p>
+                       <ol>
+                       {% for row in table %}
+                               <li style="border: 1px solid gray; margin-top: -1px;"><ul>
+                                       {% set group = loop.index0 %}
+                                       {% for key in row %}
+                                               <li><a href="#{{ table_name ~ "~~" ~ group ~ "~~" ~ key }}">{{ key }}</a></li>
+                                       {% endfor %}
+                               </ul></li>
+                       {% endfor %}
+                       </ol>
+               </nav>
+               <main>
+                       <h1>Table: {{ table_name }}</h1>
+                       <form class="optionsGrid" method="post">
+                               <input type="submit" value="Submit table" style="grid-column: 1 / span 4;">
+                               <span class="optionsGridSpacer" style="grid-column: 1 / span 4;"></span>
+                               {% for row in table %}
+
+                                       {# CREATE RADIO BUTTON FOR ROW #}
+
+                                               <span style="
+                                                       grid-row: {{ ((row|length + 1) * loop.index0) + 3 }} / span {{ row|length }};
+                                                       display: flex;
+                                                       justify-content: center;
+                                                       align-items: center;
+                                                       ">
+                                                       {% if loop.index0 == indexes["int--" ~ table_name] %}
+                                                               <input type="radio" name="{{ "indexes~~0~~int--" ~ table_name }}" value="{{ loop.index0 }}" checked="checked" class="currentValue">
+                                                       {% else %}
+                                                               <input type="radio" name="{{ "indexes~~0~~int--" ~ table_name }}" value="{{ loop.index0 }}" >
+                                                       {% endif %}
+                                               </span>
+
+                                       {# CREATE FEILDS #}
+
+                                       {% set group = loop.index0 %}
+                                       {% for feild in row %}
+
+                                               {# SELECT ELEMENT FROM TYPE #}
+
+                                               {% set type, label = feild.split('--') %}
+                                               {% set value = row[feild] %}
+                                               {% set name = table_name ~ "~~" ~ group ~ "~~" ~ feild %}
+
+                                               {% if type == "textarea" %}
+                                                       {% include "admin/textarea.html" %}
+                                               {% elif type == "text" %}
+                                                       {% include "admin/text.html" %}
+                                               {% elif type == "number" %}
+                                                       {% include "admin/number.html" %}
+                                               {% elif type == "float" %}
+                                                       {% include "admin/float.html" %}
+                                               {% elif type == "trigger" %}
+                                                       {% include "admin/trigger.html" %}
+                                               {% elif type == "color" %}
+                                                       {% include "admin/color.html" %}
+                                               {% endif %}
+                                       {% endfor %}
+                                       <span class="optionsGridSpacer" style="grid-column: 1 / span 4;"></span>
+
+                               {% endfor %}
+                               <input type="submit" value="Submit table" style="grid-column: 1 / span 4;">
+                       </form>
+               </main>
+       </body>
+</html>
diff --git a/director/utils.py b/director/utils.py
new file mode 100644 (file)
index 0000000..d2fc032
--- /dev/null
@@ -0,0 +1,10 @@
+def deindex(data, indexes):
+       deindexed = {}
+       for key in data:
+               table, index, feild = key.split("~~")
+               if table != "indexes" and int(index) == indexes[f"int--{table}"]:
+                       deindexed.update({
+                               f"{table}~~{feild}": data[key]
+                               })
+
+       return deindexed