diff options
-rw-r--r-- | app.py | 83 | ||||
-rw-r--r-- | static/styles/manage.css | 50 | ||||
-rw-r--r-- | views/base.html | 5 | ||||
-rw-r--r-- | views/manage.html | 55 |
4 files changed, 170 insertions, 23 deletions
@@ -9,6 +9,7 @@ import os import sqlite3 from bottle.ext import sqlite from beaker.middleware import SessionMiddleware +import functools load_dotenv() @@ -31,17 +32,29 @@ connection = sqlite3.connect(DB_PATH) cursor = connection.cursor() cursor.executescript(""" CREATE TABLE IF NOT EXISTS applications ( + id INTEGER PRIMARY KEY, username VARCHAR(12) NOT NULL, + userId INTEGER UNIQUE NOT NULL, preferredRole VARCHAR(6) NOT NULL, motivation TEXT NOT NULL, - userId INTEGER UNIQUE NOT NULL + applicationTime INT NOT NULL -- unix timestamp ); + INSERT OR IGNORE + INTO applications(username, userId, preferredRole, motivation, applicationTime) + VALUES + ('DillerBlaster69', 0, 'dps', 'fake motivation', strftime('%s','now')), + ('DillerDiller', 1, 'healer', 'fake motivation', strftime('%s','now')), + ('diller123', 2, 'tank', 'fake motivation', strftime('%s','now')), + ('susamongus', 3, 'dps', 'fake motivation #4', strftime('%s','now')); CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY, + username VARCHAR(12) NOT NULL, userId INTEGER UNIQUE NOT NULL, - role VARCHAR(6) NOT NULL + preferredRole VARCHAR(6) NOT NULL, + joinTime INT NOT NULL -- unix timestamp ); - INSERT OR IGNORE INTO users(userId, role) VALUES (1165955606, 'dps'); + INSERT OR IGNORE INTO users(userId, preferredRole, joinTime) VALUES (1165955606, 'dps', strftime('%s','now')); """) cursor.close() connection.close() @@ -164,7 +177,11 @@ def join_submission(db: sqlite3.Connection): raise HTTPError(400, "Missing or invalid user id") try: - db.execute("INSERT INTO applications(username, preferredRole, motivation, userId) VALUES (?, ?, ?, ?)", (name, preferred_role, motivation, user_id)) + db.execute(""" + INSERT + INTO applications(username, userId, preferredRole, motivation, applicationTime) + VALUES (?, ?, ?, strftime('%s','now'), ?) + """, (name, user_id, preferred_role, motivation)) except sqlite3.IntegrityError as e: print(e.sqlite_errorcode == sqlite3.SQLITE_CONSTRAINT_UNIQUE) print(str(e)) @@ -176,23 +193,47 @@ def join_submission(db: sqlite3.Connection): return template("join_success") [email protected]("/leaderboards.html") -def leaderboards(db: sqlite3.Connection): - # all_members = db.execute("SELECT name FROM members") - all_members = [ - ["a", f"10 days", 1], - ["b", f"8 days", 2], - ["c", f"6 days", 3], - ["d", f"3 days", 4], - ["e", f"1 days", 5], - ["f", f"1 days", 6], - ["g", f"1 days", 7], - ["h", f"1 days", 8], - ["i", f"1 days", 9], - ["j", f"1 days", 10], - ["k", f"0 days", 11] - ] - return template("leaderboards.html", all_members=all_members) +def require_authentication(fn): + """Decorator that ensures the client is logged in""" + @functools.wraps(fn) + def wrapped(db: sqlite3.Connection, *args, **kwargs): + # Ensure authentication + session = request.environ.get("beaker.session") + print(session) + user_id = session.get("user_id", None) + if user_id is None: + raise HTTPError(403, "Must be logged in! (missing cookie)") + user = db.execute("SELECT * FROM users WHERE userId = ?", [user_id]).fetchone() + if user is None: + raise HTTPError(403, "Must be logged in! (unknown user)") + + # Wrapped function may or may not want this + kwargs["db"] = db + + return fn(*args, **kwargs) + return wrapped + +@require_authentication [email protected]("/manage.html") +def manage(db: sqlite3.Connection): + applications = db.execute("SELECT * FROM applications").fetchall(); + return template("manage", applications=applications) + + +@require_authentication [email protected]("/manage/<action:re:accept|reject>/<user_id:int>", method="POST") +def approve_application(action: str, user_id: int, db: sqlite3.Connection): + if action == "accept": + db.execute(""" + INSERT INTO users(username, userId, preferredRole, joinTime) + SELECT username, userId, preferredRole, strftime('%s','now') + FROM applications + WHERE userId = ?; + """, [user_id]) + print(user_id) + db.execute("DELETE FROM applications WHERE userId = ?", [user_id]) + + return f"Application {action}ed!" @app.route("/<type:re:styles|images>/<filename>") def server_static(type, filename): diff --git a/static/styles/manage.css b/static/styles/manage.css new file mode 100644 index 0000000..d2e67c9 --- /dev/null +++ b/static/styles/manage.css @@ -0,0 +1,50 @@ +/* + * This file contains styles specific to the "manage" view. The corresponding + * HTML/template file is `views/manage.html`. + */ + +main { + /* Center align and keep width readable. */ + width: 100%; + max-width: 500px; + margin: 2rem auto; +} + +.applications__item { + border: 2px solid white; + border-radius: 5px; + padding: 1rem; +} + +.application__username { + font-size: x-large; +} + +.applications { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.action { + display: contents; +} + +.action__button { + width: 48%; +} + +.action__button:hover { filter: brightness(90%); } +.action__button:active { filter: brightness(60%); } + +.action__button--accept { + border: 2px solid #4b694b; + background-color: #608660; + color: white; +} + +.action__button--reject { + border: 2px solid #891818; + background-color: #cd4747; + color: white; +} diff --git a/views/base.html b/views/base.html index 556a777..294cfd1 100644 --- a/views/base.html +++ b/views/base.html @@ -14,8 +14,9 @@ <ul class="navbar__links" role="navigation" aria-label="Main"> <li><a class="navbar__location" href="/index.html">About us</a></li> <li><a class="navbar__location" href="/history.html">History</a></li> - <li><a class="navbar__location" href="/leaderboards.html">Leaderboards</a></li> - {% if not logged_in %} + {% if logged_in %} + <li><a class="navbar__location" href="/manage.html">Manage</a></li> + {% else %} <li><a class="navbar__location" href="/login">Log in</a></li> <li><a class="navbar__location" href="/join_intro.html">Join</a></li> {% endif %} diff --git a/views/manage.html b/views/manage.html new file mode 100644 index 0000000..d7e2b65 --- /dev/null +++ b/views/manage.html @@ -0,0 +1,55 @@ +{% extends "base.html" %} + +{% block head %} + <link rel="stylesheet" href="/styles/manage.css"> + <script type="module"> + // Progressive enhancement – perform form requests without leaving page. + + for (const application of document.querySelectorAll(".applications__item")) { + for (const form of application.querySelectorAll("form.action")) { + form.addEventListener("submit", async (event) => { + event.preventDefault(); + + if (!confirm("Are you sure?")) { + return; + } + + let ok; + try { + const response = await fetch(form.action, { + method: "POST", + data: new FormData(form), + }); + ok = response.ok; + } catch { + ok = false; + } + + if (ok) { + application.remove(); + } else { + alert("Something went wrong >w<"); + } + }); + } + } + </script> +{% endblock %} + +{% block content %} + <h1>Manage applications</h1> + <div class="applications"> + {% for application in applications %} + <section class="applications__item"> + <span class="application__username">{{ application.username | e }}</span> + <p>{{ application.motivation | e }}</p> + <form class="action" method="POST" action="/manage/reject/{{ application.userId | e }}"> + <button class="action__button action__button--reject">Reject</button> + </form> + <form class="action" method="POST" action="/manage/accept/{{ application.userId | e }}"> + <button class="action__button action__button--accept">Accept</button> + </form> + </section> + {% endfor %} + </div> +{% endblock %} |